Skip to content

Commit c4eb241

Browse files
committed
feat: add VanguardDynamicSpending cash flow strategy
1 parent 1a01117 commit c4eb241

File tree

3 files changed

+144
-196
lines changed

3 files changed

+144
-196
lines changed

main_notebook.ipynb

Lines changed: 81 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": 1,
5+
"execution_count": 2,
66
"metadata": {
77
"ExecuteTime": {
88
"end_time": "2025-07-29T11:26:06.735435Z",
@@ -36,7 +36,7 @@
3636
},
3737
{
3838
"cell_type": "code",
39-
"execution_count": 2,
39+
"execution_count": 3,
4040
"metadata": {
4141
"ExecuteTime": {
4242
"end_time": "2025-07-29T11:26:09.894658Z",
@@ -54,7 +54,7 @@
5454
"'1.5.0'"
5555
]
5656
},
57-
"execution_count": 2,
57+
"execution_count": 3,
5858
"metadata": {},
5959
"output_type": "execute_result"
6060
}
@@ -77,7 +77,7 @@
7777
},
7878
{
7979
"cell_type": "code",
80-
"execution_count": 146,
80+
"execution_count": 4,
8181
"metadata": {
8282
"ExecuteTime": {
8383
"end_time": "2025-07-29T11:26:11.913869Z",
@@ -93,7 +93,7 @@
9393
},
9494
{
9595
"cell_type": "code",
96-
"execution_count": 221,
96+
"execution_count": 5,
9797
"metadata": {
9898
"ExecuteTime": {
9999
"end_time": "2025-07-29T11:26:55.347301Z",
@@ -109,7 +109,7 @@
109109
"name": "stdout",
110110
"output_type": "stream",
111111
"text": [
112-
"symbol portfolio_8286.PF\n",
112+
"symbol portfolio_6434.PF\n",
113113
"assets [RGBITR.INDX, RUCBTRNS.INDX, MCFTR.INDX, GC.COMM]\n",
114114
"weights [0.1, 0.1, 0.55, 0.25]\n",
115115
"rebalancing_period year\n",
@@ -750,7 +750,9 @@
750750
"output_type": "display_data"
751751
}
752752
],
753-
"source": "pf.dcf.wealth_index(discounting=\"fv\", include_negative_values=False).plot()"
753+
"source": [
754+
"pf.dcf.wealth_index(discounting=\"fv\", include_negative_values=False).plot()"
755+
]
754756
},
755757
{
756758
"cell_type": "markdown",
@@ -1471,10 +1473,10 @@
14711473
"evalue": "'PortfolioDCF' object has no attribute 'cash_flow_parameters'",
14721474
"output_type": "error",
14731475
"traceback": [
1474-
"\u001B[31m---------------------------------------------------------------------------\u001B[39m",
1475-
"\u001B[31mAttributeError\u001B[39m Traceback (most recent call last)",
1476-
"\u001B[36mCell\u001B[39m\u001B[36m \u001B[39m\u001B[32mIn[20]\u001B[39m\u001B[32m, line 1\u001B[39m\n\u001B[32m----> \u001B[39m\u001B[32m1\u001B[39m \u001B[43mpf\u001B[49m\u001B[43m.\u001B[49m\u001B[43mdcf\u001B[49m\u001B[43m.\u001B[49m\u001B[43mcash_flow_parameters\u001B[49m\n",
1477-
"\u001B[31mAttributeError\u001B[39m: 'PortfolioDCF' object has no attribute 'cash_flow_parameters'"
1476+
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
1477+
"\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)",
1478+
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[20]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mpf\u001b[49m\u001b[43m.\u001b[49m\u001b[43mdcf\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcash_flow_parameters\u001b[49m\n",
1479+
"\u001b[31mAttributeError\u001b[39m: 'PortfolioDCF' object has no attribute 'cash_flow_parameters'"
14781480
]
14791481
}
14801482
],
@@ -1527,7 +1529,9 @@
15271529
"output_type": "display_data"
15281530
}
15291531
],
1530-
"source": "pf.dcf.wealth_index(discounting=\"fv\", include_negative_values=False).plot()"
1532+
"source": [
1533+
"pf.dcf.wealth_index(discounting=\"fv\", include_negative_values=False).plot()"
1534+
]
15311535
},
15321536
{
15331537
"cell_type": "code",
@@ -1604,6 +1608,71 @@
16041608
"pf.dcf.plot_forecast_monte_carlo(backtest=True)"
16051609
]
16061610
},
1611+
{
1612+
"cell_type": "markdown",
1613+
"metadata": {},
1614+
"source": [
1615+
"### Vanguard Dynamic Spending"
1616+
]
1617+
},
1618+
{
1619+
"cell_type": "code",
1620+
"execution_count": 6,
1621+
"metadata": {},
1622+
"outputs": [],
1623+
"source": [
1624+
"# Fixed Percentage strategy\n",
1625+
"vds = ok.VanguardDynamicSpending(pf)\n",
1626+
"vds.initial_investment = 10_000_000\n",
1627+
"vds.frequency = \"year\"\n",
1628+
"vds.percentage = -0.07\n",
1629+
"vds.indexation = 0.09\n",
1630+
"vds.maximum_annual_withdrawal = 10_000_000 / 10 # 10%\n",
1631+
"vds.minimum_annual_withdrawal = 10_000_000 / 25 # 4%\n",
1632+
"vds.ceiling = 0.10\n",
1633+
"vds.floor = -0.10"
1634+
]
1635+
},
1636+
{
1637+
"cell_type": "code",
1638+
"execution_count": 7,
1639+
"metadata": {},
1640+
"outputs": [
1641+
{
1642+
"data": {
1643+
"text/plain": [
1644+
"Portfolio symbol portfolio_6434.PF\n",
1645+
"Cash flow initial investment 10000000\n",
1646+
"Cash flow frequency year\n",
1647+
"Cash flow strategy VDS\n",
1648+
"Cash flow percentage -0.07000\n",
1649+
"Minimum annual withdrawal 400,000.00000\n",
1650+
"Maximum annual withdrawal 1,000,000.00000\n",
1651+
"Ceiling 0.10000\n",
1652+
"Floor -0.10000\n",
1653+
"dtype: object"
1654+
]
1655+
},
1656+
"execution_count": 7,
1657+
"metadata": {},
1658+
"output_type": "execute_result"
1659+
}
1660+
],
1661+
"source": [
1662+
"vds"
1663+
]
1664+
},
1665+
{
1666+
"cell_type": "code",
1667+
"execution_count": null,
1668+
"metadata": {},
1669+
"outputs": [],
1670+
"source": [
1671+
"vds.calculate_withdrawal_size(\n",
1672+
" last_withdrawal=\n",
1673+
")"
1674+
]
1675+
},
16071676
{
16081677
"cell_type": "markdown",
16091678
"metadata": {},
@@ -1874,189 +1943,6 @@
18741943
" tolerance_rel=0.10,\n",
18751944
")"
18761945
]
1877-
},
1878-
{
1879-
"cell_type": "markdown",
1880-
"metadata": {},
1881-
"source": [
1882-
"#### Fixed Percentage"
1883-
]
1884-
},
1885-
{
1886-
"cell_type": "code",
1887-
"execution_count": 15,
1888-
"metadata": {
1889-
"ExecuteTime": {
1890-
"end_time": "2025-06-17T15:39:59.304002Z",
1891-
"start_time": "2025-06-17T15:39:59.234001Z"
1892-
}
1893-
},
1894-
"outputs": [
1895-
{
1896-
"name": "stdout",
1897-
"output_type": "stream",
1898-
"text": [
1899-
"Portfolio symbol portfolio_6436.PF\n",
1900-
"Cash flow initial investment 10000\n",
1901-
"Cash flow frequency year\n",
1902-
"Cash flow strategy fixed_percentage\n",
1903-
"Cash flow percentage -0.12000\n",
1904-
"dtype: object\n"
1905-
]
1906-
}
1907-
],
1908-
"source": [
1909-
"# Fixed Percentage strategy\n",
1910-
"pc = okama.portfolios.portfolio.cashflow_strategy.PercentageStrategy(pf)\n",
1911-
"pc.initial_investment = 10_000\n",
1912-
"pc.frequency = \"year\"\n",
1913-
"pc.percentage = -0.12\n",
1914-
"print(pc)"
1915-
]
1916-
},
1917-
{
1918-
"cell_type": "code",
1919-
"execution_count": 16,
1920-
"metadata": {
1921-
"ExecuteTime": {
1922-
"end_time": "2025-06-17T15:40:00.794567Z",
1923-
"start_time": "2025-06-17T15:40:00.733193Z"
1924-
}
1925-
},
1926-
"outputs": [],
1927-
"source": [
1928-
"pf.dcf.cashflow_parameters = pc"
1929-
]
1930-
},
1931-
{
1932-
"cell_type": "code",
1933-
"execution_count": 17,
1934-
"metadata": {
1935-
"ExecuteTime": {
1936-
"end_time": "2025-06-17T15:40:02.295463Z",
1937-
"start_time": "2025-06-17T15:40:02.232467Z"
1938-
}
1939-
},
1940-
"outputs": [],
1941-
"source": [
1942-
"pf.dcf.mc.period = 50\n",
1943-
"pf.dcf.mc.number = 100\n",
1944-
"pf.dcf.mc.distribution = \"norm\""
1945-
]
1946-
},
1947-
{
1948-
"cell_type": "code",
1949-
"execution_count": 18,
1950-
"metadata": {
1951-
"ExecuteTime": {
1952-
"end_time": "2025-06-17T15:40:16.348221Z",
1953-
"start_time": "2025-06-17T15:40:04.809674Z"
1954-
}
1955-
},
1956-
"outputs": [
1957-
{
1958-
"name": "stdout",
1959-
"output_type": "stream",
1960-
"text": [
1961-
"wealth_at_quantile=0.00, main_parameter=-1.000\n",
1962-
"error_rel=1.000, gradient=0.000\n",
1963-
"decreasing withdrawal\n",
1964-
"wealth_at_quantile=0.00, main_parameter=-0.500\n",
1965-
"error_rel=1.000, gradient=-0.000\n",
1966-
"decreasing withdrawal\n",
1967-
"wealth_at_quantile=0.06, main_parameter=-0.250\n",
1968-
"error_rel=1.000, gradient=-0.000\n",
1969-
"decreasing withdrawal\n",
1970-
"wealth_at_quantile=60.09, main_parameter=-0.125\n",
1971-
"error_rel=0.994, gradient=-0.006\n",
1972-
"decreasing withdrawal\n",
1973-
"wealth_at_quantile=1365.34, main_parameter=-0.062\n",
1974-
"error_rel=0.863, gradient=-0.131\n",
1975-
"decreasing withdrawal\n",
1976-
"wealth_at_quantile=5971.99, main_parameter=-0.031\n",
1977-
"error_rel=0.403, gradient=-0.461\n",
1978-
"decreasing withdrawal\n",
1979-
"wealth_at_quantile=11971.37, main_parameter=-0.016\n",
1980-
"error_rel=0.197, gradient=-0.206\n",
1981-
"increasing withdrawal\n",
1982-
"wealth_at_quantile=6993.80, main_parameter=-0.023\n",
1983-
"error_rel=0.301, gradient=0.103\n",
1984-
"decreasing withdrawal\n",
1985-
"wealth_at_quantile=9759.33, main_parameter=-0.020\n",
1986-
"error_rel=0.024, gradient=-0.277\n",
1987-
"solution found: -195.31 or 1.95% after 9 steps.\n"
1988-
]
1989-
},
1990-
{
1991-
"data": {
1992-
"text/plain": [
1993-
"success True\n",
1994-
"withdrawal_abs -195.31250\n",
1995-
"withdrawal_rel 0.01953\n",
1996-
"error_rel 0.02407\n",
1997-
"attempts 9\n",
1998-
"dtype: object"
1999-
]
2000-
},
2001-
"execution_count": 18,
2002-
"metadata": {},
2003-
"output_type": "execute_result"
2004-
}
2005-
],
2006-
"source": [
2007-
"sol = pf.dcf.find_the_largest_withdrawals_size(\n",
2008-
" goal=\"maintain_balance_pv\", percentile=20, tolerance_rel=0.10, threshold=0.05\n",
2009-
")\n",
2010-
"sol"
2011-
]
2012-
},
2013-
{
2014-
"cell_type": "code",
2015-
"execution_count": 19,
2016-
"metadata": {
2017-
"ExecuteTime": {
2018-
"end_time": "2025-06-17T15:40:23.638200Z",
2019-
"start_time": "2025-06-17T15:40:23.573691Z"
2020-
}
2021-
},
2022-
"outputs": [
2023-
{
2024-
"name": "stdout",
2025-
"output_type": "stream",
2026-
"text": [
2027-
"success True\n",
2028-
"withdrawal_abs -195.31250\n",
2029-
"withdrawal_rel 0.01953\n",
2030-
"error_rel 0.02407\n",
2031-
"attempts 9\n",
2032-
"dtype: object\n"
2033-
]
2034-
}
2035-
],
2036-
"source": [
2037-
"print(sol)"
2038-
]
2039-
},
2040-
{
2041-
"cell_type": "code",
2042-
"execution_count": null,
2043-
"metadata": {},
2044-
"outputs": [],
2045-
"source": [
2046-
"sol.solutions"
2047-
]
2048-
},
2049-
{
2050-
"cell_type": "code",
2051-
"execution_count": null,
2052-
"metadata": {
2053-
"collapsed": false,
2054-
"jupyter": {
2055-
"outputs_hidden": false
2056-
}
2057-
},
2058-
"outputs": [],
2059-
"source": []
20601946
}
20611947
],
20621948
"metadata": {

okama/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
)
3030
from okama.portfolios.mc import MonteCarlo
3131
from okama.portfolios.dcf import PortfolioDCF
32-
from okama.portfolios.cashflow_strategies import CashFlow, IndexationStrategy, PercentageStrategy, TimeSeriesStrategy
32+
from okama.portfolios.cashflow_strategies import CashFlow, IndexationStrategy, PercentageStrategy, TimeSeriesStrategy, VanguardDynamicSpending
3333
from okama.macro import Inflation, Rate, Indicator
3434
from okama.frontier.multi_period import EfficientFrontierReb
3535
from okama.frontier.single_period import EfficientFrontier

0 commit comments

Comments
 (0)