owlplanner 2025.6.3__tar.gz → 2025.7.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.
Files changed (130) hide show
  1. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/PKG-INFO +6 -4
  2. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/README.md +5 -3
  3. owlplanner-2025.7.1/RELEASE_NOTES.md +12 -0
  4. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/owl.pdf +0 -0
  5. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/owl.tex +184 -80
  6. owlplanner-2025.7.1/examples/case_drawdowncalc-comparison-1.toml +56 -0
  7. owlplanner-2025.7.1/examples/case_jack+jill.toml +57 -0
  8. owlplanner-2025.7.1/examples/case_joe.toml +54 -0
  9. owlplanner-2025.7.1/examples/case_john+sally.toml +53 -0
  10. owlplanner-2025.7.1/examples/case_jon+jane.toml +56 -0
  11. owlplanner-2025.7.1/examples/case_kim+sam-bequest.toml +56 -0
  12. owlplanner-2025.7.1/examples/case_kim+sam-spending.toml +56 -0
  13. owlplanner-2025.7.1/examples/jack+jill.xlsx +0 -0
  14. owlplanner-2025.7.1/examples/joe.xlsx +0 -0
  15. owlplanner-2025.7.1/examples/john+sally.xlsx +0 -0
  16. owlplanner-2025.7.1/examples/jon+jane.xlsx +0 -0
  17. owlplanner-2025.7.1/examples/kim+sam.xlsx +0 -0
  18. owlplanner-2025.7.1/examples/template.xlsx +0 -0
  19. owlplanner-2025.7.1/examples.new/case_drawdowncalc-comparison-1.toml +56 -0
  20. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/case_jack+jill.toml +0 -1
  21. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/case_joe.toml +0 -1
  22. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/case_john+sally.toml +0 -1
  23. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/case_jon+jane.toml +1 -2
  24. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/case_kim+sam-bequest.toml +1 -2
  25. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/case_kim+sam-spending.toml +1 -2
  26. owlplanner-2025.7.1/examples.new/jack+jill.xlsx +0 -0
  27. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/joe.xlsx +0 -0
  28. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/john+sally.xlsx +0 -0
  29. owlplanner-2025.7.1/examples.new/jon+jane.xlsx +0 -0
  30. owlplanner-2025.7.1/examples.new/kim+sam.xlsx +0 -0
  31. {owlplanner-2025.6.3/examples → owlplanner-2025.7.1/examples.new}/template.xlsx +0 -0
  32. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/notebooks/kim+sam.ipynb +0 -1
  33. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/notebooks/template.ipynb +959 -961
  34. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/notebooks/tutorial_1.ipynb +1078 -1080
  35. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/notebooks/tutorial_2.ipynb +0 -1
  36. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/notebooks/tutorial_3.ipynb +0 -1
  37. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/pyproject.toml +1 -1
  38. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/requirements.txt +2 -1
  39. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/config.py +0 -2
  40. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/plan.py +153 -76
  41. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/tax2025.py +42 -9
  42. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/timelists.py +11 -10
  43. owlplanner-2025.7.1/src/owlplanner/version.py +1 -0
  44. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_regressions.py +0 -1
  45. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_repro.py +26 -15
  46. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/About_Owl.py +12 -9
  47. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Create_Case.py +17 -14
  48. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Current_Assets.py +15 -9
  49. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Documentation.py +102 -54
  50. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Optimization_Parameters.py +7 -7
  51. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Output_Files.py +13 -13
  52. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Quick_Start.py +7 -6
  53. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Rates_Selection.py +3 -8
  54. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Settings.py +28 -1
  55. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Wages_and_Contributions.py +33 -14
  56. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/main.py +4 -2
  57. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/owlbridge.py +20 -13
  58. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/sskeys.py +6 -5
  59. owlplanner-2025.7.1/ui/tomlexamples.py +31 -0
  60. owlplanner-2025.6.3/examples/jack+jill.xlsx +0 -0
  61. owlplanner-2025.6.3/examples/jon+jane.xlsx +0 -0
  62. owlplanner-2025.6.3/src/owlplanner/version.py +0 -1
  63. owlplanner-2025.6.3/ui/tomlexamples.py +0 -16
  64. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/.devcontainer/devcontainer.json +0 -0
  65. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/.flake8 +0 -0
  66. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/.gitattributes +0 -0
  67. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/.github/workflows/github-actions-runtests.yml +0 -0
  68. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/.gitignore +0 -0
  69. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/.streamlit/config.toml +0 -0
  70. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/.streamlit/fullconfig.toml +0 -0
  71. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/INSTALL.md +0 -0
  72. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/LICENSE +0 -0
  73. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/USER_GUIDE.md +0 -0
  74. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docker/Dockerfile.build +0 -0
  75. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docker/Dockerfile.run +0 -0
  76. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docker/README.md +0 -0
  77. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docker/buildentrypoint.sh +0 -0
  78. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docker/docker-compose.yml +0 -0
  79. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docker/runentrypoint.sh +0 -0
  80. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/AD-taxDef.png +0 -0
  81. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/AD-taxFree.png +0 -0
  82. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/AD-taxable.png +0 -0
  83. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/Hist_Bequest.png +0 -0
  84. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/Hist_Spending.png +0 -0
  85. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/MC-tutorial2a.png +0 -0
  86. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/MC-tutorial2b.png +0 -0
  87. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/OwlUI.png +0 -0
  88. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/allocations.png +0 -0
  89. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/owl.png +0 -0
  90. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/profile.png +0 -0
  91. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/ratesCorrelations.png +0 -0
  92. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/ratesPlot.png +0 -0
  93. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/savingsPlot.png +0 -0
  94. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/sourcesPlot.png +0 -0
  95. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/spendingPlot.png +0 -0
  96. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/taxIncomePlot.png +0 -0
  97. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/docs/images/taxesPlot.png +0 -0
  98. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/notebooks/john+sally.ipynb +0 -0
  99. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/owlplanner.cmd +0 -0
  100. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/owlplanner.sh +0 -0
  101. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/pytest.ini +0 -0
  102. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/__init__.py +0 -0
  103. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/abcapi.py +0 -0
  104. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/data/__init__.py +0 -0
  105. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/data/rates.csv +0 -0
  106. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/mylogging.py +0 -0
  107. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/plotting/__init__.py +0 -0
  108. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/plotting/base.py +0 -0
  109. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/plotting/factory.py +0 -0
  110. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/plotting/matplotlib_backend.py +0 -0
  111. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/plotting/plotly_backend.py +0 -0
  112. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/progress.py +0 -0
  113. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/rates.py +0 -0
  114. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/src/owlplanner/utils.py +0 -0
  115. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_logger.py +0 -0
  116. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_toml_cases.py +0 -0
  117. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_ui_asset_allocation.py +0 -0
  118. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_ui_compare_summaries.py +0 -0
  119. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_ui_sskeys.py +0 -0
  120. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/tests/test_units.py +0 -0
  121. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Asset_Allocation.py +0 -0
  122. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Fixed_Income.py +0 -0
  123. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Graphs.py +0 -0
  124. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Historical_Range.py +0 -0
  125. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Logs.py +0 -0
  126. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Monte_Carlo.py +0 -0
  127. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/README.md +0 -0
  128. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/Worksheets.py +0 -0
  129. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/__init__.py +0 -0
  130. {owlplanner-2025.6.3 → owlplanner-2025.7.1}/ui/progress.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.6.3
