Login     Register

        Contact Us     Search

XLeratorDB/windowing Documentation

SQL Server moving PRODUCT function


MovingPRODUCT

Updated: 20 Dec 2013


Use MovingPRODUCT to calculate the product of column values in an ordered resultant table, without the need for a self-join. The product is calculated for each value from the first value in the window to the last value in the window. If the column values are presented to the functions out of order, an error message will be generated.
Syntax
SELECT [wctWindowing].[wct].[MovingPRODUCT](
  <@Val, float,>
 ,<@Offset, int,>
 ,<@RowNum, int,>
 ,<@Id, tinyint,>
 ,<@Exact, bit,>)
Arguments
@Val
the value passed into the function. @Val is an expression of type float or of a type that can be implicitly converted to float.
@Offset
specifies the window size. The window size (or the number of rows included in the result) is the current row plus the @Offset. @Offset is an expression of type int or of a type that can be implicitly converted to int.
@RowNum
the number of the row within the group for which the sum is being calculated. If @RowNum for the current row in a set is less than or equal to the previous @RowNum and @RowNum is not equal to 1, an error message will be generated. @RowNum is an expression of type int or of a type that can be implicitly converted to int.
@Id
a unique identifier for the MovingPRODUCT calculation. @Id allows you to specify multiple moving sums within a resultant table. @Id is an expression of type tinyint or of a type that can be implicitly converted to tinyint.
@Exact
a bit value which tells the function whether or not to return a NULL value if the number of rows in the window is smaller the @Offset value. If @Exact is 'True' and the number of rows in the window is less the @Offset then a NULL is returned. @Exact is an expression of type bit or of a type that can be implicitly converted to bit.
Return Type
float
Remarks
·         If @Id is NULL then @Id = 0.
·         To calculate the running product from the beginning of a dataset or partition, use the RunningPRODUCT function.
·         If @RowNum is equal to 1, MovingPRODUCT is equal to @Val
·         @RowNum must be in ascending order.
·         There may be cases where the order in which the data are returned to the function and the order in which the results are returned are different, generally due to parallelism. You can use OPTION(MAXDOP 1) or OPTION(MAXDOP 1,FORCE ORDER) to help eliminate this problem.
Example
In this example we simply calculate the product of the current row and the previous 4 rows.
SELECT rn
,x
,wct.MovingPRODUCT(
      x,    --@Val
      4,    --@Offset
      rn,   --@RowNum
      NULL, --@Id
      'False'     --@Exact
      ) as [Product]
FROM (VALUES
(1,1)
,(2,0.5)
,(3,0.333333333333333)
,(4,0.25)
,(5,0.2)
,(6,0.166666666666667)
,(7,0.142857142857143)
,(8,0.125)
,(9,0.111111111111111)
,(10,0.1)
,(11,0.0909090909090909)
,(12,0.0833333333333333)
,(13,0.0769230769230769)
,(14,0.0714285714285714)
,(15,0.0666666666666667)
,(16,0.0625)
,(17,0.0588235294117647)
,(18,0.0555555555555556)
,(19,0.0526315789473684)
,(20,0.05)
)n(rn,x)
This produces the following result.
         rn                                       x                Product
----------- --------------------------------------- ----------------------
          1                      1.0000000000000000                      1
          2                      0.5000000000000000                    0.5
          3                      0.3333333333333330      0.166666666666666
          4                      0.2500000000000000     0.0416666666666666
          5                      0.2000000000000000    0.00833333333333332
          6                      0.1666666666666670    0.00138888888888889
          7                      0.1428571428571430   0.000396825396825398
          8                      0.1250000000000000   0.000148809523809524
          9                      0.1111111111111110   6.61375661375663E-05
         10                      0.1000000000000000   3.30687830687831E-05
         11                      0.0909090909090909    1.8037518037518E-05
         12                      0.0833333333333333   1.05218855218855E-05
         13                      0.0769230769230769   6.47500647500646E-06
         14                      0.0714285714285714   4.16250416250416E-06
         15                      0.0666666666666667   2.77500277500277E-06
         16                      0.0625000000000000   1.90781440781441E-06
         17                      0.0588235294117647   1.34669252316311E-06
         18                      0.0555555555555556   9.72611266728914E-07
         19                      0.0526315789473684     7.166609333792E-07
         20                      0.0500000000000000     5.374957000344E-07


