owlplanner 2025.3.27__tar.gz → 2025.4.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.
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/PKG-INFO +1 -1
- owlplanner-2025.4.1/Papers/bv_cvxbook.pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/owl.pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/owl.tex +1 -1
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/pyproject.toml +1 -1
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/requirements.txt +1 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/config.py +6 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/plan.py +21 -8
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/tax2025.py +1 -1
- owlplanner-2025.4.1/src/owlplanner/version.py +1 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/About_Owl.py +5 -6
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Current_Assets.py +2 -1
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Documentation.py +16 -2
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Optimization_Parameters.py +7 -1
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/requirements.txt +2 -1
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/sskeys.py +6 -5
- owlplanner-2025.3.27/src/owlplanner/version.py +0 -1
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/.devcontainer/devcontainer.json +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/.flake8 +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/.gitattributes +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/.github/workflows/github-actions-runtests.yml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/.gitignore +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/INSTALL.md +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/LICENSE +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/Papers/FE00006821-Class-VI-Injection-Permit--Salient-Features-and-Regulatory-Challenges_Final.pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/Papers/Kou-OptionPricingDouble-2004.pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/Papers/Multi-Period Mean Expected-Shortfall Strategies Cut Your Losses and Ride Your Gains .pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/Papers/Optimal Asset Allocation for Retirement Saving Deterministic Vs. Time Consistent Adaptive Strategies.pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/Papers/Rule-based_strategies_for_dynamic_life_cycle_inves.pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/Papers/s10436-006-0062-y.pdf +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/README.md +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/USER_GUIDE.md +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docker/Dockerfile +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docker/README.md +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docker/docker-compose.yml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docker/fastentrypoint.sh +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/AD-taxDef.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/AD-taxFree.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/AD-taxable.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/Hist_Bequest.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/Hist_Spending.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/MC-tutorial2a.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/MC-tutorial2b.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/OwlUI.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/allocations.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/owl.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/profile.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/ratesCorrelations.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/ratesPlot.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/savingsPlot.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/sourcesPlot.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/spendingPlot.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/taxIncomePlot.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/docs/images/taxesPlot.png +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/case_jack+jill.toml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/case_joe.toml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/case_john+sally.toml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/case_jon+jane.toml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/case_kim+sam-bequest.toml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/case_kim+sam-spending.toml +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/jack+jill.xlsx +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/joe.xlsx +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/john+sally.xlsx +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/jon+jane.xlsx +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/examples/template.xlsx +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/notebooks/john+sally.ipynb +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/notebooks/kim+sam.ipynb +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/notebooks/template.ipynb +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/notebooks/tutorial_1.ipynb +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/notebooks/tutorial_2.ipynb +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/notebooks/tutorial_3.ipynb +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/owlplanner.cmd +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/owlplanner.sh +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/__init__.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/abcapi.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/data/__init__.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/data/rates.csv +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/logging.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/progress.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/rates.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/timelists.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/src/owlplanner/utils.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/tests/test_logger.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/tests/test_regressions.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/tests/test_repro.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/tests/test_toml_cases.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/tests/test_units.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ttt.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ttt2.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ttt3.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Asset_Allocation.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Create_Case.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Fixed_Income.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Graphs.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Historical_Range.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Logs.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Monte_Carlo.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Output_Files.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Quick_Start.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/README.md +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Rates_Selection.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Settings.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Wages_And_Contributions.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/Worksheets.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/main+fonts.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/main.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/owlbridge.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/plots.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/progress.py +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/sskeys.py.color +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/style.css +0 -0
- {owlplanner-2025.3.27 → owlplanner-2025.4.1}/ui/tomlexamples.py +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -329,7 +329,7 @@ or an s-curve as in
|
|
|
329
329
|
Income tax rate on long-term capital gain and qualified dividends, typically 15\%.
|
|
330
330
|
\item [$\eta$]
|
|
331
331
|
Spousal ratio for surplus deposits, which goes from 0 to 1, as the fraction
|
|
332
|
-
that goes to the $i = 1$
|
|
332
|
+
that goes to the $i = 1$ spouse's account. Therefore, a surplus $s_n$ in year $n$
|
|
333
333
|
will result in a deposit $d$ in the taxable account of individual $i$ as
|
|
334
334
|
\begin{eqnarray}
|
|
335
335
|
\label{Eq:eta}
|
|
@@ -12,6 +12,7 @@ Disclaimer: This program comes with no guarantee. Use at your own risk.
|
|
|
12
12
|
import toml as toml
|
|
13
13
|
from io import StringIO, BytesIO
|
|
14
14
|
import numpy as np
|
|
15
|
+
from datetime import date
|
|
15
16
|
import os
|
|
16
17
|
|
|
17
18
|
from owlplanner import plan
|
|
@@ -302,6 +303,11 @@ def readConfig(file, *, verbose=True, logstreams=None, readContributions=True):
|
|
|
302
303
|
if name != "None" and name not in p.inames:
|
|
303
304
|
raise ValueError(f"Unknown name {name} for noRothConversions.")
|
|
304
305
|
|
|
306
|
+
# Rebase startRothConversions on year change.
|
|
307
|
+
thisyear = date.today().year
|
|
308
|
+
year = p.solverOptions.get("startRothConversions", thisyear)
|
|
309
|
+
diconf["Solver Options"]["startRothConversions"] = max(year, thisyear)
|
|
310
|
+
|
|
305
311
|
# Results.
|
|
306
312
|
p.setDefaultPlots(diconf["Results"]["Default plots"])
|
|
307
313
|
|
|
@@ -1143,6 +1143,17 @@ class Plan(object):
|
|
|
1143
1143
|
# Should we adjust Roth conversion cap with inflation?
|
|
1144
1144
|
B.set0_Ub(_q2(Cx, i, n, Ni, Nn), rhsopt)
|
|
1145
1145
|
|
|
1146
|
+
# Process startRothConversions option.
|
|
1147
|
+
if "startRothConversions" in options:
|
|
1148
|
+
rhsopt = options["startRothConversions"]
|
|
1149
|
+
thisyear = date.today().year
|
|
1150
|
+
yearn = max(rhsopt - thisyear, 0)
|
|
1151
|
+
|
|
1152
|
+
for i in range(Ni):
|
|
1153
|
+
nstart = min(yearn, self.horizons[i])
|
|
1154
|
+
for n in range(0, nstart):
|
|
1155
|
+
B.set0_Ub(_q2(Cx, i, n, Ni, Nn), zero)
|
|
1156
|
+
|
|
1146
1157
|
# Process noRothConversions option. Also valid when N_i == 1, why not?
|
|
1147
1158
|
if "noRothConversions" in options and options["noRothConversions"] != "None":
|
|
1148
1159
|
rhsopt = options["noRothConversions"]
|
|
@@ -1197,7 +1208,8 @@ class Plan(object):
|
|
|
1197
1208
|
# Account for time elapsed in the current year.
|
|
1198
1209
|
spending *= units * self.yearFracLeft
|
|
1199
1210
|
# self.mylog.vprint('Maximizing bequest with desired net spending of:', u.d(spending))
|
|
1200
|
-
|
|
1211
|
+
# To allow slack in first year, Cg can be made Nn+1 and store basis in g[Nn].
|
|
1212
|
+
A.addNewRow({_q1(Cg, 0, Nn): 1}, spending, spending)
|
|
1201
1213
|
|
|
1202
1214
|
# Set initial balances through constraints.
|
|
1203
1215
|
for i in range(Ni):
|
|
@@ -1370,7 +1382,7 @@ class Plan(object):
|
|
|
1370
1382
|
# Now build a solver-neutral objective vector.
|
|
1371
1383
|
c = abc.Objective(self.nvars)
|
|
1372
1384
|
if objective == "maxSpending":
|
|
1373
|
-
# c.setElem(_q1(Cg, 0, Nn), -1)
|
|
1385
|
+
# c.setElem(_q1(Cg, 0, Nn), -1) # Only OK in implemention without slack.
|
|
1374
1386
|
for n in range(Nn):
|
|
1375
1387
|
c.setElem(_q1(Cg, n, Nn), -1/self.gamma_n[n])
|
|
1376
1388
|
elif objective == "maxBequest":
|
|
@@ -1615,16 +1627,17 @@ class Plan(object):
|
|
|
1615
1627
|
knownObjectives = ["maxBequest", "maxSpending"]
|
|
1616
1628
|
knownSolvers = ["HiGHS", "MOSEK"]
|
|
1617
1629
|
knownOptions = [
|
|
1618
|
-
"units",
|
|
1619
|
-
"maxRothConversion",
|
|
1620
|
-
"netSpending",
|
|
1621
|
-
"spendingSlack",
|
|
1622
1630
|
"bequest",
|
|
1623
1631
|
"bigM",
|
|
1632
|
+
"maxRothConversion",
|
|
1633
|
+
"netSpending",
|
|
1624
1634
|
"noRothConversions",
|
|
1625
|
-
"withMedicare",
|
|
1626
|
-
"solver",
|
|
1627
1635
|
"previousMAGIs",
|
|
1636
|
+
"solver",
|
|
1637
|
+
"spendingSlack",
|
|
1638
|
+
"startRothConversions",
|
|
1639
|
+
"units",
|
|
1640
|
+
"withMedicare",
|
|
1628
1641
|
]
|
|
1629
1642
|
# We will modify options if required.
|
|
1630
1643
|
if options is None:
|
|
@@ -36,7 +36,7 @@ rates_nonTCJA = np.array([0.10, 0.15, 0.25, 0.28, 0.33, 0.35, 0.396])
|
|
|
36
36
|
taxBrackets_TCJA = np.array(
|
|
37
37
|
[
|
|
38
38
|
[11925, 48475, 103350, 197300, 250525, 626350, 9999999],
|
|
39
|
-
[23850, 96950, 206700, 394600, 501050,
|
|
39
|
+
[23850, 96950, 206700, 394600, 501050, 751600, 9999999],
|
|
40
40
|
]
|
|
41
41
|
)
|
|
42
42
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2025.04.01"
|
|
@@ -30,6 +30,7 @@ Copyright © 2024 - Martin-D. Lacasse
|
|
|
30
30
|
[Seaborn](https://seaborn.pydata.org),
|
|
31
31
|
[Scipy](https://scipy.org),
|
|
32
32
|
[openpyxl](https://openpyxl.readthedocs.io),
|
|
33
|
+
[odfpy](https://https://pypi.org/project/odfpy),
|
|
33
34
|
[toml](https://toml.io),
|
|
34
35
|
and [Streamlit](https://streamlit.io) for the front-end
|
|
35
36
|
- Contributors: Josh (noimjosh@gmail.com) for Docker image code.
|
|
@@ -41,12 +42,10 @@ or directly by [email](mailto://martin.d.lacasse@gmail.com).
|
|
|
41
42
|
|
|
42
43
|
#### :orange[Privacy]
|
|
43
44
|
- This app does not store or forward any information. All data entered is lost
|
|
44
|
-
after a session is closed.
|
|
45
|
-
computer before closing the session.
|
|
46
|
-
Source code is publicly available and can be inspected in the repository.
|
|
45
|
+
after a session is closed. You can choose to download selected parts of your
|
|
46
|
+
own data to your computer before closing the session.
|
|
47
47
|
|
|
48
|
-
#### :orange[
|
|
49
|
-
-
|
|
50
|
-
- This program comes with no guarantee. Use at your own risk.
|
|
48
|
+
#### :orange[Disclaimer]
|
|
49
|
+
- This program is provided for educational purposes and comes with no guarantee. Use at your own risk.
|
|
51
50
|
"""
|
|
52
51
|
)
|
|
@@ -51,7 +51,8 @@ else:
|
|
|
51
51
|
col1, col2, col3 = st.columns(3, gap="large", vertical_alignment="top")
|
|
52
52
|
with col1:
|
|
53
53
|
kz.initKey("surplusFraction", 0.5)
|
|
54
|
-
helpmsg = "When beneficiary fractions
|
|
54
|
+
helpmsg = ("When beneficiary fractions not all 1, "
|
|
55
|
+
"set surplus deposits to all go to account of first spouse to pass.")
|
|
55
56
|
ret = kz.getNum(
|
|
56
57
|
f"Fraction deposited in {iname1}'s taxable account",
|
|
57
58
|
"surplusFraction",
|
|
@@ -55,7 +55,8 @@ are entered and reported in unit dollars.
|
|
|
55
55
|
|
|
56
56
|
If you are accessing Owl through the Chrome browser,
|
|
57
57
|
the performance manager might be configured to disable hidden or inactive tabs.
|
|
58
|
-
This
|
|
58
|
+
This could cause your Owl session to inadvertently reset when idling for too long,
|
|
59
|
+
and losing the state of the calculator.
|
|
59
60
|
To avoid this, configure Chrome to keep the page active using
|
|
60
61
|
`More Tools` -> `Performance` -> `Always keep these sites active` and
|
|
61
62
|
add the site *owlplanner.streamlit.app*.
|
|
@@ -80,8 +81,13 @@ and life expectancies are required. A starting date for the plan determines when
|
|
|
80
81
|
starts in the first year. Plan still ends at the end of the year when all individuals
|
|
81
82
|
have passed according to the specified life expectancies.
|
|
82
83
|
|
|
84
|
+
A typical workflow will involve creating
|
|
85
|
+
a base case, and duplicating it a few times with slight parameter changes
|
|
86
|
+
for investigating the resulting effects.
|
|
87
|
+
It is recommended to rename the case to reflect the change in parameters.
|
|
83
88
|
When duplicating a scenario, make sure to visit all pages in the **Case Setup** section
|
|
84
|
-
and verify that all parameters are as intended.
|
|
89
|
+
and verify that all parameters are as intended. When all cases were succussfully run,
|
|
90
|
+
results of the different cases can be compared side-by-side in the `Output Files` section.
|
|
85
91
|
|
|
86
92
|
##### Initializing the life parameters for the realization
|
|
87
93
|
Start with the `Case selector` box and choose one of `New case...` or `Upload case file...`.
|
|
@@ -291,6 +297,14 @@ and a time delay, in years from today, before the non-flat behavior starts to ac
|
|
|
291
297
|
Values default to 15%, 12%, and 0 year respectively, but they are fully configurable
|
|
292
298
|
for experimentation and to fit your anticipated lifestyle.
|
|
293
299
|
|
|
300
|
+
A slack variable can also be adjusted. This variable allows the net spending to deviate from
|
|
301
|
+
the desired profile in order to maximize the objective. This is provided mostly for educational purpose
|
|
302
|
+
as maximizing the total net spending will involve leaving the savings invested for as long as possible,
|
|
303
|
+
and therefore this will favor smaller spending early in the plan and larger towards the end.
|
|
304
|
+
This tension between maximizing a dollar amount and the utility of money then becomes evident.
|
|
305
|
+
While the health of the individuals and therefore the utility of money is higher at the beginning
|
|
306
|
+
of retirement, maximizing the total spending or bequest will pull in an opposite direction.
|
|
307
|
+
|
|
294
308
|
For married couples, the survivor's
|
|
295
309
|
net spending percentage is also configurable. A value of 60% is typically used.
|
|
296
310
|
The selected profile multiplies
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import streamlit as st
|
|
2
|
+
from datetime import date
|
|
2
3
|
|
|
3
4
|
import sskeys as kz
|
|
4
5
|
import owlbridge as owb
|
|
@@ -49,10 +50,15 @@ else:
|
|
|
49
50
|
kz.initKey("readRothX", False)
|
|
50
51
|
fromFile = kz.getKey("readRothX")
|
|
51
52
|
kz.initKey("maxRothConversion", 50)
|
|
52
|
-
ret = kz.getNum("Maximum Roth conversion (\\$k)", "maxRothConversion", disabled=fromFile, help=helpmsg)
|
|
53
|
+
ret = kz.getNum("Maximum annual Roth conversion (\\$k)", "maxRothConversion", disabled=fromFile, help=helpmsg)
|
|
53
54
|
ret = kz.getToggle("Convert as in wages and contributions tables", "readRothX")
|
|
54
55
|
|
|
55
56
|
with col2:
|
|
57
|
+
helpmsg = "Do not perform Roth conversions before that year."
|
|
58
|
+
thisyear = date.today().year
|
|
59
|
+
kz.initKey("startRothConversions", thisyear)
|
|
60
|
+
ret = kz.getIntNum("Year to start Roth conversions", "startRothConversions", min_value=thisyear,
|
|
61
|
+
disabled=fromFile, help=helpmsg)
|
|
56
62
|
if kz.getKey("status") == "married":
|
|
57
63
|
iname1 = kz.getKey("iname1")
|
|
58
64
|
choices = ["None", iname0, iname1]
|
|
@@ -360,7 +360,7 @@ def getSolveParameters():
|
|
|
360
360
|
objective = "maxBequest"
|
|
361
361
|
|
|
362
362
|
options = {}
|
|
363
|
-
optList = ["netSpending", "maxRothConversion", "noRothConversions",
|
|
363
|
+
optList = ["netSpending", "maxRothConversion", "noRothConversions", "startRothConversions",
|
|
364
364
|
"withMedicare", "bequest", "solver", "spendingSlack"]
|
|
365
365
|
for opt in optList:
|
|
366
366
|
val = getKey(opt)
|
|
@@ -510,8 +510,8 @@ def getToggle(text, nkey, callback=setpull, disabled=False, help=None):
|
|
|
510
510
|
)
|
|
511
511
|
|
|
512
512
|
|
|
513
|
-
def divider(color):
|
|
514
|
-
st.html("<style> hr {border-color: %s;}</style><hr>" % color)
|
|
513
|
+
def divider(color, width="auto"):
|
|
514
|
+
st.html("<style> hr {border-color: %s;width: %s}</style><hr>" % (color, width))
|
|
515
515
|
|
|
516
516
|
|
|
517
517
|
def getColors():
|
|
@@ -545,7 +545,7 @@ def titleBar(txt, choices=None):
|
|
|
545
545
|
/* background-color: %s; */
|
|
546
546
|
color: %s;
|
|
547
547
|
top: 2.875rem;
|
|
548
|
-
z-index:
|
|
548
|
+
z-index: 100;
|
|
549
549
|
}
|
|
550
550
|
</style>""" % (bc, fc), unsafe_allow_html=True
|
|
551
551
|
)
|
|
@@ -565,5 +565,6 @@ def titleBar(txt, choices=None):
|
|
|
565
565
|
args=[nkey],
|
|
566
566
|
)
|
|
567
567
|
|
|
568
|
-
divider("white")
|
|
568
|
+
divider("white", "99%")
|
|
569
|
+
|
|
569
570
|
return ret
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2025.03.27"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|