RunningPRODUCT
Updated 2023-11-14 15:16:30.180000
Syntax
SELECT [westclintech].[wct].[RunningPRODUCT](
<@Val, float,>
,<@RowNum, int,>
,<@Id, tinyint,>)
Description
Use the scalar function RunningPRODUCT 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 to the last value in the ordered group or partition. 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 RunningPRODUCT calculation. @Id allows you to specify multiple moving products within a resultant table. @Id is an expression of type tinyint or of a type that can be implicitly converted to tinyint.
@RowNum
the number of the row within the group for which the product 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.
@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.
Return Type
float
Remarks
If @Id is NULL then @Id = 0.
To calculate moving products, use the MovingPRODUCT function.
If @RowNum is equal to 1, RunningPRODUCT 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 x after each row.
SELECT *,
wct.RunningProduct( x, --@Val
rn, --@RowNum
NULL --@Id
) 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)
) 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.000000000000000","PRODUCT":"1"},{"rn":"2","x":"0.500000000000000","PRODUCT":"0.5"},{"rn":"3","x":"0.333333333333333","PRODUCT":"0.166666666666666"},{"rn":"4","x":"0.250000000000000","PRODUCT":"0.0416666666666666"},{"rn":"5","x":"0.200000000000000","PRODUCT":"0.00833333333333332"},{"rn":"6","x":"0.166666666666667","PRODUCT":"0.00138888888888889"},{"rn":"7","x":"0.142857142857143","PRODUCT":"0.000198412698412699"},{"rn":"8","x":"0.125000000000000","PRODUCT":"2.48015873015873E-05"},{"rn":"9","x":"0.111111111111111","PRODUCT":"2.75573192239859E-06"},{"rn":"10","x":"0.100000000000000","PRODUCT":"2.75573192239859E-07"}]}
You can combine the RunningPRODUCT function with other arithmetic operations. In this example, we calculate the geometric return for a portfolio by adding 1 to the monthly return figures and then subtracting 1 from the result.
SELECT *,
wct.RunningPRODUCT(1 + r, ROW_NUMBER() OVER (ORDER BY dt), NULL) - 1 as [
Geometric Return]
FROM
(
VALUES
('2013-01-31', 0.099),
('2013-02-28', 0.006),
('2013-03-31', 0.086),
('2013-04-30', 0.064),
('2013-05-31', 0.003),
('2013-06-30', -0.011),
('2013-07-31', 0.046),
('2013-08-31', -0.012),
('2013-09-30', 0.069),
('2013-10-31', 0.094),
('2013-11-30', 0.073),
('2013-12-31', -0.021)
) 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":"Geometric Return","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"}],"rows":[{"dt":"2013-01-31","r":"0.099","Geometric Return":"0.099"},{"dt":"2013-02-28","r":"0.006","Geometric Return":"0.105594"},{"dt":"2013-03-31","r":"0.086","Geometric Return":"0.200675084"},{"dt":"2013-04-30","r":"0.064","Geometric Return":"0.277518289376"},{"dt":"2013-05-31","r":"0.003","Geometric Return":"0.281350844244128"},{"dt":"2013-06-30","r":"-0.011","Geometric Return":"0.267255984957443"},{"dt":"2013-07-31","r":"0.046","Geometric Return":"0.325549760265485"},{"dt":"2013-08-31","r":"-0.012","Geometric Return":"0.309643163142299"},{"dt":"2013-09-30","r":"0.069","Geometric Return":"0.400008541399118"},{"dt":"2013-10-31","r":"0.094","Geometric Return":"0.531609344290635"},{"dt":"2013-11-30","r":"0.073","Geometric Return":"0.643416826423851"},{"dt":"2013-12-31","r":"-0.021","Geometric Return":"0.60890507306895"}]}
In this example, we combine the XLeratorDB LAG function with the RunningPRODUCT function to calculate the geometric returns using the portfolio values.
SELECT *,
wct.RunningProduct(
val / wct.LAG(val, 1, NULL, ROW_NUMBER() OVER (ORDER
BY dt), NULL),
ROW_NUMBER() OVER (ORDER BY dt),
NULL
) - 1 as [Geometric Return]
FROM
(
VALUES
('2012-12-31', 100000),
('2013-01-31', 106200),
('2013-02-28', 110448),
('2013-03-31', 114865.92),
('2013-04-30', 119115.96),
('2013-05-31', 120188),
('2013-06-30', 127399.28),
('2013-07-31', 127781.48),
('2013-08-31', 131231.58),
('2013-09-30', 136743.31),
('2013-10-31', 148639.98),
('2013-11-30', 146559.02),
('2013-12-31', 154619.77)
) n (dt, val);
This produces the following result.
{"columns":[{"field":"dt","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"val","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"Geometric Return"}],"rows":[{"dt":"2012-12-31","val":"100000.00","Geometric Return":"NULL"},{"dt":"2013-01-31","val":"106200.00","Geometric Return":"0.0620000000000001"},{"dt":"2013-02-28","val":"110448.00","Geometric Return":"0.10448"},{"dt":"2013-03-31","val":"114865.92","Geometric Return":"0.1486592"},{"dt":"2013-04-30","val":"119115.96","Geometric Return":"0.1911596"},{"dt":"2013-05-31","val":"120188.00","Geometric Return":"0.20188"},{"dt":"2013-06-30","val":"127399.28","Geometric Return":"0.2739928"},{"dt":"2013-07-31","val":"127781.48","Geometric Return":"0.2778148"},{"dt":"2013-08-31","val":"131231.58","Geometric Return":"0.3123158"},{"dt":"2013-09-30","val":"136743.31","Geometric Return":"0.3674331"},{"dt":"2013-10-31","val":"148639.98","Geometric Return":"0.4863998"},{"dt":"2013-11-30","val":"146559.02","Geometric Return":"0.4655902"},{"dt":"2013-12-31","val":"154619.77","Geometric Return":"0.5461977"}]}
In this example we use the LAG and the RunningPRODUCT 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 RunningPRODUCT 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.RunningProduct( port / wct.LAG(port,
--@val
1,
--@offset
NULL,
--@DefaultValues
ROW_NUMBER() OVER (ORDER BY dt),
--@RowNum
1
--@id
), --@val
ROW_NUMBER() OVER (ORDER BY dt), --@RowNum
0 --@id
) - 1 as [Y-T-D Portfolio],
bmk / wct.LAG( bmk, --@val
1, --@offset
NULL, --@DefaultValue
ROW_NUMBER() OVER (ORDER BY dt), --@RowNum
2 --@Id
) - 1 as [Monthly Benchmark],
wct.RunningProduct( bmk / wct.LAG(bmk,
--@Val
1,
--@Offset
NULL,
--@DefaultValue
ROW_NUMBER() OVER (ORDER BY dt),
--@RowNum
3
--@Id
), --@Val
ROW_NUMBER() OVER (ORDER BY dt), --@RowNum
1
) - 1 as [Y-T-D Benchmark]
FROM
(
VALUES
('2012-12-31', 100000, 1426.19),
('2013-01-31', 106200, 1498.11),
('2013-02-28', 110448, 1514.68),
('2013-03-31', 114865.92, 1569.19),
('2013-04-30', 119115.96, 1597.57),
('2013-05-31', 120188, 1630.74),
('2013-06-30', 127399.28, 1606.28),
('2013-07-31', 127781.48, 1685.73),
('2013-08-31', 131231.58, 1632.97),
('2013-09-30', 136743.31, 1681.55),
('2013-10-31', 148639.98, 1756.54),
('2013-11-30', 146559.02, 1805.81),
('2013-12-31', 154619.77, 1810.65)
) n (dt, port, bmk);
This produces the following results.
{"columns":[{"field":"dt","headerClass":"ag-right-aligned-header","cellClass":"ag-right-aligned-cell"},{"field":"Monthly Portfolio"},{"field":"Y-T-D Portfolio"},{"field":"Monthly Benchmark"},{"field":"Y-T-D Benchmark"}],"rows":[{"dt":"2012-12-31","Monthly Portfolio":"NULL","Y-T-D Portfolio":"NULL","Monthly Benchmark":"NULL","Y-T-D Benchmark":"NULL"},{"dt":"2013-01-31","Monthly Portfolio":"0.0620000000000001","Y-T-D Portfolio":"0.0620000000000001","Monthly Benchmark":"0.0504280635819911","Y-T-D Benchmark":"0.0504280635819911"},{"dt":"2013-02-28","Monthly Portfolio":"0.04","Y-T-D Portfolio":"0.10448","Monthly Benchmark":"0.0110606030264802","Y-T-D Benchmark":"0.0620464314011457"},{"dt":"2013-03-31","Monthly Portfolio":"0.04","Y-T-D Portfolio":"0.1486592","Monthly Benchmark":"0.0359877994031743","Y-T-D Benchmark":"0.100267145331267"},{"dt":"2013-04-30","Monthly Portfolio":"0.0370000083575703","Y-T-D Portfolio":"0.1911596","Monthly Benchmark":"0.018085763992888","Y-T-D Benchmark":"0.120166317250857"},{"dt":"2013-05-31","Monthly Portfolio":"0.00899996944154235","Y-T-D Portfolio":"0.20188","Monthly Benchmark":"0.0207627834774065","Y-T-D Benchmark":"0.14342408795462"},{"dt":"2013-06-30","Monthly Portfolio":"0.0600000000000001","Y-T-D Portfolio":"0.2739928","Monthly Benchmark":"-0.0149993254596074","Y-T-D Benchmark":"0.126273497921034"},{"dt":"2013-07-31","Monthly Portfolio":"0.00300001695456986","Y-T-D Portfolio":"0.2778148","Monthly Benchmark":"0.0494621112134872","Y-T-D Benchmark":"0.181981362932008"},{"dt":"2013-08-31","Monthly Portfolio":"0.0270000003130344","Y-T-D Portfolio":"0.3123158","Monthly Benchmark":"-0.0312980133236046","Y-T-D Benchmark":"0.14498769448671"},{"dt":"2013-09-30","Monthly Portfolio":"0.0420000277372261","Y-T-D Portfolio":"0.3674331","Monthly Benchmark":"0.0297494748831884","Y-T-D Benchmark":"0.179050477145402"},{"dt":"2013-10-31","Monthly Portfolio":"0.0870000148453334","Y-T-D Portfolio":"0.4863998","Monthly Benchmark":"0.0445957598644109","Y-T-D Benchmark":"0.231631129092197"},{"dt":"2013-11-30","Monthly Portfolio":"-0.0140000018837464","Y-T-D Portfolio":"0.4655902","Monthly Benchmark":"0.0280494608719415","Y-T-D Benchmark":"0.266177718256334"},{"dt":"2013-12-31","Monthly Portfolio":"0.05500002661044","Y-T-D Portfolio":"0.5461977","Monthly Benchmark":"0.00268023767727499","Y-T-D Benchmark":"0.269571375482931"}]}