Here's the result when we change @Exact to
'True'
         rn                                       x                Product
----------- --------------------------------------- ----------------------
          1                      1.0000000000000000                   NULL
          2                      0.5000000000000000                   NULL
          3                      0.3333333333333330                   NULL
          4                      0.2500000000000000                   NULL
          5                      0.2000000000000000    0.00833333333333332
          6                      0.1666666666666670    0.00138888888888889
          7                      0.1428571428571430   0.000396825396825398
          8                      0.1250000000000000   0.000148809523809524
          9                      0.1111111111111110   6.61375661375663E-05
         10                      0.1000000000000000   3.30687830687831E-05
         11                      0.0909090909090909    1.8037518037518E-05
         12                      0.0833333333333333   1.05218855218855E-05
         13                      0.0769230769230769   6.47500647500646E-06
         14                      0.0714285714285714   4.16250416250416E-06
         15                      0.0666666666666667   2.77500277500277E-06
         16                      0.0625000000000000   1.90781440781441E-06
         17                      0.0588235294117647   1.34669252316311E-06
         18                      0.0555555555555556   9.72611266728914E-07
         19                      0.0526315789473684     7.166609333792E-07
         20                      0.0500000000000000     5.374957000344E-07


You can combine the
MovingPRODUCT function with other arithmetic operations. In this example, we calculate the 12-month moving geometric return for a portfolio by adding 1 to the monthly return figures and then subtracting 1 from the result.
SELECT *
,wct.MovingPRODUCT(1+r,11,ROW_NUMBER() OVER (ORDER BY dt),NULL,'True') - 1 as [12-month Return]
FROM (VALUES
('2012-01-31',-0.000225)
,('2012-02-29',-0.001225)
,('2012-03-31',0.00305)
,('2012-04-30',-0.002175)
,('2012-05-31',-0.001325)
,('2012-06-30',-0.0038)
,('2012-07-31',-0.00258333)
,('2012-08-31',0.00189167)
,('2012-09-30',-0.002175)
,('2012-10-31',-0.003825)
,('2012-11-30',-0.00046667)
,('2012-12-31',0.00160833)
,('2013-01-31',-0.000625)
,('2013-02-28',0.00135)
,('2013-03-31',-0.00276667)
,('2013-04-30',-0.002875)
,('2013-05-31',0.00306667)
,('2013-06-30',0.00279167)
,('2013-07-31',0.001)
,('2013-08-31',0.0019)
,('2013-09-30',-0.001975)
,('2013-10-31',0.002525)
,('2013-11-30',0.00281667)
,('2013-12-31',0.00128333)
)n(dt,r)

This produces the following result.
dt                                               r        12-month Return
---------- --------------------------------------- ----------------------
2012-01-31                             -0.00022500                   NULL
2012-02-29                             -0.00122500                   NULL
2012-03-31                              0.00305000                   NULL
2012-04-30                             -0.00217500                   NULL
2012-05-31                             -0.00132500                   NULL
2012-06-30                             -0.00380000                   NULL
2012-07-31                             -0.00258333                   NULL
2012-08-31                              0.00189167                   NULL
2012-09-30                             -0.00217500                   NULL
2012-10-31                             -0.00382500                   NULL
2012-11-30                             -0.00046667                   NULL
2012-12-31                              0.00160833    -0.0112187322787753
2013-01-31                             -0.00062500    -0.0116143337962052
2013-02-28                              0.00135000   -0.00906611914277999
2013-03-31                             -0.00276667    -0.0148125279726148
2013-04-30                             -0.00287500    -0.0155036624204579
2013-05-31                              0.00306667    -0.0111743430414227
2013-06-30                              0.00279167   -0.00463146769690947
2013-07-31                              0.00100000   -0.00105549585872322
2013-08-31                              0.00190000   -0.00104719036226231
2013-09-30                             -0.00197500  -0.000846964308668041
2013-10-31                              0.00252500    0.00552201882847148
2013-11-30                              0.00281667    0.00882503091042008
2013-12-31                              0.00128333    0.00849768924878869


