owlplanner 2025.2.27__py3-none-any.whl → 2025.3.11__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 +3 -3
- owlplanner/plan.py +23 -12
- owlplanner/version.py +1 -1
- {owlplanner-2025.2.27.dist-info → owlplanner-2025.3.11.dist-info}/METADATA +11 -5
- {owlplanner-2025.2.27.dist-info → owlplanner-2025.3.11.dist-info}/RECORD +7 -7
- {owlplanner-2025.2.27.dist-info → owlplanner-2025.3.11.dist-info}/WHEEL +0 -0
- {owlplanner-2025.2.27.dist-info → owlplanner-2025.3.11.dist-info}/licenses/LICENSE +0 -0
owlplanner/config.py
CHANGED
|
@@ -55,7 +55,7 @@ def saveConfig(myplan, file, mylog):
|
|
|
55
55
|
diconf["Fixed Income"] = {
|
|
56
56
|
"Pension amounts": (myplan.pensionAmounts / 1000).tolist(),
|
|
57
57
|
"Pension ages": myplan.pensionAges.tolist(),
|
|
58
|
-
"Pension indexed": myplan.
|
|
58
|
+
"Pension indexed": myplan.pensionIsIndexed,
|
|
59
59
|
"Social security amounts": (myplan.ssecAmounts / 1000).tolist(),
|
|
60
60
|
"Social security ages": myplan.ssecAges.tolist(),
|
|
61
61
|
}
|
|
@@ -222,8 +222,8 @@ def readConfig(file, *, verbose=True, logstreams=None, readContributions=True):
|
|
|
222
222
|
p.setSocialSecurity(ssecAmounts, ssecAges)
|
|
223
223
|
pensionAmounts = np.array(diconf["Fixed Income"]["Pension amounts"], dtype=np.float32)
|
|
224
224
|
pensionAges = np.array(diconf["Fixed Income"]["Pension ages"], dtype=np.int32)
|
|
225
|
-
|
|
226
|
-
p.setPension(pensionAmounts, pensionAges,
|
|
225
|
+
pensionIsIndexed = diconf["Fixed Income"]["Pension indexed"]
|
|
226
|
+
p.setPension(pensionAmounts, pensionAges, pensionIsIndexed)
|
|
227
227
|
|
|
228
228
|
# Rates Selection.
|
|
229
229
|
p.setDividendRate(float(diconf["Rates Selection"]["Dividend tax rate"]))
|
owlplanner/plan.py
CHANGED
|
@@ -291,7 +291,7 @@ class Plan(object):
|
|
|
291
291
|
self.zeta_in = np.zeros((self.N_i, self.N_n))
|
|
292
292
|
self.pensionAmounts = np.zeros(self.N_i)
|
|
293
293
|
self.pensionAges = 65 * np.ones(self.N_i, dtype=np.int32)
|
|
294
|
-
self.
|
|
294
|
+
self.pensionIsIndexed = [False, False]
|
|
295
295
|
self.ssecAmounts = np.zeros(self.N_i)
|
|
296
296
|
self.ssecAges = 67 * np.ones(self.N_i, dtype=np.int32)
|
|
297
297
|
|
|
@@ -531,7 +531,7 @@ class Plan(object):
|
|
|
531
531
|
|
|
532
532
|
self.pensionAmounts = np.array(amounts)
|
|
533
533
|
self.pensionAges = np.array(ages, dtype=np.int32)
|
|
534
|
-
self.
|
|
534
|
+
self.pensionIsIndexed = indexed
|
|
535
535
|
self.caseStatus = "modified"
|
|
536
536
|
self._adjustedParameters = False
|
|
537
537
|
|
|
@@ -1008,7 +1008,7 @@ class Plan(object):
|
|
|
1008
1008
|
self.xiBar_n = self.xi_n * self.gamma_n[:-1]
|
|
1009
1009
|
self.piBar_in = np.array(self.pi_in)
|
|
1010
1010
|
for i in range(self.N_i):
|
|
1011
|
-
if self.
|
|
1011
|
+
if self.pensionIsIndexed[i]:
|
|
1012
1012
|
self.piBar_in[i] *= self.gamma_n[:-1]
|
|
1013
1013
|
|
|
1014
1014
|
self._adjustedParameters = True
|
|
@@ -1968,8 +1968,8 @@ class Plan(object):
|
|
|
1968
1968
|
self.dist_in = self.w_ijn[:, 1, :] - self.rmd_in
|
|
1969
1969
|
self.dist_in[self.dist_in < 0] = 0
|
|
1970
1970
|
self.G_n = np.sum(self.F_tn, axis=0)
|
|
1971
|
-
T_tn = self.F_tn * self.theta_tn
|
|
1972
|
-
self.T_n = np.sum(T_tn, axis=0)
|
|
1971
|
+
self.T_tn = self.F_tn * self.theta_tn
|
|
1972
|
+
self.T_n = np.sum(self.T_tn, axis=0)
|
|
1973
1973
|
|
|
1974
1974
|
tau_0 = np.array(self.tau_kn[0, :])
|
|
1975
1975
|
tau_0[tau_0 < 0] = 0
|
|
@@ -2105,6 +2105,12 @@ class Plan(object):
|
|
|
2105
2105
|
taxPaidNow = np.sum(self.T_n / self.gamma_n[:-1], axis=0)
|
|
2106
2106
|
dic["Total income tax paid on ordinary income"] = f"{u.d(taxPaidNow)}"
|
|
2107
2107
|
dic["- Total income tax paid on ordinary income (nominal)"] = f"{u.d(taxPaid)}"
|
|
2108
|
+
for t in range(self.N_t):
|
|
2109
|
+
taxPaid = np.sum(self.T_tn[t], axis=0)
|
|
2110
|
+
taxPaidNow = np.sum(self.T_tn[t] / self.gamma_n[:-1], axis=0)
|
|
2111
|
+
tname = tx.taxBracketNames[t]
|
|
2112
|
+
dic[f"-- Subtotal in tax bracket {tname}"] = f"{u.d(taxPaidNow)}"
|
|
2113
|
+
dic[f"--- Subtotal in tax bracket {tname} (nominal)"] = f"{u.d(taxPaid)}"
|
|
2108
2114
|
|
|
2109
2115
|
taxPaid = np.sum(self.U_n, axis=0)
|
|
2110
2116
|
taxPaidNow = np.sum(self.U_n / self.gamma_n[:-1], axis=0)
|
|
@@ -2129,12 +2135,17 @@ class Plan(object):
|
|
|
2129
2135
|
iname_s = self.inames[self.i_s]
|
|
2130
2136
|
iname_d = self.inames[self.i_d]
|
|
2131
2137
|
dic[f"Sum of spousal transfer to {iname_s} in year {ynx}"] = (f"{u.d(totSpousalNow)}")
|
|
2132
|
-
dic[f"- Sum of spousal transfer to {iname_s} in year {ynx} (nominal)"] = (
|
|
2133
|
-
|
|
2134
|
-
dic[f"-- Spousal transfer to {iname_s} in year {ynx} -
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
+
dic[f"- Sum of spousal transfer to {iname_s} in year {ynx} (nominal)"] = (
|
|
2139
|
+
f"{u.d(totSpousal)}")
|
|
2140
|
+
dic[f"-- Spousal transfer to {iname_s} in year {ynx} - taxable (nominal)"] = (
|
|
2141
|
+
f"{u.d(q_j[0])}")
|
|
2142
|
+
dic[f"-- Spousal transfer to {iname_s} in year {ynx} - tax-def (nominal)"] = (
|
|
2143
|
+
f"{u.d(q_j[1])}")
|
|
2144
|
+
dic[f"-- Spousal transfer to {iname_s} in year {ynx} - tax-free (nominal)"] = (
|
|
2145
|
+
f"{u.d(q_j[2])}")
|
|
2146
|
+
|
|
2147
|
+
dic[f"Sum of post-tax non-spousal bequests from {iname_d} in year {ynx}"] = (
|
|
2148
|
+
f"{u.d(totOthersNow)}")
|
|
2138
2149
|
dic[f"- Sum of post-tax non-spousal bequests from {iname_d} in year {ynx} (nominal)"] = (
|
|
2139
2150
|
f"{u.d(totOthers)}")
|
|
2140
2151
|
dic[f"-- Post-tax non-spousal bequests from {iname_d} in year {ynx} - taxable (nominal)"] = (
|
|
@@ -2156,7 +2167,7 @@ class Plan(object):
|
|
|
2156
2167
|
dic[f"-- Post-tax account value at the end of {lastyear} - tax-free (nominal)"] = (f"{u.d(estate[2])}")
|
|
2157
2168
|
|
|
2158
2169
|
dic["Plan starting date"] = str(self.startDate)
|
|
2159
|
-
dic[f"Cumulative inflation factor from start date to end of {lastyear}"] = f"{self.gamma_n[-1]:.2f}"
|
|
2170
|
+
dic[f"Cumulative inflation factor from start date to end of {lastyear}"] = (f"{self.gamma_n[-1]:.2f}")
|
|
2160
2171
|
for i in range(self.N_i):
|
|
2161
2172
|
dic[f"{self.inames[i]:>12}'s {self.horizons[i]:02}-year life horizon"] = (
|
|
2162
2173
|
f"{now} -> {now + self.horizons[i] - 1}")
|
owlplanner/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2025.
|
|
1
|
+
__version__ = "2025.03.11"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: owlplanner
|
|
3
|
-
Version: 2025.
|
|
3
|
+
Version: 2025.3.11
|
|
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
|
|
@@ -711,7 +711,13 @@ Description-Content-Type: text/markdown
|
|
|
711
711
|
-------------------------------------------------------------------------------------
|
|
712
712
|
|
|
713
713
|
### TL;DR
|
|
714
|
-
Owl is a planning tool that uses a linear programming optimization algorithm
|
|
714
|
+
Owl is a retirement planning tool that uses a linear programming optimization algorithm
|
|
715
|
+
to provide guidance on retirement decisions, including Roth conversions.
|
|
716
|
+
Users can select varying return rates to perform historical back testing,
|
|
717
|
+
stochastic rates for performing Monte Carlo analyses,
|
|
718
|
+
or fixed rates either derived from historical averages, or set by the user.
|
|
719
|
+
|
|
720
|
+
There are a few ways to run Owl:
|
|
715
721
|
|
|
716
722
|
- Run Owl directly on the Streamlit Community Server at [owlplanner.streamlit.app](https://owlplanner.streamlit.app).
|
|
717
723
|
|
|
@@ -759,7 +765,7 @@ mathematical model can be found [here](https://raw.github.com/mdlacasse/Owl/main
|
|
|
759
765
|
It is anticipated that most end users will use Owl through the graphical interface
|
|
760
766
|
either at [owlplanner.streamlit.app](https://owlplanner.streamlit.app)
|
|
761
767
|
or [installed](INSTALL.md) on their own computer.
|
|
762
|
-
The underlying Python package can also be used directly through Python scripts or
|
|
768
|
+
The underlying Python package can also be used directly through Python scripts or Jupyter Notebooks
|
|
763
769
|
as described [here](USER_GUIDE.md).
|
|
764
770
|
|
|
765
771
|
Not every retirement decision strategy can be framed as an easy-to-solve optimization problem.
|
|
@@ -792,7 +798,7 @@ Asset allocations are selected for the duration of the plan, and these can glide
|
|
|
792
798
|
or along a configurable s-curve over the lifespan of the individual.
|
|
793
799
|
|
|
794
800
|
Spending profiles are adjusted for inflation, and so are all other indexable quantities. Proflies can be
|
|
795
|
-
flat or follow a *smile* curve which is also adjustable through
|
|
801
|
+
flat or follow a *smile* curve which is also adjustable through three simple parameters.
|
|
796
802
|
|
|
797
803
|
Available rates are from 1928 to last year and can be used to test historical performance.
|
|
798
804
|
Fixed rates can also be provided, as well as *histochastic* rates, which are generated using
|
|
@@ -868,7 +874,7 @@ assets to support, even with no estate being left.
|
|
|
868
874
|
---------------------------------------------------------------
|
|
869
875
|
## Documentation
|
|
870
876
|
|
|
871
|
-
- Documentation for the app user interface is available from the interface itself.
|
|
877
|
+
- Documentation for the app user interface is available from the interface [itself](https://owlplanner.streamlit.app/Documentation).
|
|
872
878
|
- Installation guide and software requirements can be found [here](INSTALL.md).
|
|
873
879
|
- User guide for the underlying Python package as used in a Jupyter notebook can be found [here](USER_GUIDE.md).
|
|
874
880
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
owlplanner/__init__.py,sha256=QqrdT0Qks20osBTg7h0vJHAxpP9lL7DA99xb0nYbtw4,254
|
|
2
2
|
owlplanner/abcapi.py,sha256=LbzW_KcNy0IeHp42MUHwGu_H67B2h_e1_vu-c2ACTkQ,6646
|
|
3
|
-
owlplanner/config.py,sha256=
|
|
3
|
+
owlplanner/config.py,sha256=uJ6jZ9Rx2CB2P5cFRscsUCXOW6uml7I4pta2ozjW0uo,12263
|
|
4
4
|
owlplanner/logging.py,sha256=tYMw04O-XYSzjTj36fmKJGLcE1VkK6k6oJNeqtKXzuc,2530
|
|
5
|
-
owlplanner/plan.py,sha256=
|
|
5
|
+
owlplanner/plan.py,sha256=mCcoocK9g1nCUpQDaMVUTiwugKPuDZaEYbrSPPi0sBY,115404
|
|
6
6
|
owlplanner/progress.py,sha256=8jlCvvtgDI89zXVNMBg1-lnEyhpPvKQS2X5oAIpoOVQ,384
|
|
7
7
|
owlplanner/rates.py,sha256=TN407qU4n-bac1oymkQ_n2QKEPwFQxy6JZVGwgIkLQU,15585
|
|
8
8
|
owlplanner/tax2025.py,sha256=B-A5eU3wxdcAaxRCbT3qI-JEKoD_ZeNbg_86XhNdQEI,7745
|
|
9
9
|
owlplanner/timelists.py,sha256=tYieZU67FT6TCcQQis36JaXGI7dT6NqD7RvdEjgJL4M,4026
|
|
10
10
|
owlplanner/utils.py,sha256=HM70W60qB41zfnbl2LltNwAuLYHyy5XYbwnbNcaa6FE,2351
|
|
11
|
-
owlplanner/version.py,sha256=
|
|
11
|
+
owlplanner/version.py,sha256=HrtvlhxZp9gln32M2lagzUjBWC3oqV1rUecAVUAAJj8,28
|
|
12
12
|
owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
|
|
14
|
-
owlplanner-2025.
|
|
15
|
-
owlplanner-2025.
|
|
16
|
-
owlplanner-2025.
|
|
17
|
-
owlplanner-2025.
|
|
14
|
+
owlplanner-2025.3.11.dist-info/METADATA,sha256=h06pbBAcJCEqWvY_zOybpnIk5xEaVhax8R2YESfn73o,53801
|
|
15
|
+
owlplanner-2025.3.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
owlplanner-2025.3.11.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
17
|
+
owlplanner-2025.3.11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|