owlplanner 2025.8.1__py3-none-any.whl → 2025.9.15__py3-none-any.whl
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/config.py +4 -0
- owlplanner/plan.py +9 -10
- owlplanner/version.py +1 -1
- {owlplanner-2025.8.1.dist-info → owlplanner-2025.9.15.dist-info}/METADATA +2 -2
- {owlplanner-2025.8.1.dist-info → owlplanner-2025.9.15.dist-info}/RECORD +7 -7
- {owlplanner-2025.8.1.dist-info → owlplanner-2025.9.15.dist-info}/WHEEL +0 -0
- {owlplanner-2025.8.1.dist-info → owlplanner-2025.9.15.dist-info}/licenses/LICENSE +0 -0
owlplanner/config.py
CHANGED
|
@@ -296,6 +296,10 @@ def readConfig(file, *, verbose=True, logstreams=None, readContributions=True):
|
|
|
296
296
|
# Solver Options.
|
|
297
297
|
p.solverOptions = diconf["Solver Options"]
|
|
298
298
|
|
|
299
|
+
# Address legacy case files.
|
|
300
|
+
if diconf["Solver Options"].get("withMedicare", None) is True:
|
|
301
|
+
p.solverOptions["withMedicare"] = "loop"
|
|
302
|
+
|
|
299
303
|
# Check consistency of noRothConversions.
|
|
300
304
|
name = p.solverOptions.get("noRothConversions", "None")
|
|
301
305
|
if name != "None" and name not in p.inames:
|
owlplanner/plan.py
CHANGED
|
@@ -1021,7 +1021,7 @@ class Plan(object):
|
|
|
1021
1021
|
Refer to companion document for explanations.
|
|
1022
1022
|
All binary variables must be lumped at the end of the vector.
|
|
1023
1023
|
"""
|
|
1024
|
-
medi = options.get("
|
|
1024
|
+
medi = options.get("withMedicare", "loop") == "optimize"
|
|
1025
1025
|
|
|
1026
1026
|
# Stack all variables in a single block vector with all binary variables at the end.
|
|
1027
1027
|
C = {}
|
|
@@ -1386,7 +1386,7 @@ class Plan(object):
|
|
|
1386
1386
|
self.B.setRange(_q3(self.C["zx"], i, n, 1, self.N_i, self.N_n, self.N_zx), 0, 0)
|
|
1387
1387
|
|
|
1388
1388
|
def _configure_Medicare_binary_variables(self, options):
|
|
1389
|
-
if
|
|
1389
|
+
if options.get("withMedicare", "loop") != "optimize":
|
|
1390
1390
|
return
|
|
1391
1391
|
|
|
1392
1392
|
bigM = options.get("bigM", 5e6)
|
|
@@ -1444,7 +1444,7 @@ class Plan(object):
|
|
|
1444
1444
|
self.A.addRow(row2, -np.inf, rhs2)
|
|
1445
1445
|
|
|
1446
1446
|
def _add_Medicare_costs(self, options):
|
|
1447
|
-
if
|
|
1447
|
+
if options.get("withMedicare", "loop") != "optimize":
|
|
1448
1448
|
return
|
|
1449
1449
|
|
|
1450
1450
|
for n in range(self.nm):
|
|
@@ -1627,7 +1627,7 @@ class Plan(object):
|
|
|
1627
1627
|
"netSpending",
|
|
1628
1628
|
"noRothConversions",
|
|
1629
1629
|
"oppCostX",
|
|
1630
|
-
"
|
|
1630
|
+
"withMedicare",
|
|
1631
1631
|
"previousMAGIs",
|
|
1632
1632
|
"solver",
|
|
1633
1633
|
"spendingSlack",
|
|
@@ -1635,7 +1635,6 @@ class Plan(object):
|
|
|
1635
1635
|
"units",
|
|
1636
1636
|
"xorConstraints",
|
|
1637
1637
|
"withSCLoop",
|
|
1638
|
-
"withMedicare", # Ignore keyword.
|
|
1639
1638
|
]
|
|
1640
1639
|
# We might modify options if required.
|
|
1641
1640
|
options = {} if options is None else options
|
|
@@ -1713,7 +1712,7 @@ class Plan(object):
|
|
|
1713
1712
|
"""
|
|
1714
1713
|
Self-consistent loop, regardless of solver.
|
|
1715
1714
|
"""
|
|
1716
|
-
|
|
1715
|
+
includeMedicare = options.get("withMedicare", "loop") == "loop"
|
|
1717
1716
|
withSCLoop = options.get("withSCLoop", True)
|
|
1718
1717
|
|
|
1719
1718
|
if objective == "maxSpending":
|
|
@@ -1724,7 +1723,7 @@ class Plan(object):
|
|
|
1724
1723
|
it = 0
|
|
1725
1724
|
old_x = np.zeros(self.nvars)
|
|
1726
1725
|
old_objfns = [np.inf]
|
|
1727
|
-
self._computeNLstuff(None,
|
|
1726
|
+
self._computeNLstuff(None, includeMedicare)
|
|
1728
1727
|
while True:
|
|
1729
1728
|
objfn, xx, solverSuccess, solverMsg = solverMethod(objective, options)
|
|
1730
1729
|
|
|
@@ -1735,7 +1734,7 @@ class Plan(object):
|
|
|
1735
1734
|
if not withSCLoop:
|
|
1736
1735
|
break
|
|
1737
1736
|
|
|
1738
|
-
self._computeNLstuff(xx,
|
|
1737
|
+
self._computeNLstuff(xx, includeMedicare)
|
|
1739
1738
|
|
|
1740
1739
|
delta = xx - old_x
|
|
1741
1740
|
absSolDiff = np.sum(np.abs(delta), axis=0)/100
|
|
@@ -1955,7 +1954,7 @@ class Plan(object):
|
|
|
1955
1954
|
|
|
1956
1955
|
return J_n
|
|
1957
1956
|
|
|
1958
|
-
def _computeNLstuff(self, x,
|
|
1957
|
+
def _computeNLstuff(self, x, includeMedicare):
|
|
1959
1958
|
"""
|
|
1960
1959
|
Compute MAGI, Medicare costs, long-term capital gain tax rate, and
|
|
1961
1960
|
net investment income tax (NIIT).
|
|
@@ -1972,7 +1971,7 @@ class Plan(object):
|
|
|
1972
1971
|
self.J_n = self._computeNIIT(self.MAGI_n, self.I_n, self.Q_n)
|
|
1973
1972
|
self.psi_n = tx.capitalGainTaxRate(self.N_i, self.MAGI_n, self.gamma_n[:-1], self.n_d, self.N_n)
|
|
1974
1973
|
# Compute Medicare through self-consistent loop.
|
|
1975
|
-
if
|
|
1974
|
+
if includeMedicare:
|
|
1976
1975
|
self.M_n = tx.mediCosts(self.yobs, self.horizons, self.MAGI_n, self.prevMAGI, self.gamma_n[:-1], self.N_n)
|
|
1977
1976
|
|
|
1978
1977
|
return None
|
owlplanner/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2025.
|
|
1
|
+
__version__ = "2025.09.15"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: owlplanner
|
|
3
|
-
Version: 2025.
|
|
3
|
+
Version: 2025.9.15
|
|
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
|
|
@@ -744,7 +744,7 @@ Look at *Basic capabilities* below for more detail.
|
|
|
744
744
|
One can certainly have a savings plan, but due to the volatility of financial investments,
|
|
745
745
|
it is impossible to have a certain asset earnings plan. This does not mean one cannot make decisions.
|
|
746
746
|
These decisions need to be guided with an understanding of the sensitivity of the parameters.
|
|
747
|
-
This is exactly where this tool fits
|
|
747
|
+
This is exactly where this tool fits in. Given your savings capabilities and spending desires,
|
|
748
748
|
it can generate different future realizations of
|
|
749
749
|
your strategy under different market assumptions, helping to better understand your financial situation.
|
|
750
750
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
owlplanner/__init__.py,sha256=hJ2i4m2JpHPAKyQLjYOXpJzeEsgcTcKD-Vhm0AIjjWg,592
|
|
2
2
|
owlplanner/abcapi.py,sha256=rtg7d0UbftinokR9VlB49VUjDjzUq3ONnJbhMXVIrgo,6879
|
|
3
|
-
owlplanner/config.py,sha256=
|
|
3
|
+
owlplanner/config.py,sha256=UF2Dy6E9PiX6Ua8B1R0aYCNUoIYmY46up8awf_36B_Q,12615
|
|
4
4
|
owlplanner/mylogging.py,sha256=OVGeDFO7LIZG91R6HMpZBzjno-B8PH8Fo00Jw2Pdgqw,2558
|
|
5
|
-
owlplanner/plan.py,sha256=
|
|
5
|
+
owlplanner/plan.py,sha256=pIKULy5GFo_8xOZByZ9_CXrHx9TcXQpaob8cblK46N8,115180
|
|
6
6
|
owlplanner/progress.py,sha256=dUUlFmSAKUei36rUj2BINRY10f_YEUo_e23d0es6nrc,530
|
|
7
7
|
owlplanner/rates.py,sha256=9Nmo8AKsyi5PoCUrzhr06phkSlNTv-TXzj5iYFU76AY,14113
|
|
8
8
|
owlplanner/tax2025.py,sha256=2Jb_UbPT6ye-znRjA0nSaF8T8M17QW4MoRPDoW9XJ8s,10833
|
|
9
9
|
owlplanner/timelists.py,sha256=Q4kBt9kKAa5qxsvOe9wfyUtCQVgiwPmJXTwXUPRBBv8,4066
|
|
10
10
|
owlplanner/utils.py,sha256=afAjeO6Msf6Rn4jwz_7Ody9rHGWlBR7iQFqe1xzLNQc,2513
|
|
11
|
-
owlplanner/version.py,sha256=
|
|
11
|
+
owlplanner/version.py,sha256=zy-IhWuHFT4sXG7yuvz7mQvMfgQ6BwWFA7KrAtVvwYg,28
|
|
12
12
|
owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
|
|
14
14
|
owlplanner/plotting/__init__.py,sha256=uhxqtUi0OI-QWNOO2LkXgQViW_9yM3rYb-204Wit974,250
|
|
@@ -16,7 +16,7 @@ owlplanner/plotting/base.py,sha256=UimGKpMTV-dVm3BX5Apr_Ltorc7dlDLCRPRQ3RF_v7c,2
|
|
|
16
16
|
owlplanner/plotting/factory.py,sha256=EDopIAPQr9zHRgemObko18FlCeRNhNCoLNNFAOq-X6s,1030
|
|
17
17
|
owlplanner/plotting/matplotlib_backend.py,sha256=AOEkapD94U5hGNoS0EdbRoe8mgdMHH4oOvkXADZS914,17957
|
|
18
18
|
owlplanner/plotting/plotly_backend.py,sha256=AO33GxBHGYG5osir_H1iRRtGxdhs4AjfLV2d_xm35nY,33138
|
|
19
|
-
owlplanner-2025.
|
|
20
|
-
owlplanner-2025.
|
|
21
|
-
owlplanner-2025.
|
|
22
|
-
owlplanner-2025.
|
|
19
|
+
owlplanner-2025.9.15.dist-info/METADATA,sha256=yacvHpuuPAtZ4fvLHH2UcbIdtkWwgVc0zwIvtOBNw64,54045
|
|
20
|
+
owlplanner-2025.9.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
21
|
+
owlplanner-2025.9.15.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
22
|
+
owlplanner-2025.9.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|