In this example, we combine the XLeratorDB
LAG function with the MovingPRODUCT function to calculate the 12-month moving return using the portfolio values.
SELECT *
,wct.MovingPRODUCT(
      port/wct.LAG(port,1,NULL,ROW_NUMBER() OVER (ORDER BY DT),NULL)
      ,11
      ,ROW_NUMBER() OVER (ORDER BY DT)
      ,NULL
      ,'TRUE'
      ) - 1 as [12-month Return]
FROM (VALUES
      ('2011-12-31',100000)
      ,('2012-01-31',99666.67)
      ,('2012-02-29',99749.73)
      ,('2012-03-31',100165.35)
      ,('2012-04-30',100165.35)
      ,('2012-05-31',100248.82)
      ,('2012-06-30',99831.12)
      ,('2012-07-31',99997.51)
      ,('2012-08-31',99830.85)
      ,('2012-09-30',99747.66)
      ,('2012-10-31',99997.03)
      ,('2012-11-30',99913.7)
      ,('2012-12-31',100330.01)
      ,('2013-01-31',99911.97)
      ,('2013-02-28',100245.01)
      ,('2013-03-31',99827.32)
      ,('2013-04-30',100076.89)
      ,('2013-05-31',99993.49)
      ,('2013-06-30',99660.18)
      ,('2013-07-31',99327.98)
      ,('2013-08-31',99327.98)
      ,('2013-09-30',99576.3)
      ,('2013-10-31',99908.22)
      ,('2013-11-30',100324.5)
      ,('2013-12-31',100073.69)
      )n(dt,port)

This produces the following result.
dt                                            port        12-month Return
---------- --------------------------------------- ----------------------
2011-12-31                               100000.00                   NULL
2012-01-31                                99666.67                   NULL
2012-02-29                                99749.73                   NULL
2012-03-31                               100165.35                   NULL
2012-04-30                               100165.35                   NULL
2012-05-31                               100248.82                   NULL
2012-06-30                                99831.12                   NULL
2012-07-31                                99997.51                   NULL
2012-08-31                                99830.85                   NULL
2012-09-30                                99747.66                   NULL
2012-10-31                                99997.03                   NULL
2012-11-30                                99913.70                   NULL
2012-12-31                               100330.01    0.00330009999999969
2013-01-31                                99911.97    0.00246120393106319
2013-02-28                               100245.01    0.00496522647229192
2013-03-31                                99827.32   -0.00337471990064453
2013-04-30                               100076.89  -0.000883139728459326
2013-05-31                                99993.49   -0.00254696264754073
2013-06-30                                99660.18   -0.00171229171825416
2013-07-31                                99327.98   -0.00669546671712151
2013-08-31                                99327.98   -0.00503722045840571
2013-09-30                                99576.30   -0.00171793503727347
2013-10-31                                99908.22  -0.000888126377353693
2013-11-30                               100324.50    0.00411154826615334
2013-12-31                               100073.69   -0.00255476900680085
 
In this example we use the LAG and the MovingPRODUCT functions to calculate portfolio returns and compare them to the returns on a benchmark. Note that the @Id parameter must be unique for each invocation of the LAG function and for each invocation of the MovingPRODUCT function but that the same @Id parameter can be used in each function once.
SELECT dt
,port/wct.LAG(
      port, --@val
      1,    --@Offset
      NULL, --@DefaultValue
      ROW_NUMBER() OVER (ORDER BY dt),          --@RowNum
      0     --@id
      ) - 1 as [Monthly Portfolio]
,wct.MovingPRODUCT(
      port/wct.LAG(
            port, --@val
            1,    --@Offset
            NULL, --@DefaultValue
            ROW_NUMBER() OVER (ORDER BY DT),    --@RowNum
            1     --@Id
            ),
      11,   --@Offset
      ROW_NUMBER() OVER (ORDER BY DT),          --@RowNum
      0,    --@Id
      'TRUE'--@Exact
      ) - 1 as [12-month Portfolio Return]
