Logo

MovingPRODUCT

Updated 2023-11-13 21:36:02.603000

Syntax

SELECT [westclintech].[wct].[MovingPRODUCT](
  <@Val, float,>
 ,<@Offset, int,>
 ,<@RowNum, int,>
 ,<@Id, tinyint,>
 ,<@Exact, bit,>)

Description

Use the scalar function 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.

Arguments

@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.

@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.

@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.

@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.

@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.

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.

Examples

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.

{"columns":[{"field":"rn","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"x","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"Product","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"}],"rows":[{"rn":"1","x":"1.0000000000000000","Product":"1"},{"rn":"2","x":"0.5000000000000000","Product":"0.5"},{"rn":"3","x":"0.3333333333333330","Product":"0.166666666666666"},{"rn":"4","x":"0.2500000000000000","Product":"0.0416666666666666"},{"rn":"5","x":"0.2000000000000000","Product":"0.00833333333333332"},{"rn":"6","x":"0.1666666666666670","Product":"0.00138888888888889"},{"rn":"7","x":"0.1428571428571430","Product":"0.000396825396825398"},{"rn":"8","x":"0.1250000000000000","Product":"0.000148809523809524"},{"rn":"9","x":"0.1111111111111110","Product":"6.61375661375663E-05"},{"rn":"10","x":"0.1000000000000000","Product":"3.30687830687831E-05"},{"rn":"11","x":"0.0909090909090909","Product":"1.8037518037518E-05"},{"rn":"12","x":"0.0833333333333333","Product":"1.05218855218855E-05"},{"rn":"13","x":"0.0769230769230769","Product":"6.47500647500646E-06"},{"rn":"14","x":"0.0714285714285714","Product":"4.16250416250416E-06"},{"rn":"15","x":"0.0666666666666667","Product":"2.77500277500277E-06"},{"rn":"16","x":"0.0625000000000000","Product":"1.90781440781441E-06"},{"rn":"17","x":"0.0588235294117647","Product":"1.34669252316311E-06"},{"rn":"18","x":"0.0555555555555556","Product":"9.72611266728914E-07"},{"rn":"19","x":"0.0526315789473684","Product":"7.166609333792E-07"},{"rn":"20","x":"0.0500000000000000","Product":"5.374957000344E-07"}]}

Here's the result when we change @Exact to 'True' .

