2017-07-28 135 views
0

我想要有SQL代码,可以让我为每月股票数据形成五分之一投资组合。五分位投资组合的形成取决于比率(在我的电子表格中称为B/M)。我希望代码能够自动为每个月产生不同的五分位投资组合,因为与前一个月相比,股票/公司增加或撤销某个月。比率也可以改变,以便在接下来的一个月内某个股票可以排在另一个五分位数中。用SQL创建五分之一股票投资组合

我添加了一个打印屏幕来简要地展示我如何组织我的Excel表格。 基本上,它每月排序。 enter image description here

+0

我看不到您的图片。你目前用什么公式计算比率?您可以使用窗口函数(https://docs.microsoft.com/zh-cn/sql/t-sql/functions/ranking-functions-transact-sql)计算五分位数。你使用什么风格和版本的SQL? – Shawn

+0

另外,https://stackoverflow.com/help/mcve <<<非常有帮助。 – Shawn

回答

0

注意:这适用于MS SQL 2008+。

我不知道你的数据结构看起来像什么,但也许沿

/* Test Data */ 
WITH stocks AS (
    /* Jan = 10 = 2 per quint */ 
    SELECT 'abc' AS StockName, 100.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'def' AS StockName, 99.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'ghi' AS StockName, 50.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'jkl' AS StockName, 50.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'mno' AS StockName, 75.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'pqr' AS StockName, 77.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'stu' AS StockName, 20.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'vwx' AS StockName, 10.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT 'yz1' AS StockName, 2.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 
    SELECT '234' AS StockName, 1.00 AS StockPrice, '20170101' AS PriceDate UNION ALL 

    /* Feb = 7 = uneven quints */ 
    SELECT 'abc' AS StockName, 1.00 AS StockPrice, '20170201' AS PriceDate UNION ALL 
    SELECT 'def' AS StockName, 2.00 AS StockPrice, '20170201' AS PriceDate UNION ALL 
    SELECT 'ghi' AS StockName, 20.00 AS StockPrice, '20170201' AS PriceDate UNION ALL 
    SELECT 'jkl' AS StockName, 55.00 AS StockPrice, '20170201' AS PriceDate UNION ALL 
    SELECT 'mno' AS StockName, 50.00 AS StockPrice, '20170201' AS PriceDate UNION ALL 
    SELECT 'pqr' AS StockName, 100.00 AS StockPrice, '20170201' AS PriceDate UNION ALL 
    SELECT 'stu' AS StockName, 90.00 AS StockPrice, '20170201' AS PriceDate UNION ALL 

    /* Mar = 3 = not enough for 5 quints. */ 
    SELECT 'abc' AS StockName, 42.00 AS StockPrice, '20170301' AS PriceDate UNION ALL 
    SELECT 'jkl' AS StockName, 42.00 AS StockPrice, '20170301' AS PriceDate UNION ALL 
    SELECT 'vwx' AS StockName, 42.00 AS StockPrice, '20170301' AS PriceDate 
) 

/* Query */  
SELECT y.StockName, y.StockPrice, y.PriceMonth, y.quintile 
FROM (
    SELECT x.StockName, x.StockPrice, month(x.PriceDate) AS PriceMonth 
     , NTILE(5) OVER (PARTITION BY month(x.PriceDate) ORDER BY x.StockPrice DESC) AS quintile 
    FROM stocks x 
    GROUP BY x.StockName, x.StockPrice, month(x.PriceDate) 
) y 
ORDER BY y.PriceMonth, y.quintile ASC 

线的东西给你

StockName StockPrice PriceMonth quintile 
abc  100.00  1   1 
def   99.00  1   1 
pqr   77.00  1   2 
mno   75.00  1   2 
ghi   50.00  1   3 
jkl   50.00  1   3 
stu   20.00  1   4 
vwx   10.00  1   4 
yz1   2.00  1   5 
234   1.00  1   5 
pqr  100.00  2   1 
stu   90.00  2   1 
jkl   55.00  2   2 
mno   50.00  2   2 
ghi   20.00  2   3 
def   2.00  2   4 
abc   1.00  2   5 
abc   42.00  3   1 
jkl   42.00  3   2 
vwx   42.00  3   3 

然后,当你显示它做什么,你可以按五分位进行分类/分组。

此外,我上面的示例说明了NTILE()如何不一定会给你你想要的。你可能需要计算然后自己创建五分位数。看到三月集团>>都是42美元,但他们被分成3个不同的五分位数。它也低于其他昆泰3价格。所以检查它是你想要的。

最后,最好是添加一个日期维度表,预先为您计算日期部分,然后JOIN这对您的主要子查询而言,但这是一个完全不同的讨论。

0

如果您计算比率,并且可以使用这些比率来确定某个五分位数应该处于哪个五分位数,那么它会使变得更容易。您不必使用NTILE(),您可以设置CTE来指定将落入每个五分位数的最小和最大比率。

/* Test Data */ 
IF OBJECT_ID(N'tempdb..#stocks') IS NOT NULL 
    DROP TABLE #stocks 
; 

CREATE TABLE #stocks (StockName varchar(10), BMratio int, StockMonth datetime) ; 
INSERT INTO #stocks (StockName, BMratio, StockMonth) 
    /* Jan = 10 = 2 per quint */ 
    SELECT 'abc' AS StockName, 10 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'def' AS StockName, 9 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'ghi' AS StockName, 8 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'jkl' AS StockName, 7 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'mno' AS StockName, 6 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'pqr' AS StockName, 5 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'stu' AS StockName, 4 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'vwx' AS StockName, 3 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT 'yz1' AS StockName, 2 AS BMratio, '20170101' AS StockMonth UNION ALL 
    SELECT '234' AS StockName, 1 AS BMratio, '20170101' AS StockMonth UNION ALL 

    /* Feb = 7 = uneven quints */ 
    SELECT 'abc' AS StockName, 1 AS BMratio, '20170201' AS StockMonth UNION ALL 
    SELECT 'def' AS StockName, 2 AS BMratio, '20170201' AS StockMonth UNION ALL 
    SELECT 'ghi' AS StockName, 4 AS BMratio, '20170201' AS StockMonth UNION ALL 
    SELECT 'jkl' AS StockName, 5 AS BMratio, '20170201' AS StockMonth UNION ALL 
    SELECT 'mno' AS StockName, 7 AS BMratio, '20170201' AS StockMonth UNION ALL 
    SELECT 'pqr' AS StockName, 10 AS BMratio, '20170201' AS StockMonth UNION ALL 
    SELECT 'stu' AS StockName, 9 AS BMratio, '20170201' AS StockMonth UNION ALL 

    /* Mar = 3 = not enough for 5 quints. */ 
    SELECT 'abc' AS StockName, 5 AS BMratio, '20170301' AS StockMonth UNION ALL 
    SELECT 'jkl' AS StockName, 5 AS BMratio, '20170301' AS StockMonth UNION ALL 
    SELECT 'vwx' AS StockName, 5 AS BMratio, '20170301' AS StockMonth 
; 

/* Create CTE query */ 
; WITH Quint_CTE AS (
    SELECT 1 AS quintNum, 9 AS quintMin, 10 AS quintMax UNION ALL 
    SELECT 2 AS quintNum, 7 AS quintMin, 8 AS quintMax UNION ALL 
    SELECT 3 AS quintNum, 5 AS quintMin, 6 AS quintMax UNION ALL 
    SELECT 4 AS quintNum, 3 AS quintMin, 4 AS quintMax UNION ALL 
    SELECT 5 AS quintNum, 0 AS quintMin, 2 AS quintMax 
) 
SELECT x.StockName, month(x.StockMonth) AS StockMonth, q.quintNum AS quintile 
FROM #stocks x 
INNER JOIN Quint_CTE q ON x.BMratio BETWEEN q.quintMin AND q.quintMax 
ORDER BY StockMonth, q.quintNum ASC