I have a table which shows the simulated daily returns of different stocks. The variable date_simul is the date where the simulation was done, stock is the name of the stock simulated, N simul is the number of simulations for each stock (depending on the stock might be 1000, 5000 or 10000), simul is nth value simulated of the stock, FutureDate is the date where the stock is being simulated and Return, the daily return of the stock simulated in the future date.
SQL so far:
select date_simul, Stock, N Simu, FutureDate, Return
exp(sum(ln(1 Return)) over (order by FutureDate asc)) - 1 as cumul
from portfolio
order by Stock, FutureDate;
I would like to get the cumulative return, day 1, (1 r1) - 1, day 2, (1 r1)*(1 r2) - 1 and so on. Likewise, I wanted to use the fact that:
(1 r1)*(1 r2)*(1 r3) - 1 = exp(log(1 r1) log(1 r2) log(1 r3)) - 1,
since a sum should be easier than a product. I have tried using the query above, but with no success.
Data:
date_simul|Stock|N Simu|FutureDate| Return
30/09/22 | A | 1000 | 01/10/22 | -0,0073
30/09/22 | A | 1000 | 02/09/22 | 0,0078
30/09/22 | A | 1000 | 03/09/22 | 0,0296
30/09/22 | A | 1000 | 04/09/22 | 0,0602
30/09/22 | A | 1000 | 05/10/22 | -0,0177
Desired results:
date_simul|Stock|N Simu|FutureDate| Return | Cumul
30/09/22 | A | 1000 | 01/10/22 | -0,0073| -0,0073
30/09/22 | A | 1000 | 02/09/22 | 0,0078 | 0,0004
30/09/22 | A | 1000 | 03/09/22 | 0,0296 | 0,0301
30/09/22 | A | 1000 | 04/09/22 | 0,0602 | 0,0921
30/09/22 | A | 1000 | 05/10/22 | -0,0177| 0,0727
CodePudding user response:
You could use analytic function with windowing clause:
WITH
tbl AS
(
Select To_Date('30/09/22', 'dd/mm/yy') "DATE_SIMUL", 'A' "STOCK", To_Date('01/10/22', 'dd/mm/yy') "FUTURE_DATE", -0.0073 "RETURN" From Dual Union All
Select To_Date('30/09/22', 'dd/mm/yy') "DATE_SIMUL", 'A' "STOCK", To_Date('02/09/22', 'dd/mm/yy') "FUTURE_DATE", 0.0078 "RETURN" From Dual Union All
Select To_Date('30/09/22', 'dd/mm/yy') "DATE_SIMUL", 'A' "STOCK", To_Date('03/09/22', 'dd/mm/yy') "FUTURE_DATE", 0.0296 "RETURN" From Dual Union All
Select To_Date('30/09/22', 'dd/mm/yy') "DATE_SIMUL", 'A' "STOCK", To_Date('04/09/22', 'dd/mm/yy') "FUTURE_DATE", 0.0602 "RETURN" From Dual Union All
Select To_Date('30/09/22', 'dd/mm/yy') "DATE_SIMUL", 'A' "STOCK", To_Date('05/10/22', 'dd/mm/yy') "FUTURE_DATE", -0.0177 "RETURN" From Dual
)
Select
t.*,
Sum(t.RETURN) OVER(PARTITION BY t.STOCK ORDER BY t.STOCK, t.RN ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "CUMUL"
From
(SELECT ROW_NUMBER() OVER(Order By 1) "RN", tbl.* From tbl) t
Order By t.RN
/* R e s u l t :
RN DATE_SIMUL STOCK FUTURE_DATE RETURN CUMUL
---------- ---------- ----- ----------- ---------- ----------
1 30-SEP-22 A 01-OCT-22 -0.0073 -0.0073
2 30-SEP-22 A 02-SEP-22 .0078 .0005
3 30-SEP-22 A 03-SEP-22 .0296 .0301
4 30-SEP-22 A 04-SEP-22 .0602 .0903
5 30-SEP-22 A 05-OCT-22 -0.0177 .0726
*/
More about analytic functions at: https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions004.htm#SQLRF06174
ROW_NUMBER() is added to preserve your order of rows - it is not needed if you have some other column in the table to order the rows by. In that case put From tbl t instead of subquery and in Sum() function put your ordering column instead of RN.
If it is FUTURE_DATE column then it would be...
Select
t.*,
Sum(t.RETURN) OVER(PARTITION BY t.STOCK ORDER BY t.STOCK, t.FUTURE_DATE ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "CUMUL"
From
tbl t
/* R e s u l t :
DATE_SIMUL STOCK FUTURE_DATE RETURN CUMUL
---------- ----- ----------- ---------- ----------
30-SEP-22 A 02-SEP-22 .0078 .0078
30-SEP-22 A 03-SEP-22 .0296 .0374
30-SEP-22 A 04-SEP-22 .0602 .0976
30-SEP-22 A 01-OCT-22 -0.0073 .0903
30-SEP-22 A 05-OCT-22 -0.0177 .0726
*/
Regards...
Addition
if you create CTE (I named it grid) to get elements (1 RETURN) for later multiplication:
grid AS
(
Select ROW_NUMBER() OVER(ORDER BY FUTURE_DATE) "RN",
tbl.*,
1 RETURN "ELEMENT"
From tbl
)
/* R e s u l t :
RN DATE_SIMUL STOCK FUTURE_DATE RETURN ELEMENT
---------- ---------- ----- ----------- ---------- ----------
1 30-SEP-22 A 02-SEP-22 .0078 1.0078
2 30-SEP-22 A 03-SEP-22 .0296 1.0296
3 30-SEP-22 A 04-SEP-22 .0602 1.0602
4 30-SEP-22 A 01-OCT-22 -0.0073 .9927
5 30-SEP-22 A 05-OCT-22 -0.0177 .9823
*/
... then you can get cumulative compaund like below - using MODEL Clause
Select
RN, DATE_SIMUL, STOCK, FUTURE_DATE, RETURN, ELEMENT, CUMUL_COMPAUND
From
(
SELECT
RN, DATE_SIMUL, STOCK, FUTURE_DATE, RETURN, ELEMENT,
0 "CUMUL_COMPAUND"
FROM
grid
ORDER BY RN
)
MODEL
PARTITION BY (STOCK)
DIMENSION BY (RN)
MEASURES (DATE_SIMUL, FUTURE_DATE, RETURN, ELEMENT, CUMUL_COMPAUND)
RULES
(
CUMUL_COMPAUND[ANY] = CASE WHEN CV(RN) = 1 THEN ELEMENT[CV(RN)]
WHEN CV(RN) = 2 THEN ELEMENT[CV(RN)] * ELEMENT[1]
WHEN CV(RN) = 3 THEN ELEMENT[CV(RN)] * ELEMENT[1] * ELEMENT[2]
WHEN CV(RN) = 4 THEN ELEMENT[CV(RN)] * ELEMENT[1] * ELEMENT[2] * ELEMENT[3]
WHEN CV(RN) = 5 THEN ELEMENT[CV(RN)] * ELEMENT[1] * ELEMENT[2] * ELEMENT[3] * ELEMENT[4]
END - 1
)
/*
RN DATE_SIMUL STOCK FUTURE_DATE RETURN ELEMENT CUMUL_COMPAUND
---------- ---------- ----- ----------- ---------- ---------- --------------
1 30-SEP-22 A 02-SEP-22 .0078 1.0078 .0078
2 30-SEP-22 A 03-SEP-22 .0296 1.0296 .03763088
3 30-SEP-22 A 04-SEP-22 .0602 1.0602 .100096259
4 30-SEP-22 A 01-OCT-22 -0.0073 .9927 .0920655563
5 30-SEP-22 A 05-OCT-22 -0.0177 .9823 .0727359959
*/
Regards...