{"columns":[{"field":"rn","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"x","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"Product"}],"rows":[{"rn":"1","x":"1.0000000000000000","Product":"NULL"},{"rn":"2","x":"0.5000000000000000","Product":"NULL"},{"rn":"3","x":"0.3333333333333330","Product":"NULL"},{"rn":"4","x":"0.2500000000000000","Product":"NULL"},{"rn":"5","x":"0.2000000000000000","Product":"0.00833333333333332"},{"rn":"6","x":"0.1666666666666670","Product":"0.00138888888888889"},{"rn":"7","x":"0.1428571428571430","Product":"0.000396825396825398"},{"rn":"8","x":"0.1250000000000000","Product":"0.000148809523809524"},{"rn":"9","x":"0.1111111111111110","Product":"6.61375661375663E-05"},{"rn":"10","x":"0.1000000000000000","Product":"3.30687830687831E-05"},{"rn":"11","x":"0.0909090909090909","Product":"1.8037518037518E-05"},{"rn":"12","x":"0.0833333333333333","Product":"1.05218855218855E-05"},{"rn":"13","x":"0.0769230769230769","Product":"6.47500647500646E-06"},{"rn":"14","x":"0.0714285714285714","Product":"4.16250416250416E-06"},{"rn":"15","x":"0.0666666666666667","Product":"2.77500277500277E-06"},{"rn":"16","x":"0.0625000000000000","Product":"1.90781440781441E-06"},{"rn":"17","x":"0.0588235294117647","Product":"1.34669252316311E-06"},{"rn":"18","x":"0.0555555555555556","Product":"9.72611266728914E-07"},{"rn":"19","x":"0.0526315789473684","Product":"7.166609333792E-07"},{"rn":"20","x":"0.0500000000000000","Product":"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.

{"columns":[{"field":"dt","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"r","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"12-month Return"}],"rows":[{"dt":"2012-01-31","r":"-0.00022500","12-month Return":"NULL"},{"dt":"2012-02-29","r":"-0.00122500","12-month Return":"NULL"},{"dt":"2012-03-31","r":"0.00305000","12-month Return":"NULL"},{"dt":"2012-04-30","r":"-0.00217500","12-month Return":"NULL"},{"dt":"2012-05-31","r":"-0.00132500","12-month Return":"NULL"},{"dt":"2012-06-30","r":"-0.00380000","12-month Return":"NULL"},{"dt":"2012-07-31","r":"-0.00258333","12-month Return":"NULL"},{"dt":"2012-08-31","r":"0.00189167","12-month Return":"NULL"},{"dt":"2012-09-30","r":"-0.00217500","12-month Return":"NULL"},{"dt":"2012-10-31","r":"-0.00382500","12-month Return":"NULL"},{"dt":"2012-11-30","r":"-0.00046667","12-month Return":"NULL"},{"dt":"2012-12-31","r":"0.00160833","12-month Return":"-0.0112187322787753"},{"dt":"2013-01-31","r":"-0.00062500","12-month Return":"-0.0116143337962052"},{"dt":"2013-02-28","r":"0.00135000","12-month Return":"-0.00906611914277999"},{"dt":"2013-03-31","r":"-0.00276667","12-month Return":"-0.0148125279726148"},{"dt":"2013-04-30","r":"-0.00287500","12-month Return":"-0.0155036624204579"},{"dt":"2013-05-31","r":"0.00306667","12-month Return":"-0.0111743430414227"},{"dt":"2013-06-30","r":"0.00279167","12-month Return":"-0.00463146769690947"},{"dt":"2013-07-31","r":"0.00100000","12-month Return":"-0.00105549585872322"},{"dt":"2013-08-31","r":"0.00190000","12-month Return":"-0.00104719036226231"},{"dt":"2013-09-30","r":"-0.00197500","12-month Return":"-0.000846964308668041"},{"dt":"2013-10-31","r":"0.00252500","12-month Return":"0.00552201882847148"},{"dt":"2013-11-30","r":"0.00281667","12-month Return":"0.00882503091042008"},{"dt":"2013-12-31","r":"0.00128333","12-month Return":"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.

{"columns":[{"field":"dt","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"port","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"12-month Return"}],"rows":[{"dt":"2011-12-31","port":"100000.00","12-month Return":"NULL"},{"dt":"2012-01-31","port":"99666.67","12-month Return":"NULL"},{"dt":"2012-02-29","port":"99749.73","12-month Return":"NULL"},{"dt":"2012-03-31","port":"100165.35","12-month Return":"NULL"},{"dt":"2012-04-30","port":"100165.35","12-month Return":"NULL"},{"dt":"2012-05-31","port":"100248.82","12-month Return":"NULL"},{"dt":"2012-06-30","port":"99831.12","12-month Return":"NULL"},{"dt":"2012-07-31","port":"99997.51","12-month Return":"NULL"},{"dt":"2012-08-31","port":"99830.85","12-month Return":"NULL"},{"dt":"2012-09-30","port":"99747.66","12-month Return":"NULL"},{"dt":"2012-10-31","port":"99997.03","12-month Return":"NULL"},{"dt":"2012-11-30","port":"99913.70","12-month Return":"NULL"},{"dt":"2012-12-31","port":"100330.01","12-month Return":"0.00330009999999969"},{"dt":"2013-01-31","port":"99911.97","12-month Return":"0.00246120393106319"},{"dt":"2013-02-28","port":"100245.01","12-month Return":"0.00496522647229192"},{"dt":"2013-03-31","port":"99827.32","12-month Return":"-0.00337471990064453"},{"dt":"2013-04-30","port":"100076.89","12-month Return":"-0.000883139728459326"},{"dt":"2013-05-31","port":"99993.49","12-month Return":"-0.00254696264754073"},{"dt":"2013-06-30","port":"99660.18","12-month Return":"-0.00171229171825416"},{"dt":"2013-07-31","port":"99327.98","12-month Return":"-0.00669546671712151"},{"dt":"2013-08-31","port":"99327.98","12-month Return":"-0.00503722045840571"},{"dt":"2013-09-30","port":"99576.30","12-month Return":"-0.00171793503727347"},{"dt":"2013-10-31","port":"99908.22","12-month Return":"-0.000888126377353693"},{"dt":"2013-11-30","port":"100324.50","12-month Return":"0.00411154826615334"},{"dt":"2013-12-31","port":"100073.69","12-month Return":"-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.

{"columns":[{"field":"dt","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"Monthly Portfolio"},{"field":"12-month Portfolio Return"},{"field":"Monthly Benchmark"},{"field":"12-month Benchmark Return"}],"rows":[{"dt":"2011-12-31","Monthly Portfolio":"NULL","12-month Portfolio Return":"NULL","Monthly Benchmark":"NULL","12-month Benchmark Return":"NULL"},{"dt":"2012-01-31","Monthly Portfolio":"-0.00333329999999998","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.0435830152671757","12-month Benchmark Return":"NULL"},{"dt":"2012-02-29","Monthly Portfolio":"0.000833377898549204","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.0405894499432342","12-month Benchmark Return":"NULL"},{"dt":"2012-03-31","Monthly Portfolio":"0.00416662781944388","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.0313323765450177","12-month Benchmark Return":"NULL"},{"dt":"2012-04-30","Monthly Portfolio":"0","12-month Portfolio Return":"NULL","Monthly Benchmark":"-0.00749749728428717","12-month Benchmark Return":"NULL"},{"dt":"2012-05-31","Monthly Portfolio":"0.000833322101904566","12-month Portfolio Return":"NULL","Monthly Benchmark":"-0.0626506713593866","12-month Benchmark Return":"NULL"},{"dt":"2012-06-30","Monthly Portfolio":"-0.00416663258480265","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.0395549212793724","12-month Benchmark Return":"NULL"},{"dt":"2012-07-31","Monthly Portfolio":"0.00166671474786617","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.0125976390438713","12-month Benchmark Return":"NULL"},{"dt":"2012-08-31","Monthly Portfolio":"-0.00166664149937323","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.0197633616564683","12-month Benchmark Return":"NULL"},{"dt":"2012-09-30","Monthly Portfolio":"-0.000833309543092131","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.0242360903752366","12-month Benchmark Return":"NULL"},{"dt":"2012-10-31","Monthly Portfolio":"0.00250000852150301","12-month Portfolio Return":"NULL","Monthly Benchmark":"-0.0197894035414078","12-month Benchmark Return":"NULL"},{"dt":"2012-11-30","Monthly Portfolio":"-0.000833324749745068","12-month Portfolio Return":"NULL","Monthly Benchmark":"0.00284670292318157","12-month Benchmark Return":"NULL"},{"dt":"2012-12-31","Monthly Portfolio":"0.0041666958585258","12-month Portfolio Return":"0.00330009999999969","Monthly Benchmark":"0.00706831052549806","12-month Benchmark Return":"0.134056933842239"},{"dt":"2013-01-31","Monthly Portfolio":"-0.00416664963952451","12-month Portfolio Return":"0.00246120393106319","Monthly Benchmark":"0.0504280635819911","12-month Benchmark Return":"0.141495416828582"},{"dt":"2013-02-28","Monthly Portfolio":"0.00333333433421434","12-month Portfolio Return":"0.00496522647229192","Monthly Benchmark":"0.0110606030264802","12-month Benchmark Return":"0.109103157401441"},{"dt":"2013-03-31","Monthly Portfolio":"-0.00416669118991542","12-month Portfolio Return":"-0.00337471990064453","Monthly Benchmark":"0.0359877994031743","12-month Benchmark Return":"0.114109636697977"},{"dt":"2013-04-30","Monthly Portfolio":"0.00250001702940628","12-month Portfolio Return":"-0.000883139728459326","Monthly Benchmark":"0.018085763992888","12-month Benchmark Return":"0.142827506777976"},{"dt":"2013-05-31","Monthly Portfolio":"-0.000833359230087982","12-month Portfolio Return":"-0.00254696264754073","Monthly Benchmark":"0.0207627834774065","12-month Benchmark Return":"0.244526188059497"},{"dt":"2013-06-30","Monthly Portfolio":"-0.0033333169989368","12-month Portfolio Return":"-0.00171229171825416","Monthly Benchmark":"-0.0149993254596074","12-month Benchmark Return":"0.179215363833911"},{"dt":"2013-07-31","Monthly Portfolio":"-0.00333332731287461","12-month Portfolio Return":"-0.00669546671712151","Monthly Benchmark":"0.0494621112134872","12-month Benchmark Return":"0.222145694980136"},{"dt":"2013-08-31","Monthly Portfolio":"0","12-month Portfolio Return":"-0.00503722045840571","Monthly Benchmark":"-0.0312980133236046","12-month Benchmark Return":"0.160950674686119"},{"dt":"2013-09-30","Monthly Portfolio":"0.00250000050338284","12-month Portfolio Return":"-0.00171793503727347","Monthly Benchmark":"0.0297494748831884","12-month Benchmark Return":"0.167199983341085"},{"dt":"2013-10-31","Monthly Portfolio":"0.00333332329078306","12-month Portfolio Return":"-0.000888126377353693","Monthly Benchmark":"0.0445957598644109","12-month Benchmark Return":"0.243867550419217"},{"dt":"2013-11-30","Monthly Portfolio":"0.00416662412762436","12-month Portfolio Return":"0.00411154826615334","Monthly Benchmark":"0.0280494608719415","12-month Benchmark Return":"0.275127455549437"},{"dt":"2013-12-31","Monthly Portfolio":"-0.00249998754043124","12-month Portfolio Return":"-0.00255476900680085","Monthly Benchmark":"0.00268023767727499","12-month Benchmark Return":"0.269571375482931"}]}