,bmk/wct.LAG(
      bmk, --@val
      1,    --@Offset
      NULL, --@DefaultValue
      ROW_NUMBER() OVER (ORDER BY dt),          --@RowNum
      2     --@id
      ) - 1 as [Monthly Benchmark]
,wct.MovingPRODUCT(
      bmk/wct.LAG(
            bmk, --@val
            1,    --@Offset
            NULL, --@DefaultValue
            ROW_NUMBER() OVER (ORDER BY DT),    --@RowNum
            3     --@Id
            ),
      11,   --@offset
      ROW_NUMBER() OVER (ORDER BY DT),          --@RowNum
      1,          --@Id
      'TRUE'      --@Exact
      ) - 1 as [12-month Benchmark Return]
FROM (VALUES
      ('2011-12-31',100000,1257.60)
      ,('2012-01-31',99666.67,1312.41)
      ,('2012-02-29',99749.73,1365.68)
      ,('2012-03-31',100165.35,1408.47)
      ,('2012-04-30',100165.35,1397.91)
      ,('2012-05-31',100248.82,1310.33)
      ,('2012-06-30',99831.12,1362.16)
      ,('2012-07-31',99997.51,1379.32)
      ,('2012-08-31',99830.85,1406.58)
      ,('2012-09-30',99747.66,1440.67)
      ,('2012-10-31',99997.03,1412.16)
      ,('2012-11-30',99913.7,1416.18)
      ,('2012-12-31',100330.01,1426.19)
      ,('2013-01-31',99911.97,1498.11)
      ,('2013-02-28',100245.01,1514.68)
      ,('2013-03-31',99827.32,1569.19)
      ,('2013-04-30',100076.89,1597.57)
      ,('2013-05-31',99993.49,1630.74)
      ,('2013-06-30',99660.18,1606.28)
      ,('2013-07-31',99327.98,1685.73)
      ,('2013-08-31',99327.98,1632.97)
      ,('2013-09-30',99576.3,1681.55)
      ,('2013-10-31',99908.22,1756.54)
      ,('2013-11-30',100324.5,1805.81)
      ,('2013-12-31',100073.69,1810.65)
      )n(dt,port,bmk)

This produces the following result which has been reformatted for ease of viewing.

dt
Monthly Portfolio
12-month Portfolio Return
Monthly Benchmark
12-month Benchmark Return
12/31/2011
NULL
NULL
NULL
NULL
1/31/2012
-0.003333
NULL
0.04358302
NULL
2/29/2012
0.000833
NULL
0.04058945
NULL
3/31/2012
0.004167
NULL
0.03133238
NULL
4/30/2012
0.000000
NULL
-0.00749750
NULL
5/31/2012
0.000833
NULL
-0.06265067
NULL
6/30/2012
-0.004167
NULL
0.03955492
NULL
7/31/2012
0.001667
NULL
0.01259764
NULL
8/31/2012
-0.001667
NULL
0.01976336
NULL
9/30/2012
-0.000833
NULL
0.02423609
NULL
10/31/2012
0.002500
NULL
-0.01978940
NULL
11/30/2012
-0.000833
NULL
0.00284670
NULL
12/31/2012
0.004167
0.003300
0.00706831
0.13405693
1/31/2013
-0.004167
0.002461
0.05042806
0.14149542
2/28/2013
0.003333
0.004965
0.01106060
0.10910316
3/31/2013
-0.004167
-0.003375
0.03598780
0.11410964
4/30/2013
0.002500
-0.000883
0.01808576
0.14282751
5/31/2013
-0.000833
-0.002547
0.02076278
0.24452619
6/30/2013
-0.003333
-0.001712
-0.01499933
0.17921536
7/31/2013
-0.003333
-0.006695
0.04946211
0.22214569
8/31/2013
0.000000
-0.005037
-0.03129801
0.16095067
9/30/2013
0.002500
-0.001718
0.02974947
0.16719998
10/31/2013
0.003333
-0.000888
0.04459576
0.24386755
11/30/2013
0.004167
0.004112
0.02804946
0.27512746
12/31/2013
-0.002500
-0.002555
0.00268024
0.26957138

 


Copyright 2008-2013 WestClinTech LLC         Privacy Policy        Terms of Service