owlplanner 2025.2.15__tar.gz → 2025.2.19__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.2.15 → owlplanner-2025.2.19}/PKG-INFO +1 -1
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/pyproject.toml +1 -1
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/plan.py +5 -4
- owlplanner-2025.2.19/src/owlplanner/version.py +1 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_repro.py +11 -11
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Rates_Selection.py +7 -6
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/requirements.txt +1 -1
- owlplanner-2025.2.15/src/owlplanner/version.py +0 -1
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.devcontainer/devcontainer.json +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.flake8 +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.gitattributes +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.github/workflows/github-actions-runtests.yml +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.gitignore +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/INSTALL.md +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/LICENSE +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/README.md +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/USER_GUIDE.md +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/Dockerfile +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/README.md +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/docker-compose.yml +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/fastentrypoint.sh +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/AD-taxDef.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/AD-taxFree.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/AD-taxable.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/Hist_Bequest.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/Hist_Spending.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/MC-tutorial2a.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/MC-tutorial2b.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/OwlUI.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/allocations.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/owl.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/profile.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/ratesCorrelations.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/ratesPlot.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/savingsPlot.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/sourcesPlot.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/spendingPlot.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/taxIncomePlot.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/taxesPlot.png +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/owl.pdf +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/owl.tex +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_jack+jill.toml +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_joe.toml +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_john+sally.toml +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_kim+sam-bequest.toml +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_kim+sam-spending.toml +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/jack+jill.xlsx +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/joe.xlsx +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/john+sally.xlsx +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/template.xlsx +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/john+sally.ipynb +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/kim+sam.ipynb +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/template.ipynb +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/tutorial_1.ipynb +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/tutorial_2.ipynb +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/tutorial_3.ipynb +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/owlplanner.cmd +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/owlplanner.sh +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/requirements.txt +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/__init__.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/abcapi.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/config.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/data/__init__.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/data/rates.csv +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/logging.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/progress.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/rates.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/tax2025.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/timelists.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/utils.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_logger.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_regressions.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_toml_cases.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_units.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ttt.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/About_Owl.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Asset_Allocation.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Assets.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Create_Case.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Documentation.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Fixed_Income.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Graphs.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Historical_Range.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Logs.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Monte_Carlo.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Optimization_Parameters.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Output_Files.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Quick_Start.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/README.md +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Settings.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Wages_And_Contributions.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Worksheets.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/main+fonts.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/main.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/owlbridge.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/plots.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/progress.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/sskeys.py +0 -0
- {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/style.css +0 -0
|
@@ -306,10 +306,8 @@ class Plan(object):
|
|
|
306
306
|
endyear = thisyear + self.horizons[i] - 1
|
|
307
307
|
self.mylog.vprint(f"{self.inames[i]:>14}: life horizon from {thisyear} -> {endyear}.")
|
|
308
308
|
|
|
309
|
-
# Prepare
|
|
309
|
+
# Prepare RMD time series.
|
|
310
310
|
self.rho_in = tx.rho_in(self.yobs, self.N_n)
|
|
311
|
-
self.sigma_n, self.theta_tn, self.Delta_tn = tx.taxParams(self.yobs, self.i_d, self.n_d,
|
|
312
|
-
self.N_n, self.yTCJA)
|
|
313
311
|
|
|
314
312
|
# If none was given, default is to begin plan on today's date.
|
|
315
313
|
self._setStartingDate(startDate)
|
|
@@ -452,6 +450,7 @@ class Plan(object):
|
|
|
452
450
|
self.mylog.vprint(f"Setting TCJA expiration year to {yTCJA}.")
|
|
453
451
|
self.yTCJA = yTCJA
|
|
454
452
|
self.caseStatus = "modified"
|
|
453
|
+
self._adjustedParameters = False
|
|
455
454
|
|
|
456
455
|
def setLongTermCapitalTaxRate(self, psi):
|
|
457
456
|
"""
|
|
@@ -990,9 +989,11 @@ class Plan(object):
|
|
|
990
989
|
|
|
991
990
|
if not self._adjustedParameters:
|
|
992
991
|
self.mylog.vprint("Adjusting parameters for inflation.")
|
|
992
|
+
self.sigma_n, self.theta_tn, self.Delta_tn = tx.taxParams(self.yobs, self.i_d, self.n_d,
|
|
993
|
+
self.N_n, self.yTCJA)
|
|
994
|
+
self.sigmaBar_n = self.sigma_n * self.gamma_n[:-1]
|
|
993
995
|
self.DeltaBar_tn = self.Delta_tn * self.gamma_n[:-1]
|
|
994
996
|
self.zetaBar_in = self.zeta_in * self.gamma_n[:-1]
|
|
995
|
-
self.sigmaBar_n = self.sigma_n * self.gamma_n[:-1]
|
|
996
997
|
self.xiBar_n = self.xi_n * self.gamma_n[:-1]
|
|
997
998
|
self.piBar_in = self.pi_in
|
|
998
999
|
for i in range(self.N_i):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2025.02.19"
|
|
@@ -36,7 +36,7 @@ def test_case1():
|
|
|
36
36
|
p = createJackAndJillPlan('case1')
|
|
37
37
|
p.setRates('historical', 1969)
|
|
38
38
|
p.solve('maxSpending', options={'maxRothConversion': 100, 'bequest': 500})
|
|
39
|
-
assert p.basis == pytest.approx(
|
|
39
|
+
assert p.basis == pytest.approx(81978.0, abs=0.5)
|
|
40
40
|
assert p.bequest == pytest.approx(500000, abs=0.5)
|
|
41
41
|
|
|
42
42
|
|
|
@@ -45,7 +45,7 @@ def test_case2():
|
|
|
45
45
|
p.setRates('historical', 1969)
|
|
46
46
|
p.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
47
47
|
assert p.basis == pytest.approx(80000, abs=0.5)
|
|
48
|
-
assert p.bequest == pytest.approx(
|
|
48
|
+
assert p.bequest == pytest.approx(595407.5, abs=0.5)
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def test_config1():
|
|
@@ -54,7 +54,7 @@ def test_config1():
|
|
|
54
54
|
p.setRates('historical', 1969)
|
|
55
55
|
p.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
56
56
|
assert p.basis == pytest.approx(80000, abs=0.5)
|
|
57
|
-
assert p.bequest == pytest.approx(
|
|
57
|
+
assert p.bequest == pytest.approx(595407.5, abs=0.5)
|
|
58
58
|
p.saveConfig()
|
|
59
59
|
base_filename = 'case_' + name
|
|
60
60
|
full_filename = 'case_' + name + '.toml'
|
|
@@ -62,11 +62,11 @@ def test_config1():
|
|
|
62
62
|
p2 = owl.readConfig(base_filename)
|
|
63
63
|
p2.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
64
64
|
assert p2.basis == pytest.approx(80000, abs=0.5)
|
|
65
|
-
assert p2.bequest == pytest.approx(
|
|
65
|
+
assert p2.bequest == pytest.approx(595407.5, abs=0.5)
|
|
66
66
|
p3 = owl.readConfig(full_filename)
|
|
67
67
|
p3.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
68
68
|
assert p3.basis == pytest.approx(80000, abs=0.5)
|
|
69
|
-
assert p3.bequest == pytest.approx(
|
|
69
|
+
assert p3.bequest == pytest.approx(595407.5, abs=0.5)
|
|
70
70
|
os.remove(full_filename)
|
|
71
71
|
|
|
72
72
|
|
|
@@ -76,14 +76,14 @@ def test_config2():
|
|
|
76
76
|
p.setRates('historical', 1969)
|
|
77
77
|
p.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
78
78
|
assert p.basis == pytest.approx(80000, abs=0.5)
|
|
79
|
-
assert p.bequest == pytest.approx(
|
|
79
|
+
assert p.bequest == pytest.approx(595407.5, abs=0.5)
|
|
80
80
|
iostring = StringIO()
|
|
81
81
|
p.saveConfig(iostring)
|
|
82
82
|
# print('iostream:', iostream.getvalue())
|
|
83
83
|
p2 = owl.readConfig(iostring)
|
|
84
84
|
p2.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
85
85
|
assert p2.basis == pytest.approx(80000, abs=0.5)
|
|
86
|
-
assert p2.bequest == pytest.approx(
|
|
86
|
+
assert p2.bequest == pytest.approx(595407.5, abs=0.5)
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
def test_clone1():
|
|
@@ -92,12 +92,12 @@ def test_clone1():
|
|
|
92
92
|
p.setRates('historical', 1969)
|
|
93
93
|
p.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
94
94
|
assert p.basis == pytest.approx(80000, abs=0.5)
|
|
95
|
-
assert p.bequest == pytest.approx(
|
|
95
|
+
assert p.bequest == pytest.approx(595407.5, abs=0.5)
|
|
96
96
|
name2 = 'testclone2'
|
|
97
97
|
p2 = owl.clone(p, name2)
|
|
98
98
|
p2.solve('maxBequest', options={'maxRothConversion': 100, 'netSpending': 80})
|
|
99
99
|
assert p2.basis == pytest.approx(80000, abs=0.5)
|
|
100
|
-
assert p2.bequest == pytest.approx(
|
|
100
|
+
assert p2.bequest == pytest.approx(595407.5, abs=0.5)
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
def test_clone2():
|
|
@@ -105,10 +105,10 @@ def test_clone2():
|
|
|
105
105
|
p = createJackAndJillPlan(name)
|
|
106
106
|
p.setRates('historical', 1969)
|
|
107
107
|
p.solve('maxSpending', options={'maxRothConversion': 100, 'bequest': 0})
|
|
108
|
-
assert p.basis == pytest.approx(
|
|
108
|
+
assert p.basis == pytest.approx(92317.5, abs=0.5)
|
|
109
109
|
assert p.bequest == pytest.approx(0, abs=0.5)
|
|
110
110
|
name2 = 'testclone2'
|
|
111
111
|
p2 = owl.clone(p, name2)
|
|
112
112
|
p2.solve('maxSpending', options={'maxRothConversion': 100, 'bequest': 0})
|
|
113
|
-
assert p2.basis == pytest.approx(
|
|
113
|
+
assert p2.basis == pytest.approx(92317.5, abs=0.5)
|
|
114
114
|
assert p2.bequest == pytest.approx(0, abs=0.5)
|
|
@@ -210,23 +210,24 @@ else:
|
|
|
210
210
|
|
|
211
211
|
st.divider()
|
|
212
212
|
st.write("### Other rates")
|
|
213
|
-
col1, col2 = st.columns(
|
|
213
|
+
col1, col2, col3 = st.columns(3, gap="large", vertical_alignment="top")
|
|
214
214
|
with col1:
|
|
215
215
|
kz.initKey("divRate", 2)
|
|
216
216
|
helpmsg = "Average annual (qualified) dividend return rate on stock portfolio for income tax purposes."
|
|
217
217
|
ret = kz.getNum("Dividend rate (%)", "divRate", max_value=100.0, format="%.2f", help=helpmsg, step=1.0)
|
|
218
218
|
|
|
219
219
|
st.write("#### Income taxes")
|
|
220
|
-
col1, col2 = st.columns(
|
|
220
|
+
col1, col2, col3 = st.columns(3, gap="large", vertical_alignment="top")
|
|
221
221
|
with col1:
|
|
222
222
|
kz.initKey("gainTx", 15)
|
|
223
223
|
ret = kz.getNum("Long-term capital gains tax rate (%)", "gainTx", max_value=100.0, step=1.0)
|
|
224
224
|
|
|
225
|
-
kz.initKey("yTCJA", 2026)
|
|
226
|
-
helpmsg = "Year at which the Tax Cut And Job Act tax rates are speculated to expire."
|
|
227
|
-
ret = kz.getIntNum("TCJA expiration year", "yTCJA", help=helpmsg)
|
|
228
|
-
|
|
229
225
|
with col2:
|
|
230
226
|
kz.initKey("heirsTx", 30)
|
|
231
227
|
helpmsg = "Marginal tax rate that heirs would have to pay on inherited tax-deferred balance."
|
|
232
228
|
ret = kz.getNum("Heirs marginal tax rate (%)", "heirsTx", max_value=100.0, help=helpmsg, step=1.0)
|
|
229
|
+
|
|
230
|
+
with col3:
|
|
231
|
+
kz.initKey("yTCJA", 2026)
|
|
232
|
+
helpmsg = "Year at which the Tax Cut And Job Act tax rates are speculated to expire."
|
|
233
|
+
ret = kz.getIntNum("TCJA expiration year", "yTCJA", help=helpmsg)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2025.02.15"
|
|
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
|