fundedness 0.1.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fundedness might be problematic. Click here for more details.
- {fundedness-0.1.0 → fundedness-0.2.1}/PKG-INFO +41 -9
- {fundedness-0.1.0 → fundedness-0.2.1}/README.md +40 -8
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/api/core.md +1 -1
- fundedness-0.2.1/docs/api/merton.md +91 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/api/models.md +11 -3
- fundedness-0.2.1/docs/api/viz.md +23 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/api/withdrawals.md +5 -3
- fundedness-0.2.1/docs/guide/utility-optimization.md +200 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/index.md +3 -1
- {fundedness-0.1.0 → fundedness-0.2.1}/examples/01_cefr_basics.ipynb +1 -1
- {fundedness-0.1.0 → fundedness-0.2.1}/examples/02_time_distribution.ipynb +1 -1
- {fundedness-0.1.0 → fundedness-0.2.1}/examples/03_withdrawal_comparison.ipynb +1 -1
- fundedness-0.2.1/fundedness/__init__.py +71 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/allocation/__init__.py +8 -0
- fundedness-0.2.1/fundedness/allocation/merton_optimal.py +220 -0
- fundedness-0.2.1/fundedness/merton.py +289 -0
- fundedness-0.2.1/fundedness/optimize.py +473 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/simulate.py +158 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/__init__.py +14 -0
- fundedness-0.2.1/fundedness/viz/optimal.py +542 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/withdrawals/__init__.py +8 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/withdrawals/fixed_swr.py +61 -0
- fundedness-0.2.1/fundedness/withdrawals/merton_optimal.py +286 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/mkdocs.yml +2 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/pyproject.toml +1 -1
- fundedness-0.2.1/streamlit_app/pages/5_Utility_Optimization.py +440 -0
- fundedness-0.2.1/tests/test_merton.py +311 -0
- fundedness-0.2.1/tests/test_optimize.py +336 -0
- fundedness-0.1.0/docs/api/viz.md +0 -29
- fundedness-0.1.0/fundedness/__init__.py +0 -38
- {fundedness-0.1.0 → fundedness-0.2.1}/.github/workflows/docs.yml +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/.github/workflows/publish.yml +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/.gitignore +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/CLAUDE.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/api/__init__.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/api/main.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/api/routes/__init__.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/api/routes/cefr.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/api/routes/compare.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/api/routes/simulate.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/background_information.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/examples/tutorials.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/getting-started/installation.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/getting-started/quickstart.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/guide/cefr.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/guide/simulations.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/guide/visualizations.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/docs/guide/withdrawals.md +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/allocation/base.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/allocation/constant.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/allocation/glidepath.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/cefr.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/liabilities.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/liquidity.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/__init__.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/assets.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/household.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/liabilities.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/market.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/simulation.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/tax.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/models/utility.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/policies.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/risk.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/colors.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/comparison.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/fan_chart.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/histogram.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/survival.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/tornado.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/viz/waterfall.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/withdrawals/base.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/withdrawals/comparison.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/withdrawals/guardrails.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/withdrawals/rmd_style.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/fundedness/withdrawals/vpw.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/requirements.txt +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/__init__.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/app.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/components/__init__.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/components/asset_editor.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/components/liability_editor.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/components/metrics_display.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/pages/0_Inputs.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/pages/1_CEFR_Dashboard.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/pages/2_Time_Runway.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/pages/3_Withdrawal_Lab.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/pages/4_Sensitivity.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/utils/__init__.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/streamlit_app/utils/session_state.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/tests/__init__.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/tests/conftest.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/tests/test_api.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/tests/test_cefr.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/tests/test_liabilities.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/tests/test_simulate.py +0 -0
- {fundedness-0.1.0 → fundedness-0.2.1}/tests/test_withdrawals.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fundedness
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful visualizations
|
|
5
5
|
Project-URL: Homepage, https://github.com/engineerinvestor/financial-health-calculator
|
|
6
6
|
Project-URL: Documentation, https://engineerinvestor.github.io/financial-health-calculator/
|
|
@@ -54,15 +54,22 @@ Description-Content-Type: text/markdown
|
|
|
54
54
|
|
|
55
55
|
# Financial Health Calculator
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
[](https://pypi.org/project/fundedness/)
|
|
58
|
+
[](https://pypi.org/project/fundedness/)
|
|
59
|
+
[](https://github.com/engineerinvestor/financial-health-calculator/actions/workflows/ci.yml)
|
|
60
|
+
[](https://codecov.io/gh/engineerinvestor/financial-health-calculator)
|
|
61
|
+
[](https://engineerinvestor.github.io/financial-health-calculator/)
|
|
62
|
+
[](https://opensource.org/licenses/MIT)
|
|
59
63
|
[](https://colab.research.google.com/github/engineerinvestor/financial-health-calculator/blob/main/examples/01_cefr_basics.ipynb)
|
|
60
64
|
|
|
65
|
+
A comprehensive Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful Plotly visualizations.
|
|
66
|
+
|
|
61
67
|
## Features
|
|
62
68
|
|
|
63
69
|
- **CEFR (Certainty-Equivalent Funded Ratio)**: A fundedness metric that accounts for taxes, liquidity, and concentration risk
|
|
64
70
|
- **Monte Carlo Simulations**: Project retirement outcomes with configurable market assumptions
|
|
65
|
-
- **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW,
|
|
71
|
+
- **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW, RMD-style, and Merton optimal
|
|
72
|
+
- **Utility Optimization**: Merton optimal spending and allocation based on lifetime utility maximization
|
|
66
73
|
- **Beautiful Visualizations**: Interactive Plotly charts with fan charts, waterfalls, and survival curves
|
|
67
74
|
- **REST API**: FastAPI backend for programmatic access
|
|
68
75
|
- **Streamlit App**: User-friendly web interface
|
|
@@ -72,12 +79,12 @@ A comprehensive Python financial planning toolkit with CEFR calculations, Monte
|
|
|
72
79
|
### Installation
|
|
73
80
|
|
|
74
81
|
```bash
|
|
75
|
-
pip install
|
|
82
|
+
pip install fundedness
|
|
76
83
|
```
|
|
77
84
|
|
|
78
85
|
For development with all extras:
|
|
79
86
|
```bash
|
|
80
|
-
pip install "
|
|
87
|
+
pip install "fundedness[all]"
|
|
81
88
|
```
|
|
82
89
|
|
|
83
90
|
### Basic Usage
|
|
@@ -176,6 +183,22 @@ Where τ = tax rate, λ = liquidity factor, ρ = reliability factor
|
|
|
176
183
|
| Guardrails | Adjustable with floor/ceiling | Balance |
|
|
177
184
|
| VPW | Age-based variable percentage | Maximizing spending |
|
|
178
185
|
| RMD-Style | IRS distribution table based | Tax efficiency |
|
|
186
|
+
| Merton Optimal | Utility-maximizing spending rate | Optimality |
|
|
187
|
+
|
|
188
|
+
### Utility Optimization
|
|
189
|
+
|
|
190
|
+
The toolkit includes Merton's optimal consumption and portfolio choice framework, as applied in modern retirement planning research<sup>[1]</sup>:
|
|
191
|
+
|
|
192
|
+
- **Optimal Equity Allocation**: `k* = (μ - r) / (γ × σ²)`
|
|
193
|
+
- **Wealth-Adjusted Allocation**: Reduces equity as wealth approaches subsistence floor
|
|
194
|
+
- **Optimal Spending Rate**: Increases with age as horizon shortens
|
|
195
|
+
- **Expected Lifetime Utility**: Track utility across Monte Carlo paths
|
|
196
|
+
|
|
197
|
+
Key insights from this methodology:
|
|
198
|
+
1. Optimal spending starts low (~2-3%) and rises with age
|
|
199
|
+
2. Allocation should decrease as wealth approaches the floor
|
|
200
|
+
3. Risk aversion (gamma) is the critical input parameter
|
|
201
|
+
4. The 4% rule is suboptimal from a utility perspective
|
|
179
202
|
|
|
180
203
|
## Development
|
|
181
204
|
|
|
@@ -207,13 +230,16 @@ financial-health-calculator/
|
|
|
207
230
|
├── fundedness/ # Core Python package
|
|
208
231
|
│ ├── models/ # Pydantic data models
|
|
209
232
|
│ ├── viz/ # Plotly visualizations
|
|
210
|
-
│ ├── withdrawals/ # Withdrawal strategies
|
|
211
|
-
│ ├── allocation/ # Asset allocation strategies
|
|
233
|
+
│ ├── withdrawals/ # Withdrawal strategies (SWR, guardrails, VPW, Merton)
|
|
234
|
+
│ ├── allocation/ # Asset allocation strategies (constant, glidepath, Merton)
|
|
212
235
|
│ ├── cefr.py # CEFR calculation
|
|
213
|
-
│ ├── simulate.py # Monte Carlo engine
|
|
236
|
+
│ ├── simulate.py # Monte Carlo engine with utility tracking
|
|
237
|
+
│ ├── merton.py # Merton optimal formulas
|
|
238
|
+
│ ├── optimize.py # Policy parameter optimization
|
|
214
239
|
│ └── policies.py # Spending/allocation policies
|
|
215
240
|
├── api/ # FastAPI REST API
|
|
216
241
|
├── streamlit_app/ # Streamlit web application
|
|
242
|
+
│ └── pages/ # Includes Utility Optimization page
|
|
217
243
|
├── examples/ # Jupyter notebooks
|
|
218
244
|
└── tests/ # pytest tests
|
|
219
245
|
```
|
|
@@ -228,6 +254,12 @@ financial-health-calculator/
|
|
|
228
254
|
|
|
229
255
|
MIT License
|
|
230
256
|
|
|
257
|
+
## References
|
|
258
|
+
|
|
259
|
+
1. Haghani, V., & White, J. (2023). *The Missing Billionaires: A Guide to Better Financial Decisions*. Wiley. See also [Elm Wealth](https://elmwealth.com/) for related research on optimal spending and allocation.
|
|
260
|
+
|
|
261
|
+
2. Merton, R. C. (1969). Lifetime Portfolio Selection under Uncertainty: The Continuous-Time Case. *The Review of Economics and Statistics*, 51(3), 247-257.
|
|
262
|
+
|
|
231
263
|
## Disclaimer
|
|
232
264
|
|
|
233
265
|
This tool is for educational purposes only and does not constitute financial advice. Consult a qualified financial advisor for personalized recommendations.
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
# Financial Health Calculator
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://pypi.org/project/fundedness/)
|
|
4
|
+
[](https://pypi.org/project/fundedness/)
|
|
5
|
+
[](https://github.com/engineerinvestor/financial-health-calculator/actions/workflows/ci.yml)
|
|
6
|
+
[](https://codecov.io/gh/engineerinvestor/financial-health-calculator)
|
|
7
|
+
[](https://engineerinvestor.github.io/financial-health-calculator/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
9
|
[](https://colab.research.google.com/github/engineerinvestor/financial-health-calculator/blob/main/examples/01_cefr_basics.ipynb)
|
|
6
10
|
|
|
11
|
+
A comprehensive Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful Plotly visualizations.
|
|
12
|
+
|
|
7
13
|
## Features
|
|
8
14
|
|
|
9
15
|
- **CEFR (Certainty-Equivalent Funded Ratio)**: A fundedness metric that accounts for taxes, liquidity, and concentration risk
|
|
10
16
|
- **Monte Carlo Simulations**: Project retirement outcomes with configurable market assumptions
|
|
11
|
-
- **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW,
|
|
17
|
+
- **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW, RMD-style, and Merton optimal
|
|
18
|
+
- **Utility Optimization**: Merton optimal spending and allocation based on lifetime utility maximization
|
|
12
19
|
- **Beautiful Visualizations**: Interactive Plotly charts with fan charts, waterfalls, and survival curves
|
|
13
20
|
- **REST API**: FastAPI backend for programmatic access
|
|
14
21
|
- **Streamlit App**: User-friendly web interface
|
|
@@ -18,12 +25,12 @@ A comprehensive Python financial planning toolkit with CEFR calculations, Monte
|
|
|
18
25
|
### Installation
|
|
19
26
|
|
|
20
27
|
```bash
|
|
21
|
-
pip install
|
|
28
|
+
pip install fundedness
|
|
22
29
|
```
|
|
23
30
|
|
|
24
31
|
For development with all extras:
|
|
25
32
|
```bash
|
|
26
|
-
pip install "
|
|
33
|
+
pip install "fundedness[all]"
|
|
27
34
|
```
|
|
28
35
|
|
|
29
36
|
### Basic Usage
|
|
@@ -122,6 +129,22 @@ Where τ = tax rate, λ = liquidity factor, ρ = reliability factor
|
|
|
122
129
|
| Guardrails | Adjustable with floor/ceiling | Balance |
|
|
123
130
|
| VPW | Age-based variable percentage | Maximizing spending |
|
|
124
131
|
| RMD-Style | IRS distribution table based | Tax efficiency |
|
|
132
|
+
| Merton Optimal | Utility-maximizing spending rate | Optimality |
|
|
133
|
+
|
|
134
|
+
### Utility Optimization
|
|
135
|
+
|
|
136
|
+
The toolkit includes Merton's optimal consumption and portfolio choice framework, as applied in modern retirement planning research<sup>[1]</sup>:
|
|
137
|
+
|
|
138
|
+
- **Optimal Equity Allocation**: `k* = (μ - r) / (γ × σ²)`
|
|
139
|
+
- **Wealth-Adjusted Allocation**: Reduces equity as wealth approaches subsistence floor
|
|
140
|
+
- **Optimal Spending Rate**: Increases with age as horizon shortens
|
|
141
|
+
- **Expected Lifetime Utility**: Track utility across Monte Carlo paths
|
|
142
|
+
|
|
143
|
+
Key insights from this methodology:
|
|
144
|
+
1. Optimal spending starts low (~2-3%) and rises with age
|
|
145
|
+
2. Allocation should decrease as wealth approaches the floor
|
|
146
|
+
3. Risk aversion (gamma) is the critical input parameter
|
|
147
|
+
4. The 4% rule is suboptimal from a utility perspective
|
|
125
148
|
|
|
126
149
|
## Development
|
|
127
150
|
|
|
@@ -153,13 +176,16 @@ financial-health-calculator/
|
|
|
153
176
|
├── fundedness/ # Core Python package
|
|
154
177
|
│ ├── models/ # Pydantic data models
|
|
155
178
|
│ ├── viz/ # Plotly visualizations
|
|
156
|
-
│ ├── withdrawals/ # Withdrawal strategies
|
|
157
|
-
│ ├── allocation/ # Asset allocation strategies
|
|
179
|
+
│ ├── withdrawals/ # Withdrawal strategies (SWR, guardrails, VPW, Merton)
|
|
180
|
+
│ ├── allocation/ # Asset allocation strategies (constant, glidepath, Merton)
|
|
158
181
|
│ ├── cefr.py # CEFR calculation
|
|
159
|
-
│ ├── simulate.py # Monte Carlo engine
|
|
182
|
+
│ ├── simulate.py # Monte Carlo engine with utility tracking
|
|
183
|
+
│ ├── merton.py # Merton optimal formulas
|
|
184
|
+
│ ├── optimize.py # Policy parameter optimization
|
|
160
185
|
│ └── policies.py # Spending/allocation policies
|
|
161
186
|
├── api/ # FastAPI REST API
|
|
162
187
|
├── streamlit_app/ # Streamlit web application
|
|
188
|
+
│ └── pages/ # Includes Utility Optimization page
|
|
163
189
|
├── examples/ # Jupyter notebooks
|
|
164
190
|
└── tests/ # pytest tests
|
|
165
191
|
```
|
|
@@ -174,6 +200,12 @@ financial-health-calculator/
|
|
|
174
200
|
|
|
175
201
|
MIT License
|
|
176
202
|
|
|
203
|
+
## References
|
|
204
|
+
|
|
205
|
+
1. Haghani, V., & White, J. (2023). *The Missing Billionaires: A Guide to Better Financial Decisions*. Wiley. See also [Elm Wealth](https://elmwealth.com/) for related research on optimal spending and allocation.
|
|
206
|
+
|
|
207
|
+
2. Merton, R. C. (1969). Lifetime Portfolio Selection under Uncertainty: The Continuous-Time Case. *The Review of Economics and Statistics*, 51(3), 247-257.
|
|
208
|
+
|
|
177
209
|
## Disclaimer
|
|
178
210
|
|
|
179
211
|
This tool is for educational purposes only and does not constitute financial advice. Consult a qualified financial advisor for personalized recommendations.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Merton Optimal API Reference
|
|
2
|
+
|
|
3
|
+
This module provides Merton's optimal consumption and portfolio choice formulas.
|
|
4
|
+
|
|
5
|
+
## Core Functions
|
|
6
|
+
|
|
7
|
+
::: fundedness.merton.merton_optimal_allocation
|
|
8
|
+
options:
|
|
9
|
+
show_root_heading: true
|
|
10
|
+
|
|
11
|
+
::: fundedness.merton.certainty_equivalent_return
|
|
12
|
+
options:
|
|
13
|
+
show_root_heading: true
|
|
14
|
+
|
|
15
|
+
::: fundedness.merton.merton_optimal_spending_rate
|
|
16
|
+
options:
|
|
17
|
+
show_root_heading: true
|
|
18
|
+
|
|
19
|
+
::: fundedness.merton.wealth_adjusted_optimal_allocation
|
|
20
|
+
options:
|
|
21
|
+
show_root_heading: true
|
|
22
|
+
|
|
23
|
+
::: fundedness.merton.calculate_merton_optimal
|
|
24
|
+
options:
|
|
25
|
+
show_root_heading: true
|
|
26
|
+
|
|
27
|
+
## Helper Functions
|
|
28
|
+
|
|
29
|
+
::: fundedness.merton.optimal_spending_by_age
|
|
30
|
+
options:
|
|
31
|
+
show_root_heading: true
|
|
32
|
+
|
|
33
|
+
::: fundedness.merton.optimal_allocation_by_wealth
|
|
34
|
+
options:
|
|
35
|
+
show_root_heading: true
|
|
36
|
+
|
|
37
|
+
## Data Classes
|
|
38
|
+
|
|
39
|
+
::: fundedness.merton.MertonOptimalResult
|
|
40
|
+
options:
|
|
41
|
+
show_root_heading: true
|
|
42
|
+
|
|
43
|
+
## Spending Policies
|
|
44
|
+
|
|
45
|
+
::: fundedness.withdrawals.merton_optimal.MertonOptimalSpendingPolicy
|
|
46
|
+
options:
|
|
47
|
+
show_root_heading: true
|
|
48
|
+
|
|
49
|
+
::: fundedness.withdrawals.merton_optimal.SmoothedMertonPolicy
|
|
50
|
+
options:
|
|
51
|
+
show_root_heading: true
|
|
52
|
+
|
|
53
|
+
::: fundedness.withdrawals.merton_optimal.FloorAdjustedMertonPolicy
|
|
54
|
+
options:
|
|
55
|
+
show_root_heading: true
|
|
56
|
+
|
|
57
|
+
## Allocation Policies
|
|
58
|
+
|
|
59
|
+
::: fundedness.allocation.merton_optimal.MertonOptimalAllocationPolicy
|
|
60
|
+
options:
|
|
61
|
+
show_root_heading: true
|
|
62
|
+
|
|
63
|
+
::: fundedness.allocation.merton_optimal.WealthBasedAllocationPolicy
|
|
64
|
+
options:
|
|
65
|
+
show_root_heading: true
|
|
66
|
+
|
|
67
|
+
::: fundedness.allocation.merton_optimal.FloorProtectionAllocationPolicy
|
|
68
|
+
options:
|
|
69
|
+
show_root_heading: true
|
|
70
|
+
|
|
71
|
+
## Policy Optimization
|
|
72
|
+
|
|
73
|
+
::: fundedness.optimize.PolicyParameterSpec
|
|
74
|
+
options:
|
|
75
|
+
show_root_heading: true
|
|
76
|
+
|
|
77
|
+
::: fundedness.optimize.OptimizationResult
|
|
78
|
+
options:
|
|
79
|
+
show_root_heading: true
|
|
80
|
+
|
|
81
|
+
::: fundedness.optimize.optimize_spending_policy
|
|
82
|
+
options:
|
|
83
|
+
show_root_heading: true
|
|
84
|
+
|
|
85
|
+
::: fundedness.optimize.optimize_allocation_policy
|
|
86
|
+
options:
|
|
87
|
+
show_root_heading: true
|
|
88
|
+
|
|
89
|
+
::: fundedness.optimize.optimize_combined_policy
|
|
90
|
+
options:
|
|
91
|
+
show_root_heading: true
|
|
@@ -14,7 +14,7 @@ Pydantic models for representing financial data.
|
|
|
14
14
|
|
|
15
15
|
## Balance Sheet
|
|
16
16
|
|
|
17
|
-
::: fundedness.models.
|
|
17
|
+
::: fundedness.models.assets.BalanceSheet
|
|
18
18
|
|
|
19
19
|
## Liabilities
|
|
20
20
|
|
|
@@ -26,6 +26,14 @@ Pydantic models for representing financial data.
|
|
|
26
26
|
|
|
27
27
|
::: fundedness.models.household.Person
|
|
28
28
|
|
|
29
|
-
## Market
|
|
29
|
+
## Market Model
|
|
30
30
|
|
|
31
|
-
::: fundedness.models.
|
|
31
|
+
::: fundedness.models.market.MarketModel
|
|
32
|
+
|
|
33
|
+
## Simulation Config
|
|
34
|
+
|
|
35
|
+
::: fundedness.models.simulation.SimulationConfig
|
|
36
|
+
|
|
37
|
+
## Tax Model
|
|
38
|
+
|
|
39
|
+
::: fundedness.models.tax.TaxModel
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Visualization Functions
|
|
2
|
+
|
|
3
|
+
Plotly chart generation functions.
|
|
4
|
+
|
|
5
|
+
## CEFR Charts
|
|
6
|
+
|
|
7
|
+
::: fundedness.viz.waterfall.create_cefr_waterfall
|
|
8
|
+
|
|
9
|
+
## Simulation Charts
|
|
10
|
+
|
|
11
|
+
::: fundedness.viz.fan_chart.create_fan_chart
|
|
12
|
+
|
|
13
|
+
::: fundedness.viz.survival.create_survival_curve
|
|
14
|
+
|
|
15
|
+
::: fundedness.viz.histogram.create_time_distribution_histogram
|
|
16
|
+
|
|
17
|
+
## Comparison Charts
|
|
18
|
+
|
|
19
|
+
::: fundedness.viz.comparison.create_strategy_comparison_chart
|
|
20
|
+
|
|
21
|
+
## Sensitivity Charts
|
|
22
|
+
|
|
23
|
+
::: fundedness.viz.tornado.create_tornado_chart
|
|
@@ -6,13 +6,15 @@ Classes implementing different withdrawal strategies.
|
|
|
6
6
|
|
|
7
7
|
::: fundedness.withdrawals.base.WithdrawalPolicy
|
|
8
8
|
|
|
9
|
+
::: fundedness.withdrawals.base.BaseWithdrawalPolicy
|
|
10
|
+
|
|
9
11
|
## Fixed SWR
|
|
10
12
|
|
|
11
|
-
::: fundedness.withdrawals.fixed_swr.
|
|
13
|
+
::: fundedness.withdrawals.fixed_swr.FixedRealSWRPolicy
|
|
12
14
|
|
|
13
15
|
## Percentage of Portfolio
|
|
14
16
|
|
|
15
|
-
::: fundedness.withdrawals.
|
|
17
|
+
::: fundedness.withdrawals.fixed_swr.PercentOfPortfolioPolicy
|
|
16
18
|
|
|
17
19
|
## Guardrails
|
|
18
20
|
|
|
@@ -24,4 +26,4 @@ Classes implementing different withdrawal strategies.
|
|
|
24
26
|
|
|
25
27
|
## RMD-Style
|
|
26
28
|
|
|
27
|
-
::: fundedness.withdrawals.
|
|
29
|
+
::: fundedness.withdrawals.rmd_style.RMDStylePolicy
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Utility Optimization
|
|
2
|
+
|
|
3
|
+
This guide covers the utility-optimal spending and allocation framework based on Merton's continuous-time portfolio optimization theory.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Traditional retirement planning often uses rules of thumb like the "4% rule." While simple, these approaches don't account for individual preferences about risk and the trade-off between spending now versus later.
|
|
8
|
+
|
|
9
|
+
Utility optimization provides a rigorous framework for finding the **optimal** spending rate and asset allocation based on:
|
|
10
|
+
|
|
11
|
+
- **Risk aversion (gamma)**: How much you dislike uncertainty
|
|
12
|
+
- **Time preference**: How much you prefer spending now vs. later
|
|
13
|
+
- **Subsistence floor**: Minimum spending you need to survive
|
|
14
|
+
|
|
15
|
+
## Key Formulas
|
|
16
|
+
|
|
17
|
+
### Optimal Equity Allocation
|
|
18
|
+
|
|
19
|
+
The Merton formula gives the optimal fraction to invest in risky assets:
|
|
20
|
+
|
|
21
|
+
$$k^* = \frac{\mu - r}{\gamma \times \sigma^2}$$
|
|
22
|
+
|
|
23
|
+
Where:
|
|
24
|
+
|
|
25
|
+
- $\mu$ = expected stock return
|
|
26
|
+
- $r$ = bond/risk-free return
|
|
27
|
+
- $\gamma$ = risk aversion coefficient
|
|
28
|
+
- $\sigma$ = stock volatility
|
|
29
|
+
|
|
30
|
+
### Certainty Equivalent Return
|
|
31
|
+
|
|
32
|
+
The guaranteed return that provides the same utility as the risky portfolio:
|
|
33
|
+
|
|
34
|
+
$$r_{CE} = r + k^*(\mu - r) - \frac{\gamma \times k^{*2} \times \sigma^2}{2}$$
|
|
35
|
+
|
|
36
|
+
### Optimal Spending Rate
|
|
37
|
+
|
|
38
|
+
For an infinite horizon:
|
|
39
|
+
|
|
40
|
+
$$c^* = r_{CE} - \frac{r_{CE} - \rho}{\gamma}$$
|
|
41
|
+
|
|
42
|
+
Where $\rho$ is your time preference (discount rate).
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from fundedness import (
|
|
48
|
+
calculate_merton_optimal,
|
|
49
|
+
MarketModel,
|
|
50
|
+
UtilityModel,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Define market assumptions
|
|
54
|
+
market = MarketModel(
|
|
55
|
+
stock_return=0.05, # 5% expected real return
|
|
56
|
+
bond_return=0.015, # 1.5% risk-free rate
|
|
57
|
+
stock_volatility=0.16, # 16% annual volatility
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Define your preferences
|
|
61
|
+
utility = UtilityModel(
|
|
62
|
+
gamma=3.0, # Risk aversion (typical: 2-5)
|
|
63
|
+
subsistence_floor=30000, # Minimum annual spending
|
|
64
|
+
time_preference=0.02, # 2% discount rate
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Calculate optimal policy
|
|
68
|
+
result = calculate_merton_optimal(
|
|
69
|
+
wealth=1_000_000,
|
|
70
|
+
market_model=market,
|
|
71
|
+
utility_model=utility,
|
|
72
|
+
remaining_years=30,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
print(f"Optimal equity allocation: {result.optimal_equity_allocation:.1%}")
|
|
76
|
+
print(f"Wealth-adjusted allocation: {result.wealth_adjusted_allocation:.1%}")
|
|
77
|
+
print(f"Optimal spending rate: {result.optimal_spending_rate:.1%}")
|
|
78
|
+
print(f"Year 1 spending: ${1_000_000 * result.optimal_spending_rate:,.0f}")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Wealth-Adjusted Allocation
|
|
82
|
+
|
|
83
|
+
Near the subsistence floor, you can't afford to take risk. The wealth-adjusted allocation accounts for this:
|
|
84
|
+
|
|
85
|
+
$$k_{adj} = k^* \times \frac{W - F}{W}$$
|
|
86
|
+
|
|
87
|
+
Where $W$ is wealth and $F$ is the subsistence floor.
|
|
88
|
+
|
|
89
|
+
As wealth approaches the floor, allocation approaches zero. As wealth rises far above the floor, allocation approaches the unconstrained optimal.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from fundedness import wealth_adjusted_optimal_allocation
|
|
93
|
+
|
|
94
|
+
# Near the floor: low allocation
|
|
95
|
+
k_low = wealth_adjusted_optimal_allocation(
|
|
96
|
+
wealth=50_000, # Just above $30k floor
|
|
97
|
+
market_model=market,
|
|
98
|
+
utility_model=utility,
|
|
99
|
+
)
|
|
100
|
+
print(f"Allocation at $50k: {k_low:.1%}") # ~18%
|
|
101
|
+
|
|
102
|
+
# Well above floor: approaches optimal
|
|
103
|
+
k_high = wealth_adjusted_optimal_allocation(
|
|
104
|
+
wealth=2_000_000,
|
|
105
|
+
market_model=market,
|
|
106
|
+
utility_model=utility,
|
|
107
|
+
)
|
|
108
|
+
print(f"Allocation at $2M: {k_high:.1%}") # ~44%
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Spending Rate by Age
|
|
112
|
+
|
|
113
|
+
Optimal spending rate increases with age as the remaining horizon shortens:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from fundedness import optimal_spending_by_age
|
|
117
|
+
|
|
118
|
+
rates = optimal_spending_by_age(
|
|
119
|
+
market_model=market,
|
|
120
|
+
utility_model=utility,
|
|
121
|
+
starting_age=65,
|
|
122
|
+
end_age=95,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
for age in [65, 75, 85, 95]:
|
|
126
|
+
print(f"Age {age}: {rates[age]:.1%}")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Example output:
|
|
130
|
+
```
|
|
131
|
+
Age 65: 3.8%
|
|
132
|
+
Age 75: 5.2%
|
|
133
|
+
Age 85: 7.8%
|
|
134
|
+
Age 95: 100.0%
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Using Merton Policies in Simulation
|
|
138
|
+
|
|
139
|
+
### Merton Spending Policy
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from fundedness.withdrawals import MertonOptimalSpendingPolicy
|
|
143
|
+
from fundedness.allocation import MertonOptimalAllocationPolicy
|
|
144
|
+
from fundedness import run_simulation_with_utility, SimulationConfig
|
|
145
|
+
|
|
146
|
+
# Create policies
|
|
147
|
+
spending_policy = MertonOptimalSpendingPolicy(
|
|
148
|
+
market_model=market,
|
|
149
|
+
utility_model=utility,
|
|
150
|
+
starting_age=65,
|
|
151
|
+
end_age=95,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
allocation_policy = MertonOptimalAllocationPolicy(
|
|
155
|
+
market_model=market,
|
|
156
|
+
utility_model=utility,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Run simulation with utility tracking
|
|
160
|
+
config = SimulationConfig(
|
|
161
|
+
n_simulations=5000,
|
|
162
|
+
n_years=30,
|
|
163
|
+
market_model=market,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
result = run_simulation_with_utility(
|
|
167
|
+
initial_wealth=1_000_000,
|
|
168
|
+
spending_policy=spending_policy,
|
|
169
|
+
allocation_policy=allocation_policy,
|
|
170
|
+
config=config,
|
|
171
|
+
utility_model=utility,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
print(f"Expected lifetime utility: {result.expected_lifetime_utility:.2e}")
|
|
175
|
+
print(f"Certainty equivalent: ${result.certainty_equivalent_consumption:,.0f}/year")
|
|
176
|
+
print(f"Success rate: {result.success_rate:.1%}")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Key Insights
|
|
180
|
+
|
|
181
|
+
1. **Spending rate increases with age** - As the horizon shortens, you should spend more
|
|
182
|
+
2. **Allocation decreases near the floor** - You can't afford risk when close to subsistence
|
|
183
|
+
3. **Risk aversion (gamma) is critical** - Higher gamma means lower allocation and spending
|
|
184
|
+
4. **The 4% rule is often suboptimal** - Merton optimal typically starts lower and ends higher
|
|
185
|
+
|
|
186
|
+
## Choosing Your Risk Aversion
|
|
187
|
+
|
|
188
|
+
Risk aversion (gamma) is the most important input. Guidelines:
|
|
189
|
+
|
|
190
|
+
| Gamma | Risk Tolerance | Profile |
|
|
191
|
+
|-------|----------------|---------|
|
|
192
|
+
| 1-2 | High | Comfortable with volatility |
|
|
193
|
+
| 2-3 | Moderate | Typical investor |
|
|
194
|
+
| 3-5 | Low | Prefers stability |
|
|
195
|
+
| 5+ | Very low | Strong loss aversion |
|
|
196
|
+
|
|
197
|
+
## References
|
|
198
|
+
|
|
199
|
+
- Merton, R. C. (1969). Lifetime Portfolio Selection under Uncertainty: The Continuous-Time Case. *The Review of Economics and Statistics*, 51(3), 247-257.
|
|
200
|
+
- Haghani, V., & White, J. (2023). *The Missing Billionaires: A Guide to Better Financial Decisions*. Wiley.
|
|
@@ -8,7 +8,8 @@ A comprehensive Python financial planning toolkit with CEFR calculations, Monte
|
|
|
8
8
|
|
|
9
9
|
- **CEFR (Certainty-Equivalent Funded Ratio)**: A fundedness metric that accounts for taxes, liquidity, and concentration risk
|
|
10
10
|
- **Monte Carlo Simulations**: Project retirement outcomes with configurable market assumptions
|
|
11
|
-
- **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW,
|
|
11
|
+
- **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW, RMD-style, and Merton optimal
|
|
12
|
+
- **Utility Optimization**: Merton optimal spending and allocation based on lifetime utility maximization
|
|
12
13
|
- **Beautiful Visualizations**: Interactive Plotly charts with fan charts, waterfalls, and survival curves
|
|
13
14
|
- **REST API**: FastAPI backend for programmatic access
|
|
14
15
|
- **Streamlit App**: User-friendly web interface
|
|
@@ -55,6 +56,7 @@ print(f"CEFR: {result.cefr:.2f}")
|
|
|
55
56
|
- [CEFR Explained](guide/cefr.md) - Understanding the CEFR metric
|
|
56
57
|
- [Monte Carlo Simulations](guide/simulations.md) - Running projections
|
|
57
58
|
- [Withdrawal Strategies](guide/withdrawals.md) - Comparing approaches
|
|
59
|
+
- [Utility Optimization](guide/utility-optimization.md) - Merton optimal spending and allocation
|
|
58
60
|
- [Visualizations](guide/visualizations.md) - Creating charts
|
|
59
61
|
|
|
60
62
|
## API Reference
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"execution_count": null,
|
|
11
11
|
"metadata": {},
|
|
12
12
|
"outputs": [],
|
|
13
|
-
"source": "# Install the fundedness package
|
|
13
|
+
"source": "# Install the fundedness package\n!pip install fundedness -q"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"cell_type": "code",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"execution_count": null,
|
|
11
11
|
"metadata": {},
|
|
12
12
|
"outputs": [],
|
|
13
|
-
"source": "# Install the fundedness package
|
|
13
|
+
"source": "# Install the fundedness package\n!pip install fundedness -q"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"cell_type": "code",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"execution_count": null,
|
|
11
11
|
"metadata": {},
|
|
12
12
|
"outputs": [],
|
|
13
|
-
"source": "# Install the fundedness package
|
|
13
|
+
"source": "# Install the fundedness package\n!pip install fundedness -q"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"cell_type": "code",
|