owlplanner 2025.2.25__py3-none-any.whl → 2025.3.7__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/plan.py CHANGED
@@ -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
@@ -2062,6 +2062,12 @@ class Plan(object):
2062
2062
 
2063
2063
  return mylist
2064
2064
 
2065
+ def summaryDf(self):
2066
+ """
2067
+ Return summary as a dataframe.
2068
+ """
2069
+ return pd.DataFrame(self.summaryDic(), index=[self._name])
2070
+
2065
2071
  def summaryString(self):
2066
2072
  """
2067
2073
  Return summary as a string.
@@ -2081,29 +2087,40 @@ class Plan(object):
2081
2087
  dic = {}
2082
2088
  # Results
2083
2089
  dic["Plan name"] = self._name
2084
- dic[f"Net yearly spending basis in {now}$"] = u.d(self.g_n[0] / self.xi_n[0])
2085
- dic[f"Net yearly spending for year {now}"] = u.d(self.g_n[0] / self.yearFracLeft)
2090
+ dic["Net yearly spending basis"] = u.d(self.g_n[0] / self.xi_n[0])
2091
+ dic[f"Net spending for year {now}"] = u.d(self.g_n[0] / self.yearFracLeft)
2086
2092
  dic[f"Net spending remaining in year {now}"] = u.d(self.g_n[0])
2087
2093
 
2088
2094
  totIncome = np.sum(self.g_n, axis=0)
2089
2095
  totIncomeNow = np.sum(self.g_n / self.gamma_n[:-1], axis=0)
2090
- dic[f"Total net spending in {now}$"] = f"{u.d(totIncomeNow)} ({u.d(totIncome)} nominal)"
2096
+ dic["Total net spending"] = f"{u.d(totIncomeNow)}"
2097
+ dic["- Total net spending (nominal)"] = f"{u.d(totIncome)}"
2091
2098
 
2092
2099
  totRoth = np.sum(self.x_in, axis=(0, 1))
2093
2100
  totRothNow = np.sum(np.sum(self.x_in, axis=0) / self.gamma_n[:-1], axis=0)
2094
- dic[f"Total Roth conversions in {now}$"] = f"{u.d(totRothNow)} ({u.d(totRoth)} nominal)"
2101
+ dic["Total Roth conversions"] = f"{u.d(totRothNow)}"
2102
+ dic["- Total Roth conversions (nominal)"] = f"{u.d(totRoth)}"
2095
2103
 
2096
2104
  taxPaid = np.sum(self.T_n, axis=0)
2097
2105
  taxPaidNow = np.sum(self.T_n / self.gamma_n[:-1], axis=0)
2098
- dic[f"Total income tax paid on ordinary income in {now}$"] = f"{u.d(taxPaidNow)} ({u.d(taxPaid)} nominal)"
2106
+ dic["Total income tax paid on ordinary income"] = f"{u.d(taxPaidNow)}"
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)}"
2099
2114
 
2100
2115
  taxPaid = np.sum(self.U_n, axis=0)
2101
2116
  taxPaidNow = np.sum(self.U_n / self.gamma_n[:-1], axis=0)
2102
- dic[f"Total tax paid on gains and dividends in {now}$"] = f"{u.d(taxPaidNow)} ({u.d(taxPaid)} nominal)"
2117
+ dic["Total tax paid on gains and dividends"] = f"{u.d(taxPaidNow)}"
2118
+ dic["- Total tax paid on gains and dividends (nominal)"] = f"{u.d(taxPaid)}"
2103
2119
 
2104
2120
  taxPaid = np.sum(self.M_n, axis=0)
2105
2121
  taxPaidNow = np.sum(self.M_n / self.gamma_n[:-1], axis=0)
2106
- dic[f"Total Medicare premiums paid in {now}$"] = f"{u.d(taxPaidNow)} ({u.d(taxPaid)} nominal)"
2122
+ dic["Total Medicare premiums paid"] = f"{u.d(taxPaidNow)}"
2123
+ dic["- Total Medicare premiums paid (nominal)"] = f"{u.d(taxPaid)}"
2107
2124
 
2108
2125
  if self.N_i == 2 and self.n_d < self.N_n:
2109
2126
  p_j = self.partialEstate_j * (1 - self.phi_j)
@@ -2117,30 +2134,40 @@ class Plan(object):
2117
2134
  totSpousalNow = totSpousal / self.gamma_n[nx + 1]
2118
2135
  iname_s = self.inames[self.i_s]
2119
2136
  iname_d = self.inames[self.i_d]
2120
- dic[f"Spousal wealth transfer from {iname_d} to {iname_s} in year {ynx} (nominal)"] = (
2121
- f"taxable: {u.d(q_j[0])} tax-def: {u.d(q_j[1])} tax-free: {u.d(q_j[2])}")
2122
-
2123
- dic[f"Sum of spousal bequests to {iname_s} in year {ynx} in {now}$"] = (
2124
- f"{u.d(totSpousalNow)} ({u.d(totSpousal)} nominal)")
2125
- dic[
2126
- f"Post-tax non-spousal bequests from {iname_d} in year {ynx} (nominal)"] = (
2127
- f"taxable: {u.d(p_j[0])} tax-def: {u.d(p_j[1])} tax-free: {u.d(p_j[2])}")
2128
- dic[
2129
- f"Sum of post-tax non-spousal bequests from {iname_d} in year {ynx} in {now}$"] = (
2130
- f"{u.d(totOthersNow)} ({u.d(totOthers)} nominal)")
2137
+ dic[f"Sum of spousal transfer to {iname_s} in year {ynx}"] = (f"{u.d(totSpousalNow)}")
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)}")
2149
+ dic[f"- Sum of post-tax non-spousal bequests from {iname_d} in year {ynx} (nominal)"] = (
2150
+ f"{u.d(totOthers)}")
2151
+ dic[f"-- Post-tax non-spousal bequests from {iname_d} in year {ynx} - taxable (nominal)"] = (
2152
+ f"{u.d(p_j[0])}")
2153
+ dic[f"-- Post-tax non-spousal bequests from {iname_d} in year {ynx} - tax-def (nominal)"] = (
2154
+ f"{u.d(p_j[1])}")
2155
+ dic[f"-- Post-tax non-spousal bequests from {iname_d} in year {ynx} - tax-free (nominal)"] = (
2156
+ f"{u.d(p_j[2])}")
2131
2157
 
2132
2158
  estate = np.sum(self.b_ijn[:, :, self.N_n], axis=0)
2133
2159
  estate[1] *= 1 - self.nu
2134
2160
  lastyear = self.year_n[-1]
2135
- dic[f"Post-tax account values at the end of final plan year {lastyear} (nominal)"] = (
2136
- f"taxable: {u.d(estate[0])} tax-def: {u.d(estate[1])} tax-free: {u.d(estate[2])}")
2137
-
2138
2161
  totEstate = np.sum(estate)
2139
2162
  totEstateNow = totEstate / self.gamma_n[-1]
2140
- dic[f"Total estate value at the end of final plan year {lastyear} in {now}$"] = (
2141
- f"{u.d(totEstateNow)} ({u.d(totEstate)} nominal)")
2163
+ dic[f"Total estate value at the end of {lastyear}"] = (f"{u.d(totEstateNow)}")
2164
+ dic[f"- Total estate value at the end of {lastyear} (nominal)"] = (f"{u.d(totEstate)}")
2165
+ dic[f"-- Post-tax account value at the end of {lastyear} - taxable (nominal)"] = (f"{u.d(estate[0])}")
2166
+ dic[f"-- Post-tax account value at the end of {lastyear} - tax-def (nominal)"] = (f"{u.d(estate[1])}")
2167
+ dic[f"-- Post-tax account value at the end of {lastyear} - tax-free (nominal)"] = (f"{u.d(estate[2])}")
2168
+
2142
2169
  dic["Plan starting date"] = str(self.startDate)
2143
- dic["Cumulative inflation factor from start date to end of plan"] = 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}")
2144
2171
  for i in range(self.N_i):
2145
2172
  dic[f"{self.inames[i]:>12}'s {self.horizons[i]:02}-year life horizon"] = (
2146
2173
  f"{now} -> {now + self.horizons[i] - 1}")
owlplanner/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2025.02.25"
1
+ __version__ = "2025.03.07"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.2.25
3
+ Version: 2025.3.7
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 to provide guidance on retirement decisions. There are a few ways to run Owl.
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
 
@@ -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
 
@@ -2,16 +2,16 @@ owlplanner/__init__.py,sha256=QqrdT0Qks20osBTg7h0vJHAxpP9lL7DA99xb0nYbtw4,254
2
2
  owlplanner/abcapi.py,sha256=LbzW_KcNy0IeHp42MUHwGu_H67B2h_e1_vu-c2ACTkQ,6646
3
3
  owlplanner/config.py,sha256=bvqoOWrdPrWYQF9-PS_n00MoyZkAsmMOSczhTUFRGVU,12257
4
4
  owlplanner/logging.py,sha256=tYMw04O-XYSzjTj36fmKJGLcE1VkK6k6oJNeqtKXzuc,2530
5
- owlplanner/plan.py,sha256=cUXGyTgDGolGyLM8be7KhpJMpci8gGs0QTi4Aop89t0,113914
5
+ owlplanner/plan.py,sha256=KSQafUjDijtAFS-G6SK-0qrbML8MOOWZboHVkFwU-yk,115398
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=lWe1D4H8ZnLL1iNVPfsd_IJiZYc-uM_0FpLtePMlE8c,28
11
+ owlplanner/version.py,sha256=oFLGanJ_QJZi21IR_SCxLKDCLsykO3ijgdBcyw2kymg,28
12
12
  owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
14
- owlplanner-2025.2.25.dist-info/METADATA,sha256=8ioe1et4gjwsawDxV8U2BOqDhPTwQuS2ZsBhNm9_hdU,53506
15
- owlplanner-2025.2.25.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- owlplanner-2025.2.25.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
17
- owlplanner-2025.2.25.dist-info/RECORD,,
14
+ owlplanner-2025.3.7.dist-info/METADATA,sha256=BSvC46OldPiE6rDniinaJbgHwagtlXnTVFs1xTqlnGg,53799
15
+ owlplanner-2025.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ owlplanner-2025.3.7.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
17
+ owlplanner-2025.3.7.dist-info/RECORD,,