3
+ Version: 2025.7.1
4
4
  Summary: Owl: Retirement planner with great wisdom
5
5
  Project-URL: HomePage, https://github.com/mdlacasse/owl
6
6
  Project-URL: Repository, https://github.com/mdlacasse/owl
@@ -837,6 +837,8 @@ which are all tracked separately for married individuals. Asset transition to th
837
837
  is done according to beneficiary fractions for each type of savings account.
838
838
  Tax status covers married filing jointly and single, depending on the number of individuals reported.
839
839
 
840
+ Maturation rules for Roth contributions and conversions are implemented as constraints
841
+ limiting withdrawal amounts to cover Roth account balances for 5 years after the events.
840
842
  Medicare and IRMAA calculations are performed through a self-consistent loop on cash flow constraints.
841
843
  Future values are simple projections of current values with the assumed inflation rates.
842
844
 
@@ -885,7 +887,7 @@ assets to support, even with no estate being left.
885
887
  - Streamlit Community Cloud [Streamlit](https://streamlit.io)
886
888
  - Contributors: Josh (noimjosh@gmail.com) for Docker image code,
887
889
  Dale Seng (sengsational) for great insights and suggestions,
888
- Robert E. Anderson (NH-RedAnt) for bug fixes and suggestions.
890
+ Robert E. Anderson (NH-RedAnt) for bug fixes and suggestions, Clark Jefcoat (hubcity) for fruitful interactions.
889
891
 
890
892
  ---------------------------------------------------------------------
891
893
 
@@ -893,8 +895,8 @@ Copyright © 2024 - Martin-D. Lacasse
893
895
 
894
896
  Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
895
897
 
896
- Code output has been verified with analytical solutions and other approaches.
897
- Nevertheless, accuracy of results are not guaranteed.
898
+ Code output has been verified with analytical solutions when applicable, and comparative approaches otherwise.
899
+ Nevertheless, accuracy of results is not guaranteed.
898
900
 
899
901
  --------------------------------------------------------
900
902
 
@@ -131,6 +131,8 @@ which are all tracked separately for married individuals. Asset transition to th
131
131
  is done according to beneficiary fractions for each type of savings account.
132
132
  Tax status covers married filing jointly and single, depending on the number of individuals reported.
133
133
 
134
+ Maturation rules for Roth contributions and conversions are implemented as constraints
135
+ limiting withdrawal amounts to cover Roth account balances for 5 years after the events.
134
136
  Medicare and IRMAA calculations are performed through a self-consistent loop on cash flow constraints.
135
137
  Future values are simple projections of current values with the assumed inflation rates.
136
138
 
@@ -179,7 +181,7 @@ assets to support, even with no estate being left.
179
181
  - Streamlit Community Cloud [Streamlit](https://streamlit.io)
180
182
  - Contributors: Josh (noimjosh@gmail.com) for Docker image code,
181
183
  Dale Seng (sengsational) for great insights and suggestions,
182
- Robert E. Anderson (NH-RedAnt) for bug fixes and suggestions.
184
+ Robert E. Anderson (NH-RedAnt) for bug fixes and suggestions, Clark Jefcoat (hubcity) for fruitful interactions.
183
185
 
184
186
  ---------------------------------------------------------------------
185
187
 
@@ -187,8 +189,8 @@ Copyright © 2024 - Martin-D. Lacasse
187
189
 
188
190
  Disclaimers: This code is for educatonal purposes only and does not constitute financial advice.
189
191
 
190
- Code output has been verified with analytical solutions and other approaches.
191
- Nevertheless, accuracy of results are not guaranteed.
192
+ Code output has been verified with analytical solutions when applicable, and comparative approaches otherwise.
193
+ Nevertheless, accuracy of results is not guaranteed.
192
194
 
193
195
  --------------------------------------------------------
194
196
 
@@ -0,0 +1,12 @@
1
+ ### Version 2025.07.01
2
+
3
+ - Added Net Investment Income Tax calculations in self-consistent loop
4
+ - Added capability to load example Excel sheet directly in UI
5
+ - Added constraint for 5-year maturation rule on Roth conversions
6
+ - Added capability to read last 5 years in Wages and Contributions file
7
+ - Added option in UI to turn off sticky header
8
+ - Added RELEASE_NOTES file
9
+ - Improved color scheme in header gradient for visibility
10
+ - Removed long-term capital tax rate from options. Rate is now automatically calculated in self-consistent loop.
11
+ - Added option to use HiGHS library through PuLP for speed comparison with DrawdownCalc. Using HiGHS diretly is by far the fastest option.
12
+ - Add option for menu position thanks to Streamlit 1.46.
@@ -1,5 +1,6 @@
1
1
  \documentclass{report}[fleqn,12pt]
2
2
  \usepackage{amsmath}
3
+ \usepackage{amsfonts}
3
4
  \usepackage{enumitem}
4
5
  \usepackage{bm}
5
6
  \usepackage{blindtext}
@@ -32,7 +33,7 @@
32
33
  \begin{document}
33
34
  \title{Formulation of the optimization model in Owl}
34
35
  \author{Martin-D. Lacasse}
35
- \date{March 14, 2025}
36
+ \date{June 13, 2025}
36
37
  \maketitle
37
38
  \thispagestyle{fancy}
38
39
  \fancyfoot[R]{\copyright\ 2024 - Martin-D. Lacasse}
@@ -115,12 +116,12 @@ to take only non-negative values ($\ge 0$ inequality).
115
116
  sometimes be less that the standard exemption $\bar{\sigma}_n$, leading to a
116
117
  negative taxable income if the inflation-adjusted standard exemption is simply subtracted
117
118
  from the gross taxable income $G_n$.
118
- \item [$f_{t n}$]
119
+ \item [$f_{tn}$]
119
120
  Fraction of tax bracket $t$ filled, so that taxable ordinary income $G_n$ can be expressed as
120
121
  \begin{eqnarray}
121
122
  \label{Eq:Tx1}
122
- G_n = \sum_t f_{t n}\bar{\Delta}_{t n},\\
123
- 0 \leq f_{t n} \leq 1.
123
+ G_n = \sum_t f_{tn}\bar{\Delta}_{tn},\\
124
+ 0 \leq f_{tn} \leq 1.
124
125
  \end{eqnarray}
125
126
  A definition of $\Delta$ can be found in the section describing the parameters below.
126
127
  \item [$g_n$]
@@ -135,11 +136,12 @@ to take only non-negative values ($\ge 0$ inequality).
135
136
  \item [$x_{in}$]
136
137
  Roth conversion performed by individual $i$ during year $n$.
137
138
  These events are taxable as ordinary income.
139
+ \item [$z_{xn}$]
140
+ Variables denoted $z$ are reserved for binary variables.
138
141
  \end{description}
139
142
 
140
143
  \section{Parameters}
141
- For more easily distinguishing parameters from variables, all parameters will be expressed either in Greek letters
142
- or using caligraphic fonts.
144
+ For more easily distinguishing parameters from variables, all parameters will be expressed either in Greek letters.
143
145
  Parameter values are either set by the user, historical data, or by the tax code.
144
146
  \begin{description}[leftmargin=4em,style=multiline]
145
147
  \item [$\beta_{ij}$]
@@ -155,9 +157,8 @@ Parameter values are either set by the user, historical data, or by the tax code
155
157
  \gamma_n = \prod_{n' = 0}^{n-1} (1 + \tau_{3n'}),
156
158
  \end{equation}
157
159
  with $\gamma_0 := 1$, and where $n'$ is a dummy index.
158
- As the time span of interest goes from the first year to the beginning
159
- of the year following the last year,
160
- variable $\gamma_n$ will have $N_n + 1$ elements.
160
+ As the time span of interest goes from the beginning of the first year to the beginning
161
+ of the year following the last year, variable $\gamma_n$ will have $N_n + 1$ elements.
161
162
  Parameters indexed for inflation will be indicated by a bar on top as in $\bar{\sigma}_n$.
162
163
  \item [$\sigma_n$]
163
164
  Standard deduction. It can be adjusted for inflation as follows
@@ -177,7 +178,7 @@ Parameter values are either set by the user, historical data, or by the tax code
177
178
  The {\em smile} can be implemented using a cosine superimposed over a gentle linear increase
178
179
  such as in
179
180
  \begin{equation}
180
- \xi_n = 1 + a_1*cos(2n\pi/(N_n-1)) + a_2n/(N_n-1),
181
+ \xi_n = 1 + a_1*\cos(2n\pi/(N_n-1)) + a_2n/(N_n-1),
181
182
  \end{equation}
182
183
  and then normalized by factor $N_n/(\sum_n \xi_n )$ to be sum-neutral with respect to a flat profile.
183
184
  Values of $a_1 = 15\%$ and $a_2=12\%$ provide curves that are similar to realistic
@@ -229,9 +230,10 @@ Parameter values are either set by the user, historical data, or by the tax code
229
230
  Then, intermediate values are interpolated either using
230
231
  a linear relation,
231
232
  \begin{equation}
232
- \alpha_{ijkn} = a + \frac{n}{N_n - 1} (b - a),
233
+ \alpha_{ijkn} = a + \frac{n}{N - 1} (b - a),
233
234
  \end{equation}
234
- or an s-curve as in
235
+ where $N$ is either $n_d$ or $N_n$,
236
+ or using an s-curve as in
235
237
  \begin{equation}
236
238
  \alpha_{ijkn} = a + \frac{(b - a)}{2}
237
239
  (\tanh((n-n_1)/n_2) + 1),
@@ -252,8 +254,8 @@ or an s-curve as in
252
254
  \begin{eqnarray}
253
255
  k_{11} &=& \frac{1}{2}(1 + \tanh(n_1/n_2)) \nonumber \\
254
256
  k_{12} &=& \frac{1}{2}(1 - \tanh(n_1/n_2)) \nonumber \\
255
- k_{21} &=& \frac{1}{2}(1 - \tanh((N_n-1-n_1)/n_2)) \nonumber \\
256
- k_{22} &=& \frac{1}{2}(1 + \tanh((N_n-1-n_1)/n_2)).
257
+ k_{21} &=& \frac{1}{2}(1 - \tanh((N-1-n_1)/n_2)) \nonumber \\
258
+ k_{22} &=& \frac{1}{2}(1 + \tanh((N-1-n_1)/n_2)).
257
259
  \end{eqnarray}
258
260
  These interpolation functions allow the allocation ratios to gradually change
259
261
  or {\em glide} during retirement. Fig.~(\ref{Fig:allocations}) provides an example
@@ -318,7 +320,7 @@ or an s-curve as in
318
320
  Sum of wages obtained by individual $i$ during year $n$.
319
321
  Do not confuse wages $\omega$ with withdrawals $w$.
320
322
  \item [$\mu$]
321
- Dividend return rate for equities in taxable accounts. Average is little above 2\% for S\&P 500.
323
+ Dividend return rate for equities in taxable accounts. Average is little below 2\% for S\&P 500.
322
324
  \item [$\nu$]
323
325
  Heirs income tax rate to be applied on the tax-deferred portion of the estate. This is not an estate tax
324
326
  but rather the federal income marginal tax rate for the heirs.
@@ -343,80 +345,49 @@ or an s-curve as in
343
345
  when $\phi_j \neq 1, \forall j$, it is recommended that $\eta$ be set to $i_d$ so that
344
346
  all surplus get deposited to $i_d$'s accounts,
345
347
  thus avoid loopholes when optimizing for the final bequest.
346
- \item [$\mathcal{M}_n$]
347
- Costs of Medicare and its Income Related Monthly Adjusted Adjustment (IRMAA).
348
- As this additional adjustment
349
- is a step function, it would have to be computed using binary variables and mixed-integer linear
350
- programming. In the current tax code, this adjustment
351
- depends on the modified adjusted gross income (MAGI) from 2 years earlier. For the
352
- MAGI, we simply use $G_{n-2} + e_{n-2}$ (i.e., gross taxable income
353
- plus standard deduction (exemption) from 2 years ago) and ignore the additional IRS
354
- rules around tax-free interests which are insignificant in most cases. If the plan
355
- has individuals above 63 years old, values of MAGI for previous years are requested from the user.
356
348
 
357
- There are $q=5$ levels
358
- of step adjustments adjusted for inflation,
359
- $\bar{L}_{qn} = L_q\gamma_n$ and each of them introduces
360
- an annual additional Medicare
361
- cost of $\bar{C}_{qn} = C_q\gamma_n$, also adjusted for inflation.
362
- One could use binary variables $z_{inq}$ and the following {\em big M} constraint
363
- \begin{equation}
364
- e_{n-2} -\bar{L}_{qn} \le z_{inq} M - G_{n-2}
365
- \le M - \bar{L}_{qn} + e_{n-2},
366
- \end{equation}
367
- so that the IRMAA adjustments can be computed as
368
- \begin{equation}
369
- \label{Eq:IRMAA}
370
- \mathcal{M}_n = \sum_{iq} z_{iqn} \bar{C}_{qn}.
371
- \end{equation}
372
- If the plan needs data from 1 or 2 years ago as Medicare has already started or will in the next years,
373
- values for years before current year need to be provided.
374
-
375
- While this approach has been implemented and tested, the robustness of the {\em big M} approach
376
- is not guaranteed. Moreover, the introduction of a large number ($5\times N_i\times N_m$,
377
- where $N_m$ is the number of years eligible for Medicare) of integer variables with
378
- disjonctive constraints makes the
379
- convergence to a solution very slow in most situations.
380
-
381
- A more practical approach is to implement a self-consistent loop that
382
- optimizes the spending or bequest, and updates the Medicare/IRMAA premiums accordingly.
383
- Therefore, the value will be computed from the MAGI
384
- as $\mathcal{M}_n^\ell(G_{n-2} + e_{n-2})$, where the value is at iteration $\ell$.
385
- After only a few iterations, the solution is converging to within a dollar over the
386
- sum of all variables in the plan. This approach, however, does not guarantee convergence
387
- as there can be cases where the premiums affect the solution which can oscillate between
388
- two solutions, but these cases are detected and a slight change in parameters
389
- solves this issue. As these premiums are introduced as parameters in the constraints,
390
- there is no direct optimization being performed on Medicare costs.
391
349
  \end{description}
392
350
 
393
351
  \section{Intermediate variables}
394
352
  We use intermediate variables for conciseness or clarity,
395
353
  but they are ultimately replaced in the final formulation.
396
- All intermediate variables are in uppercase letters.
354
+ All intermediate variables are in uppercase letters or in double-struck fonts.
397
355
  \begin{description}[leftmargin=4em,style=multiline]
398
- \item [$G_n$]
399
- Taxable ordinary income in year $n$. Sum of wages, pension, social security benefits, all withdrawals
400
- from tax-deferred accounts, including Roth conversions, and gains from securities
401
- (i.e., all gains except those from the $(k=0)$ equities)
402
- in the ($j=0$) taxable account, including contributions $\kappa$, minus the standard deduction,
356
+ \item [$W_n$]
357
+ Taxable wages and withdrawals in year $n$. Sum of wages, pensions, social security benefits,
358
+ all withdrawals from tax-deferred accounts, including Roth conversions:
403
359
  \begin{eqnarray}
404
- \label{Eq:Tx2}
405
- G_n &=&
406
- \sum_{i} [\omega_{in} + .85\bar\zeta_{in} + \pi_{in}]
407
- - e_n +
408
- \nonumber \\
409
- && \sum_{i} [w_{i1n} + x_{in}] +
410
- \nonumber \\
411
- && \sum_{ik}
412
- [(1-\delta(k, 0))(b_{i0n} - w_{i0n} + d_{in} + .5\kappa_{i0n})\alpha_{i0kn}\tau_{kn}]
360
+ \label{Eq:Wn}
361
+ W_n &=&
362
+ \sum_{i} [\omega_{in} + .85\bar\zeta_{in} + \pi_{in} + w_{i1n} + x_{in}].
413
363
  \end{eqnarray}
414
364
  Social security is indexed for inflation and is assumed to be taxed at 85\%.
415
365
  Pensions can optionally be indexed for inflation.
416
- We use a discrete Kronecker $\delta$ function for selecting gains from non-equity assets in
417
- taxable accounts. These gains are all taxed as ordinary income. Here, we assumed that
366
+ All these are taxed as ordinary income.
367
+
368
+ \item [$I_n$]
369
+ Income from interests obtained in year $n$. This only involves gains from securities
370
+ in the taxable account:
371
+ \begin{eqnarray}
372
+ \label{Eq:In}
373
+ I_n &=& \sum_{ik}
374
+ [(1-\delta(k, 0))(b_{i0n} - w_{i0n} + d_{in} + .5\kappa_{i0n})\alpha_{i0kn}\tau_{kn}].
375
+ \end{eqnarray}
376
+ Here, we assumed that
418
377
  withdrawals and deposits in the taxable account are taking place at the beginning of the year, while
419
378
  contributions, if any, are taking place in mid-year.
379
+ We use a discrete Kronecker $\delta$ function for selecting gains from non-equity assets in
380
+ taxable accounts.
381
+ All these are also taxed as ordinary income.
382
+
383
+ \item [$G_n$]
384
+ Taxable ordinary income in year $n$. Sum of wages, pension, social security benefits,
385
+ all withdrawals from tax-deferred accounts, including Roth conversions and gains from securities,
386
+ minus the standard deduction,
387
+ \begin{eqnarray}
388
+ \label{Eq:Tx2}
389
+ G_n &=& W_n + I_n - e_n.
390
+ \end{eqnarray}
420
391
 
421
392
  \item [$Q_n$]
422
393
  Qualified dividends and long-term capital gains obtained in year $n$.
@@ -444,6 +415,17 @@ All intermediate variables are in uppercase letters.
444
415
  the taxable savings account is being depleted slowly. An implementation keeping track
445
416
  of stock purchases and sales is beyond the goal of providing a guide for retirement decisions.
446
417
 
418
+ \item [$\mathbb{G}_n$]
419
+ For Owl, we define the Modified Adjusted Gross Income (MAGI) as
420
+ \begin{eqnarray}
421
+ \label{Eq:MAGI}
422
+ \mathbb{G}_n &=& G_n + Q_n + e_n, \nonumber \\
423
+ &=& W_n + I_n + Q_n + e_n.
424
+ \end{eqnarray}
425
+ It includes all wages, social security benefits, pensions,
426
+ withdrawals, Roth conversions, interests, and dividends, plus
427
+ the standard deduction for that year.
428
+
447
429
  \item [$P_n$]
448
430
  Amount of 10\% early withdrawal penalty in year $n$,
449
431
  \begin{equation}
@@ -493,6 +475,93 @@ All intermediate variables are in uppercase letters.
493
475
 
494
476
  \end{description}
495
477
 
478
+ \section{Derived variables}
479
+ These variables could be additional variables in the original formulation. However, their
480
+ calculations involve the use of binary variables that can considerably impact the solution
481
+ performance. For that reason, these variables are currently computed after the optimization
482
+ step and reintroduced as constraints in the next, self-consistent solution.
483
+ We will use caligraphic letters to represent these variables.
484
+ \begin{description}[leftmargin=4em,style=multiline]
485
+ \item [$\mathcal{M}_n$]
486
+ Costs of Medicare and its Income Related Monthly Adjusted Adjustment (IRMAA).
487
+ As this additional adjustment
488
+ is a step function, it would have to be computed using binary variables and mixed-integer linear
489
+ programming. In the current tax code, this adjustment
490
+ depends on the modified adjusted gross income (MAGI) from 2 years earlier. The
491
+ MAGI $\mathbb{G}_{n-2}$ from 2 years ago as defined below. It ignores the additional IRS
492
+ rules around tax-free interests which are insignificant in most cases. If the plan
493
+ has individuals above 63 years old, values of MAGI for previous years are requested from the user.
494
+
495
+ There are $q=5$ levels
496
+ of step adjustments adjusted for inflation,
497
+ $\bar{L}_{qn} = L_q\gamma_n$ and each of them introduces
498
+ an annual additional Medicare
499
+ cost of $\bar{C}_{qn} = C_q\gamma_n$, also adjusted for inflation.
500
+ One could use binary variables $z_{inq}$ and the following {\em big M} constraint
501
+ \begin{equation}
502
+ e_{n-2} -\bar{L}_{qn} \le z_{inq} M - \mathbb{G}_{n-2}
503
+ \le M - \bar{L}_{qn} + e_{n-2},
504
+ \end{equation}
505
+ so that the IRMAA adjustments can be computed as
506
+ \begin{equation}
507
+ \label{Eq:IRMAA}
508
+ \mathcal{M}_n = \sum_{iq} z_{iqn} \bar{C}_{qn}.
509
+ \end{equation}
510
+ If the plan needs data from 1 or 2 years ago as Medicare has already started or will in the next years,
511
+ values for years before current year need to be provided.
512
+
513
+ While this approach has been implemented and tested, the robustness of the {\em big M} approach
514
+ is not guaranteed. Moreover, the introduction of a large number ($5\times N_i\times N_m$,
515
+ where $N_m$ is the number of years eligible for Medicare) of integer variables with
516
+ disjonctive constraints makes the
517
+ convergence to a solution very slow in most situations.
518
+
519
+ A more practical approach is to implement a self-consistent loop that
520
+ optimizes the spending or bequest, and updates the Medicare/IRMAA premiums accordingly.
521
+ Therefore, the value will be computed from the MAGI
522
+ as $\mathcal{M}_n^\ell(\mathbb{G}_{n-2})$, where the value is at iteration $\ell$.
523
+ After only a few iterations, the solution is converging to within a dollar over the
524
+ sum of all variables in the plan. This approach, however, does not guarantee convergence
525
+ as there can be cases where the premiums affect the solution which can oscillate between
526
+ two solutions, but these cases are detected and a slight change in parameters
527
+ solves this issue. As these premiums are introduced as parameters in the constraints,
528
+ there is no direct optimization being performed on Medicare costs.
529
+
530
+ \item [$\mathcal{J}_n$]
531
+ This variable represents the Net Investment Income Tax (NIIT) that was introduced in 2013.
532
+ This additional tax is meant to finance the Affordable Care Act though a 3.8\% tax on
533
+ income that does not involve direct work.
534
+ These include dividends, interests, rents, and the like.
535
+ This tax is applied on individuals having a MAGI ($\mathbb{G}_n$)
536
+ more than $\mathbb{G}_{\max}$ = \$200k (single)
537
+ or $\mathbb{G}_{\max}$ = \$250k for couples (married filing jointly).
538
+ These threshold values are not adjusted for inflation.
539
+ This additional tax can be implemented using binary variables $z_n$ and
540
+ the big $M$ method through the following constraints:
541
+ \begin{eqnarray}
542
+ \label{Eq:bigMAGI}
543
+ \mathbb{G}_n - \mathbb{G}_{\max} &\leq& M z_n, \nonumber\\
544
+ \mathbb{G}_n - \mathbb{G}_{\max} &>& -M (1 - z_n),
545
+ \end{eqnarray}
546
+ where $M$ is a number larger than any possible MAGI.
547
+ The twist of this rule is that the taxable amount is the smallest between the MAGI excedent and
548
+ the investment income per se. Therefore, another decision needs to be made on the object
549
+ on which the tax should be applied:
550
+ \begin{eqnarray}
551
+ \mathcal{J}_n &=& 0.038 * z_n \min(I_n + Q_n, \mathbb{G}_n - \mathbb{G}_{\max}).
552
+ \end{eqnarray}
553
+ This requires the introduction of another binary variable, and $2N_n$ more constraints as
554
+ in Eq.~\ref{Eq:bigMAGI} to linearize the $\min$ function. Alternatively, $\mathcal{J}^\ell_n$
555
+ can also be computed self-consistently as its (optimization) impact will be small
556
+ in most cases, unless near the MAGI threshold.
557
+ The NIIT amount is relevant for those with large taxable investments
558
+ or rent income, both has little to no room for optimization.
559
+ Current code does not account for rent income, but that could be easily added in the
560
+ future through an additional column in the {\em Wages and Contributions} input file.
561
+
562
+ \end{description}
563
+
564
+
496
565
  \chapter{Formulation with imposed asset allocation ratios}
497
566
  We first present the case where the sums of assets in each savings accounts $b_{ijn}$ are known
498
567
  over which we assume a prescribed asset allocation ratios.
@@ -709,6 +778,41 @@ add the market returns to the savings balances.
709
778
  x_{in} \le \min(b_{i1n}, x_{max}).
710
779
  \end{equation}
711
780
 
781
+ Gains on Roth contributions are governed by a 5-year maturation rule for withdrawals.
782
+ Roth conversions are also governed by a 5-year maturation rule for withdrawals covering
783
+ both conversions and gains.
784
+ Some Roth contributions can sometimes be done through a backdoor
785
+ by converting from an IRA, therefore requiring a hold on both the contributions and their gains.
786
+ To stay on the safe side, we impose that
787
+ withdrawals need to be smaller than the balance minus the sum of all contributions
788
+ and conversions that happened over the last 5 years.
789
+ For that purpose, the {\em Wages and Contributions}
790
+ file which stores $\omega_{in}, \kappa_{ijn}, \ldots$, goes back 5 years, and the Roth
791
+ conversions in that year range are interpreted as having actually happened. We use
792
+ the same arrays and store previous conversions and contributions at the end of the array so that
793
+ they can be retrieved with negative indices in Python. Mathematically, we want that
794
+ \begin{equation}
795
+ w_{i2n} \le b_{i2n} -
796
+ \sum_{n'=n-5}^{n-1} [ \kappa_{i2n'} + x_{in'}] \prod_{n''=n'}^{n-1}\mathcal{T}^1_{in''}
797
+ \end{equation}
798
+ where the compounded gain is computed from
799
+ \begin{equation}
800
+ \mathcal{T}^1_{in} = 1 + \sum_k \alpha_{i2kn}\tau_{kn}.
801
+ \end{equation}
802
+ However, conversions are sometimes a decision variable $x_{in}$
803
+ and sometimes a parameter $X_{in}$, depending on the sign of $n$.
804
+ This leads to
805
+ \begin{eqnarray}
806
+ b_{i2n} - w_{i2n} -
807
+ \sum_{n'=\max(n-5, 0)}^{n-1} x_{in'}
808
+ \prod_{n''=n'}^{n-1}\mathcal{T}^1_{in''}
809
+ &\ge&
810
+ \sum_{n'=n-5}^{n-1} \kappa_{i2n'}
811
+ \prod_{n''=n'}^{n-1} \mathcal{T}^1_{in''} \nonumber \\
812
+ && + \sum_{n'=n-5}^{\min(-1, n-1)} X_{in'}
813
+ \prod_{n''=n'}^{\min(-1, n-1)}\mathcal{T}^1_{in''}.
814
+ \end{eqnarray}
815
+
712
816
  \paragraph*{Net spending}
713
817
  For calculating the net spending $g_n$, we consider the cash flow of all withdrawals,
714
818
  wages, social security and pension benefits, and big-ticket items.
@@ -716,7 +820,7 @@ add the market returns to the savings balances.
716
820
  \begin{eqnarray}
717
821
  g_n = \sum_i [\omega_{in} + \bar{\zeta}_{in} + \pi_{in} ]
718
822
  + \sum_{ij} w_{ijn} + \sum_i \Lambda^\pm_{in} - s_{n}
719
- - P_n - T_n - U_n - \mathcal{M}^\ell_n.
823
+ - P_n - T_n - U_n - \mathcal{M}^\ell_n - \mathcal{J}^\ell_n.
720
824
  \end{eqnarray}
721
825
  When both spouses are alive, surplus $s_n$ gets deposited in the taxable accounts
722
826
  according to variable $\eta$ as described in Eq.~(\ref{Eq:eta}),
@@ -736,13 +840,13 @@ add the market returns to the savings balances.
736
840
  + w_{i0n}\max(0, \tau_{0n-1})\right]
737
841
  &=& \sum_i [\omega_{in} + \bar{\zeta}_{in} + \pi_{in} ] \nonumber\\
738
842
  && + \sum_i [\Lambda^\pm_{in} - .5\psi\alpha_{i00n}\mu\kappa_{i0n}] \nonumber\\
739
- && - \mathcal{M}_n^\ell.
843
+ && - \mathcal{M}_n^\ell - \mathcal{J}^\ell_n.
740
844
  \end{eqnarray}
741
845
  Notice that we do not consider market losses as we use $\max(0, \tau)$, and that
742
846
  rates from only the previous year are used. Tax-loss
743
847
  harvesting is beyond the scope of this model, as is the tracking of stocks
744
848
  purchased over the years.
745
- For clarity, we did not express $\mathcal{M}_n^\ell(G_n+e_n)$ in terms of the modified
849
+ For clarity, we did not express $\mathcal{M}_n^\ell(G_n+Q_n+e_n)$ in terms of the modified
746
850
  adjusted gross income (MAGI), but this term is there to indicate that there is a self-consistent
747
851
  loop solving for it and that we are at iteration $\ell$.
748
852
 
@@ -1050,7 +1154,7 @@ we add $N_n$ more rows to $A_ey = v$ as
1050
1154
  where $v$ is
1051
1155
  \begin{equation}
1052
1156
  v[J_2(n)] = \sum_i [\omega_{in} + \bar\zeta_{in} + \pi_{in}
1053
- + \Lambda^\pm_{in} - .5\psi\mu\alpha_{i00n}\kappa_{i0n}] - \mathcal{M}_n^\ell.
1157
+ + \Lambda^\pm_{in} - .5\psi\mu\alpha_{i00n}\kappa_{i0n}] - \mathcal{M}_n^\ell - \mathcal{J}^\ell_n.
1054
1158
  \end{equation}
1055
1159
 
1056
1160
  The condition of having a predictable net spending expressed as an
@@ -0,0 +1,56 @@
1
+ "Plan Name" = "drawdowncalc-comparison-1"
2
+ Description = "This is a case involving a single individual. Case is used for comparing with @hugcity's DrawdownCalc. For max bequest, it should yield $90,792, while an $80k net spending should leave $673,156 as a bequest."
3
+
4
+ ["Basic Info"]
5
+ Status = "single"
6
+ Names = [ "Charles",]
7
+ "Birth year" = [ 1966,]
8
+ "Life expectancy" = [ 89,]
9
+ "Start date" = "2025-01-01"
10
+
11
+ [Assets]
12
+ "taxable savings balances" = [ 250.0,]
13
+ "tax-deferred savings balances" = [ 600.0,]
14
+ "tax-free savings balances" = [ 600.0,]
15
+
16
+ ["Wages and Contributions"]
17
+ "Contributions file name" = "edited values"
18
+
19
+ ["Fixed Income"]
20
+ "Pension amounts" = [ 0.0,]
21
+ "Pension ages" = [ 65,]
22
+ "Pension indexed" = [ true,]
23
+ "Social security amounts" = [ 36.0,]
24
+ "Social security ages" = [ 70,]
25
+
26
+ ["Rates Selection"]
27
+ "Heirs rate on tax-deferred estate" = 0.0
28
+ "Dividend rate" = 0.0
29
+ "TCJA expiration year" = 2099
30
+ Method = "user"
31
+ Values = [ 6.5, 0.0, 0.0, 2.8,]
32
+ From = 1928
33
+ To = 2024
34
+
35
+ ["Asset Allocation"]
36
+ "Interpolation method" = "s-curve"
37
+ "Interpolation center" = 15.0
38
+ "Interpolation width" = 5.0
39
+ Type = "individual"
40
+ generic = [ [ [ 100, 0, 0, 0,], [ 100, 0, 0, 0,],],]
41
+
42
+ ["Optimization Parameters"]
43
+ "Spending profile" = "flat"
44
+ "Surviving spouse spending percent" = 60
45
+ Objective = "maxBequest"
46
+
47
+ ["Solver Options"]
48
+ netSpending = 80.0
49
+ maxRothConversion = 50
50
+ startRothConversions = 2025
51
+ withMedicare = false
52
+ solver = "HiGHS"
53
+ spendingSlack = 0
54
+
55
+ [Results]
56
+ "Default plots" = "today"
@@ -0,0 +1,57 @@
1
+ "Plan Name" = "jack+jill"
2
+ Description = "This example aims to demonstrate some of Owl's capabilities. Jack and Jill are a married couple a few years from retirement. A wages and contributions file called 'jack+jill.xlsx' is associated with this case. This case uses the historical rate sequence of 1969 as a test case for guiding spending amounts from a near worst-case historical scenario. This case also demonstrates that the optimal strategy for Roth conversions does not necessarily involve surfing a tax bracket. \nA good exercise for learning Owl's capabilities is to duplicate this case and compare two scenarios: one with optimized Roth conversions and one without. Another possible exercise could involve comparing a historical retirement in 1969 vs. one taken in 1966. Or anything else you can think of..."
3
+
4
+ ["Basic Info"]
5
+ Status = "married"
6
+ Names = [ "Jack", "Jill",]
7
+ "Birth year" = [ 1962, 1965,]
8
+ "Life expectancy" = [ 89, 92,]
9
+ "Start date" = "01-01"
10
+
11
+ [Assets]
12
+ "taxable savings balances" = [ 120.5, 60.2,]
13
+ "tax-deferred savings balances" = [ 600.2, 150.0,]
14
+ "tax-free savings balances" = [ 280.6, 260.8,]
15
+ "Beneficiary fractions" = [ 1, 1, 1,]
16
+ "Spousal surplus deposit fraction" = 0.0
17
+
18
+ ["Wages and Contributions"]
19
+ "Contributions file name" = "jack+jill.xlsx"
20
+
21
+ ["Fixed Income"]
22
+ "Pension amounts" = [ 0.0, 10.5,]
23
+ "Pension ages" = [ 65, 65,]
24
+ "Pension indexed" = [ false, false,]
25
+ "Social security amounts" = [ 28.4, 19.7,]
26
+ "Social security ages" = [ 70, 62,]
27
+
28
+ ["Rates Selection"]
29
+ "Heirs rate on tax-deferred estate" = 30.0
30
+ "Dividend rate" = 1.8
31
+ "TCJA expiration year" = 2026
32
+ Method = "historical"
33
+ From = 1969
34
+ To = 2002
35
+
36
+ ["Asset Allocation"]
37
+ "Interpolation method" = "s-curve"
38
+ "Interpolation center" = 15
39
+ "Interpolation width" = 5
40
+ Type = "individual"
41
+ generic = [ [ [ 60, 40, 0, 0,], [ 70, 30, 0, 0,],], [ [ 60, 40, 0, 0,], [ 80, 0, 10, 10,],],]
42
+
43
+ ["Optimization Parameters"]
44
+ "Spending profile" = "smile"
45
+ "Smile dip" = 15
46
+ "Smile increase" = 12
47
+ "Smile delay" = 0
48
+ "Surviving spouse spending percent" = 60
49
+ Objective = "maxSpending"
50
+
51
+ ["Solver Options"]
52
+ maxRothConversion = 100
53
+ bequest = 500
54
+ noRothConversions = "Jill"
55
+
56
+ [Results]
57
+ "Default plots" = "today"
@@ -0,0 +1,54 @@
1
+ "Plan Name" = "joe"
2
+ Description = "This is an example of a case involving a single individual. Joe is single and will retire in a few years. His wages and contributions are contained in the 'joe.xlsx' spreadsheet."
3
+
4
+ ["Basic Info"]
5
+ Status = "single"
6
+ Names = [ "Joe",]
7
+ "Birth year" = [ 1966,]
8
+ "Life expectancy" = [ 89,]
9
+ "Start date" = "01-01"
10
+
11
+ [Assets]
12
+ "taxable savings balances" = [ 338.5,]
13
+ "tax-deferred savings balances" = [ 650.2,]
14
+ "tax-free savings balances" = [ 60.6,]
15
+
16
+ ["Wages and Contributions"]
17
+ "Contributions file name" = "joe.xlsx"
18
+
19
+ ["Fixed Income"]
20
+ "Pension amounts" = [ 18.0,]
21
+ "Pension ages" = [ 65,]
22
+ "Pension indexed" = [ true,]
23
+ "Social security amounts" = [ 28.4,]
24
+ "Social security ages" = [ 67,]
25
+
26
+ ["Rates Selection"]
27
+ "Heirs rate on tax-deferred estate" = 30.0
28
+ "Dividend rate" = 1.8
29
+ "TCJA expiration year" = 2026
30
+ Method = "historical average"
31
+ From = 1969
32
+ To = 2002
33
+
34
+ ["Asset Allocation"]
35
+ "Interpolation method" = "s-curve"
36
+ "Interpolation center" = 15
37
+ "Interpolation width" = 5
38
+ Type = "individual"
39
+ generic = [ [ [ 60, 40, 0, 0,], [ 70, 30, 0, 0,],],]
40
+
41
+ ["Optimization Parameters"]
42
+ "Spending profile" = "smile"
43
+ "Smile dip" = 15
44
+ "Smile increase" = 12
45
+ "Smile delay" = 0
46
+ "Surviving spouse spending percent" = 60
47
+ Objective = "maxSpending"
48
+
49
+ ["Solver Options"]
50
+ maxRothConversion = 50
51
+ bequest = 300
52
+
53
+ [Results]
54
+ "Default plots" = "nominal"