owlplanner 2025.3.13__tar.gz → 2025.3.14__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.
Files changed (108) hide show
  1. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/PKG-INFO +1 -1
  2. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/owl.tex +12 -2
  3. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/pyproject.toml +1 -1
  4. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/plan.py +21 -1
  5. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/utils.py +9 -2
  6. owlplanner-2025.3.14/src/owlplanner/version.py +1 -0
  7. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/tests/test_regressions.py +27 -27
  8. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Rates_Selection.py +2 -1
  9. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/requirements.txt +1 -1
  10. owlplanner-2025.3.13/src/owlplanner/version.py +0 -1
  11. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/.devcontainer/devcontainer.json +0 -0
  12. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/.flake8 +0 -0
  13. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/.gitattributes +0 -0
  14. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/.github/workflows/github-actions-runtests.yml +0 -0
  15. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/.gitignore +0 -0
  16. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/INSTALL.md +0 -0
  17. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/LICENSE +0 -0
  18. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/Papers/FE00006821-Class-VI-Injection-Permit--Salient-Features-and-Regulatory-Challenges_Final.pdf +0 -0
  19. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/Papers/Kou-OptionPricingDouble-2004.pdf +0 -0
  20. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/Papers/Multi-Period Mean Expected-Shortfall Strategies Cut Your Losses and Ride Your Gains .pdf +0 -0
  21. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/Papers/Optimal Asset Allocation for Retirement Saving Deterministic Vs. Time Consistent Adaptive Strategies.pdf +0 -0
  22. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/Papers/Rule-based_strategies_for_dynamic_life_cycle_inves.pdf +0 -0
  23. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/Papers/s10436-006-0062-y.pdf +0 -0
  24. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/README.md +0 -0
  25. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/USER_GUIDE.md +0 -0
  26. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docker/Dockerfile +0 -0
  27. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docker/README.md +0 -0
  28. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docker/docker-compose.yml +0 -0
  29. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docker/fastentrypoint.sh +0 -0
  30. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/AD-taxDef.png +0 -0
  31. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/AD-taxFree.png +0 -0
  32. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/AD-taxable.png +0 -0
  33. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/Hist_Bequest.png +0 -0
  34. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/Hist_Spending.png +0 -0
  35. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/MC-tutorial2a.png +0 -0
  36. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/MC-tutorial2b.png +0 -0
  37. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/OwlUI.png +0 -0
  38. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/allocations.png +0 -0
  39. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/owl.png +0 -0
  40. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/profile.png +0 -0
  41. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/ratesCorrelations.png +0 -0
  42. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/ratesPlot.png +0 -0
  43. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/savingsPlot.png +0 -0
  44. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/sourcesPlot.png +0 -0
  45. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/spendingPlot.png +0 -0
  46. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/taxIncomePlot.png +0 -0
  47. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/images/taxesPlot.png +0 -0
  48. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/docs/owl.pdf +0 -0
  49. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/case_jack+jill.toml +0 -0
  50. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/case_joe.toml +0 -0
  51. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/case_john+sally.toml +0 -0
  52. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/case_jon+jane.toml +0 -0
  53. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/case_kim+sam-bequest.toml +0 -0
  54. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/case_kim+sam-spending.toml +0 -0
  55. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/jack+jill.xlsx +0 -0
  56. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/joe.xlsx +0 -0
  57. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/john+sally.xlsx +0 -0
  58. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/jon+jane.xlsx +0 -0
  59. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/examples/template.xlsx +0 -0
  60. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/notebooks/john+sally.ipynb +0 -0
  61. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/notebooks/kim+sam.ipynb +0 -0
  62. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/notebooks/template.ipynb +0 -0
  63. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/notebooks/tutorial_1.ipynb +0 -0
  64. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/notebooks/tutorial_2.ipynb +0 -0
  65. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/notebooks/tutorial_3.ipynb +0 -0
  66. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/owlplanner.cmd +0 -0
  67. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/owlplanner.sh +0 -0
  68. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/requirements.txt +0 -0
  69. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/__init__.py +0 -0
  70. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/abcapi.py +0 -0
  71. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/config.py +0 -0
  72. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/data/__init__.py +0 -0
  73. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/data/rates.csv +0 -0
  74. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/logging.py +0 -0
  75. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/progress.py +0 -0
  76. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/rates.py +0 -0
  77. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/tax2025.py +0 -0
  78. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/src/owlplanner/timelists.py +0 -0
  79. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/tests/test_logger.py +0 -0
  80. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/tests/test_repro.py +0 -0
  81. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/tests/test_toml_cases.py +0 -0
  82. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/tests/test_units.py +0 -0
  83. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ttt.py +0 -0
  84. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/About_Owl.py +0 -0
  85. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Asset_Allocation.py +0 -0
  86. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Create_Case.py +0 -0
  87. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Current_Assets.py +0 -0
  88. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Documentation.py +0 -0
  89. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Fixed_Income.py +0 -0
  90. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Graphs.py +0 -0
  91. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Historical_Range.py +0 -0
  92. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Logs.py +0 -0
  93. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Monte_Carlo.py +0 -0
  94. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Optimization_Parameters.py +0 -0
  95. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Output_Files.py +0 -0
  96. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Quick_Start.py +0 -0
  97. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/README.md +0 -0
  98. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Settings.py +0 -0
  99. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Wages_And_Contributions.py +0 -0
  100. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/Worksheets.py +0 -0
  101. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/main+fonts.py +0 -0
  102. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/main.py +0 -0
  103. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/owlbridge.py +0 -0
  104. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/plots.py +0 -0
  105. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/progress.py +0 -0
  106. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/sskeys.py +0 -0
  107. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/style.css +0 -0
  108. {owlplanner-2025.3.13 → owlplanner-2025.3.14}/ui/tomlexamples.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.3.13
3
+ Version: 2025.3.14
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
@@ -91,7 +91,7 @@ index name as a subscript, e.g., $N_i$ for index $i$.
91
91
  the end of year $N_n-1$, and therefore $N_n + 1$ years are considered.
92
92
  Year $N_n$ is the first year following the passing of all
93
93
  individuals in the plan. The time period for all decision variables is annual.
94
- For spouses, the end of year $n_d-1$ is the year in which the first individual passes while
94
+ For spouses, the end of year $n_d-1$ is when the first individual is assumed to pass while
95
95
  the survivor will decease at the end of year $N_n-1$ of the plan.
96
96
  \item [$t$]
97
97
  Federal income tax bracket. $t$ goes from 0 to $N_t - 1$, from low to high.
@@ -441,8 +441,18 @@ All intermediate variables are in uppercase letters.
441
441
  paid in each tax bracket as
442
442
  \begin{equation}
443
443
  \label{Eq:IncTax0}
444
- T_n = \sum_t f_{tn}\bar{\Delta}_{tn}\theta_{tn}.
444
+ T_n = \sum_t f_{tn}\bar{\Delta}_{tn}\theta_{tn} + 0.10 \sum_i (1 - \mathcal{H}(n - n_{i,60})) w_{i1n},
445
445
  \end{equation}
446
+ where $H(n - n_{i, 60})$ is a Heavyside step function which is 0 or 1, depending on the sign of
447
+ its argument:
448
+ \[
449
+ \mathcal{H}(x) \def
450
+ \begin{cases}
451
+ 0 & x < 0 \\
452
+ 1 & x \geq 0. \\
453
+ \end{cases}
454
+ \]
455
+ Here, $n_{i, 60}$ is the year when individual $i$ will turn 60.
446
456
  Notice that $G_n$ is also defined by Eq.~(\ref{Eq:Tx1}), and that optimal
447
457
  values of $f_{tn}$ have to
448
458
  minimize $T_n$ when either the bequest or the desired net spending are maximized.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "owlplanner"
7
- version = "2025.03.13"
7
+ version = "2025.03.14"
8
8
  authors = [
9
9
  { name="Martin-D. Lacasse", email="martin.d.lacasse@gmail.com" },
10
10
  ]
@@ -266,6 +266,9 @@ class Plan(object):
266
266
  # self.horizons = [yobs[i] + expectancy[i] - thisyear + 1 for i in range(self.N_i)]
267
267
  self.N_n = np.max(self.horizons)
268
268
  self.year_n = np.linspace(thisyear, thisyear + self.N_n - 1, self.N_n, dtype=np.int32)
269
+ # Year in the plan (if any) where individuals turn 59. For 10% withdrawal penalty.
270
+ self.n59 = 59 - thisyear + self.yobs
271
+ self.n59[self.n59 < 0] = 0
269
272
  # Handle passing of one spouse before the other.
270
273
  if self.N_i == 2 and np.min(self.horizons) != np.max(self.horizons):
271
274
  self.n_d = np.min(self.horizons)
@@ -1289,6 +1292,11 @@ class Plan(object):
1289
1292
  for t in range(Nt):
1290
1293
  row.addElem(_q2(CF, t, n, Nt, Nn), self.theta_tn[t, n])
1291
1294
 
1295
+ # Minus 10% penalty on early withdrawals.
1296
+ if n < self.n59[i]:
1297
+ row.addElem(_q3(Cw, i, 1, n, Ni, Nj, Nn), 0.1)
1298
+ row.addElem(_q3(Cw, i, 2, n, Ni, Nj, Nn), 0.1)
1299
+
1292
1300
  A.addRow(row, rhs, rhs)
1293
1301
 
1294
1302
  # Impose income profile.
@@ -1970,6 +1978,12 @@ class Plan(object):
1970
1978
  self.G_n = np.sum(self.F_tn, axis=0)
1971
1979
  self.T_tn = self.F_tn * self.theta_tn
1972
1980
  self.T_n = np.sum(self.T_tn, axis=0)
1981
+ self.penalty_n = np.zeros(Nn)
1982
+ # Add early withdrawal penalties if any.
1983
+ for i in range(Ni):
1984
+ self.penalty_n[0:self.n59[i]] += 0.1*(self.w_ijn[i, 1, 0:self.n59[i]] + self.w_ijn[i, 2, 0:self.n59[i]])
1985
+
1986
+ self.T_n += self.penalty_n
1973
1987
 
1974
1988
  tau_0 = np.array(self.tau_kn[0, :])
1975
1989
  tau_0[tau_0 < 0] = 0
@@ -2112,6 +2126,11 @@ class Plan(object):
2112
2126
  dic[f"-- Subtotal in tax bracket {tname}"] = f"{u.d(taxPaidNow)}"
2113
2127
  dic[f"-- [Subtotal in tax bracket {tname}]"] = f"{u.d(taxPaid)}"
2114
2128
 
2129
+ penaltyPaid = np.sum(self.penalty_n, axis=0)
2130
+ penaltyPaidNow = np.sum(self.penalty_n / self.gamma_n[:-1], axis=0)
2131
+ dic["-- Subtotal in early withdrawal penalty"] = f"{u.d(penaltyPaidNow)}"
2132
+ dic["-- [Subtotal in early withdrawal penalty]"] = f"{u.d(penaltyPaid)}"
2133
+
2115
2134
  taxPaid = np.sum(self.U_n, axis=0)
2116
2135
  taxPaidNow = np.sum(self.U_n / self.gamma_n[:-1], axis=0)
2117
2136
  dic["Total tax paid on gains and dividends"] = f"{u.d(taxPaidNow)}"
@@ -2507,6 +2526,7 @@ class Plan(object):
2507
2526
 
2508
2527
  title = self._name + "\nRaw Income Sources"
2509
2528
  stypes = self.sources_in.keys()
2529
+ # stypes = [item for item in stypes if "RothX" not in item]
2510
2530
 
2511
2531
  if tag != "":
2512
2532
  title += " - " + tag
@@ -2517,7 +2537,7 @@ class Plan(object):
2517
2537
  else:
2518
2538
  yformat = "\\$k (" + str(self.year_n[0]) + "\\$)"
2519
2539
  sources_in = {}
2520
- for key in self.sources_in:
2540
+ for key in stypes:
2521
2541
  sources_in[key] = self.sources_in[key] / self.gamma_n[:-1]
2522
2542
 
2523
2543
  fig, ax = _stackPlot(
@@ -70,8 +70,8 @@ def getUnits(units) -> int:
70
70
  return fac
71
71
 
72
72
 
73
- # Could be a one-line lambda function:
74
- # krond = lambda a, b: 1 if a == b else 0
73
+ # Next two functins could be a one-line lambda functions.
74
+ # e.g., krond = lambda a, b: 1 if a == b else 0
75
75
  def krond(a, b) -> int:
76
76
  """
77
77
  Kronecker integer delta function.
@@ -79,6 +79,13 @@ def krond(a, b) -> int:
79
79
  return 1 if a == b else 0
80
80
 
81
81
 
82
+ def heavyside(x) -> int:
83
+ """
84
+ Heavyside step function.
85
+ """
86
+ return 1 if x >= 0 else 0
87
+
88
+
82
89
  def roundCents(values, decimals=2):
83
90
  """
84
91
  Round values in NumPy array down to second decimal.
@@ -0,0 +1 @@
1
+ __version__ = "2025.03.14"
@@ -89,7 +89,7 @@ def createPlan(ni, name, ny, topAge):
89
89
 
90
90
  def test_withdrawal1():
91
91
  n = 10
92
- p = createPlan(1, 'withdrawal1', n, 64)
92
+ p = createPlan(1, 'withdrawal1', n, 70)
93
93
  amount = 3.0
94
94
  p.setAccountBalances(taxable=[0], taxDeferred=[amount], taxFree=[0])
95
95
  p.setAllocationRatios('individual', generic=[[[0, 0, 0, 100], [0, 0, 0, 100]]])
@@ -102,7 +102,7 @@ def test_withdrawal1():
102
102
 
103
103
  def test_withdrawal2():
104
104
  n = 10
105
- p = createPlan(1, 'withdrawal2', n, 64)
105
+ p = createPlan(1, 'withdrawal2', n, 70)
106
106
  # Small taxable income creates an income smaller than standard deduction. Testing e_n.
107
107
  amount = 40.0
108
108
  p.setAccountBalances(taxable=[0], taxDeferred=[amount], taxFree=[0])
@@ -116,7 +116,7 @@ def test_withdrawal2():
116
116
 
117
117
  def test_withdrawal2_2():
118
118
  n = 10
119
- p = createPlan(2, 'withdrawal2_2', n, 64)
119
+ p = createPlan(2, 'withdrawal2_2', n, 70)
120
120
  # Small taxable income creates an income smaller than standard deduction. Testing e_n.
121
121
  amount = 50
122
122
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[amount/2, amount/2], taxFree=[0, 0])
@@ -130,7 +130,7 @@ def test_withdrawal2_2():
130
130
 
131
131
  def test_withdrawal3():
132
132
  n = 6
133
- p = createPlan(1, 'withdrawal3', n, 64)
133
+ p = createPlan(1, 'withdrawal3', n, 70)
134
134
  amount = 60
135
135
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
136
136
  p.setAllocationRatios('individual', generic=[[[0, 0, 0, 100], [0, 0, 0, 100]]])
@@ -143,7 +143,7 @@ def test_withdrawal3():
143
143
 
144
144
  def test_withdrawal3_2():
145
145
  n = 6
146
- p = createPlan(2, 'withdrawal3', n, 64)
146
+ p = createPlan(2, 'withdrawal3', n, 70)
147
147
  amount = 60
148
148
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
149
149
  p.setAllocationRatios('spouses', generic=[[0, 0, 0, 100], [0, 0, 0, 100]])
@@ -156,7 +156,7 @@ def test_withdrawal3_2():
156
156
 
157
157
  def test_taxfreegrowth1():
158
158
  n = 12
159
- p = createPlan(1, 'taxfreegrowth1', n, 64)
159
+ p = createPlan(1, 'taxfreegrowth1', n, 72)
160
160
  amount = 120
161
161
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
162
162
  p.setAllocationRatios('individual', generic=[[[0, 0, 100, 0], [0, 0, 100, 0]]])
@@ -170,7 +170,7 @@ def test_taxfreegrowth1():
170
170
 
171
171
  def test_taxfreegrowth1_2():
172
172
  n = 12
173
- p = createPlan(2, 'taxfreegrowth1', n, 64)
173
+ p = createPlan(2, 'taxfreegrowth1', n, 72)
174
174
  amount = 120
175
175
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
176
176
  p.setAllocationRatios('spouses', generic=[[0, 0, 100, 0], [0, 0, 100, 0]])
@@ -184,7 +184,7 @@ def test_taxfreegrowth1_2():
184
184
 
185
185
  def test_taxfreegrowth2():
186
186
  n = 15
187
- p = createPlan(1, 'taxfreegrowth2', n, 64)
187
+ p = createPlan(1, 'taxfreegrowth2', n, 75)
188
188
  amount = 120
189
189
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
190
190
  p.setAllocationRatios('individual', generic=[[[0, 50, 50, 0], [0, 50, 50, 0]]])
@@ -198,7 +198,7 @@ def test_taxfreegrowth2():
198
198
 
199
199
  def test_taxfreegrowth2_2():
200
200
  n = 15
201
- p = createPlan(2, 'taxfreegrowth2', n, 64)
201
+ p = createPlan(2, 'taxfreegrowth2', n, 75)
202
202
  amount = 120
203
203
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
204
204
  p.setAllocationRatios('spouses', generic=[[0, 50, 50, 0], [0, 50, 50, 0]])
@@ -212,7 +212,7 @@ def test_taxfreegrowth2_2():
212
212
 
213
213
  def test_taxfreegrowth3():
214
214
  n = 15
215
- p = createPlan(1, 'taxfreegrowth3', n, 64)
215
+ p = createPlan(1, 'taxfreegrowth3', n, 75)
216
216
  amount = 120
217
217
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
218
218
  p.setAllocationRatios('individual', generic=[[[50, 50, 0, 0], [50, 50, 0, 0]]])
@@ -226,7 +226,7 @@ def test_taxfreegrowth3():
226
226
 
227
227
  def test_taxfreegrowth3_2():
228
228
  n = 15
229
- p = createPlan(2, 'taxfreegrowth3', n, 64)
229
+ p = createPlan(2, 'taxfreegrowth3', n, 75)
230
230
  amount = 120
231
231
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
232
232
  p.setAllocationRatios('spouses', generic=[[50, 50, 0, 0], [50, 50, 0, 0]])
@@ -240,7 +240,7 @@ def test_taxfreegrowth3_2():
240
240
 
241
241
  def test_taxfreegrowth4():
242
242
  n = 16
243
- p = createPlan(1, 'taxfreegrowth4', n, 64)
243
+ p = createPlan(1, 'taxfreegrowth4', n, 76)
244
244
  amount = 120
245
245
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
246
246
  p.setAllocationRatios('individual', generic=[[[0, 50, 50, 0], [0, 50, 50, 0]]])
@@ -255,7 +255,7 @@ def test_taxfreegrowth4():
255
255
 
256
256
  def test_taxfreegrowth4_2():
257
257
  n = 16
258
- p = createPlan(2, 'taxfreegrowth4', n, 64)
258
+ p = createPlan(2, 'taxfreegrowth4', n, 76)
259
259
  amount = 120
260
260
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
261
261
  p.setAllocationRatios('spouses', generic=[[0, 50, 50, 0], [0, 50, 50, 0]])
@@ -270,7 +270,7 @@ def test_taxfreegrowth4_2():
270
270
 
271
271
  def test_taxfreegrowth5():
272
272
  n = 15
273
- p = createPlan(1, 'taxfreegrowth5', n, 64)
273
+ p = createPlan(1, 'taxfreegrowth5', n, 76)
274
274
  amount = 120
275
275
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
276
276
  p.setAllocationRatios('individual', generic=[[[0, 0, 100, 0], [0, 0, 100, 0]]])
@@ -284,7 +284,7 @@ def test_taxfreegrowth5():
284
284
 
285
285
  def test_taxfreegrowth5_2():
286
286
  n = 15
287
- p = createPlan(2, 'taxfreegrowth5', n, 64)
287
+ p = createPlan(2, 'taxfreegrowth5', n, 76)
288
288
  amount = 120
289
289
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
290
290
  p.setAllocationRatios('spouses', generic=[[0, 0, 100, 0], [0, 0, 100, 0]])
@@ -298,7 +298,7 @@ def test_taxfreegrowth5_2():
298
298
 
299
299
  def test_taxfreegrowth6():
300
300
  n = 15
301
- p = createPlan(1, 'taxfreegrowth6', n, 64)
301
+ p = createPlan(1, 'taxfreegrowth6', n, 76)
302
302
  amount = 120
303
303
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
304
304
  p.setAllocationRatios('individual', generic=[[[0, 0, 0, 100], [0, 0, 0, 100]]])
@@ -312,7 +312,7 @@ def test_taxfreegrowth6():
312
312
 
313
313
  def test_taxfreegrowth6_2():
314
314
  n = 15
315
- p = createPlan(2, 'taxfreegrowth6', n, 64)
315
+ p = createPlan(2, 'taxfreegrowth6', n, 76)
316
316
  amount = 120
317
317
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
318
318
  p.setAllocationRatios('spouses', generic=[[0, 0, 0, 100], [0, 0, 0, 100]])
@@ -326,7 +326,7 @@ def test_taxfreegrowth6_2():
326
326
 
327
327
  def test_taxfreegrowth7():
328
328
  n = 15
329
- p = createPlan(1, 'taxfreegrowth7', n, 64)
329
+ p = createPlan(1, 'taxfreegrowth7', n, 76)
330
330
  amount = 120
331
331
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
332
332
  p.setAllocationRatios('individual', generic=[[[0, 100, 0, 0], [0, 100, 0, 0]]])
@@ -340,7 +340,7 @@ def test_taxfreegrowth7():
340
340
 
341
341
  def test_taxfreegrowth7_2():
342
342
  n = 15
343
- p = createPlan(2, 'taxfreegrowth7', n, 64)
343
+ p = createPlan(2, 'taxfreegrowth7', n, 76)
344
344
  amount = 120
345
345
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
346
346
  p.setAllocationRatios('spouses', generic=[[0, 100, 0, 0], [0, 100, 0, 0]])
@@ -354,7 +354,7 @@ def test_taxfreegrowth7_2():
354
354
 
355
355
  def test_taxfreegrowth8():
356
356
  n = 15
357
- p = createPlan(1, 'taxfreegrowth8', n, 64)
357
+ p = createPlan(1, 'taxfreegrowth8', n, 76)
358
358
  amount = 120
359
359
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
360
360
  p.setAllocationRatios('individual', generic=[[[100, 0, 0, 0], [100, 0, 0, 0]]])
@@ -368,7 +368,7 @@ def test_taxfreegrowth8():
368
368
 
369
369
  def test_taxfreegrowth8_2():
370
370
  n = 15
371
- p = createPlan(2, 'taxfreegrowth8', n, 64)
371
+ p = createPlan(2, 'taxfreegrowth8', n, 76)
372
372
  amount = 120
373
373
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
374
374
  p.setAllocationRatios('spouses', generic=[[100, 0, 0, 0], [100, 0, 0, 0]])
@@ -382,7 +382,7 @@ def test_taxfreegrowth8_2():
382
382
 
383
383
  def test_annuity1():
384
384
  n = 12
385
- p = createPlan(1, 'annuity1', n, 64)
385
+ p = createPlan(1, 'annuity1', n, 76)
386
386
  amount = 120
387
387
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
388
388
  p.setAllocationRatios('individual', generic=[[[0, 0, 100, 0], [0, 0, 100, 0]]])
@@ -400,7 +400,7 @@ def test_annuity1():
400
400
 
401
401
  def test_annuity1_2():
402
402
  n = 12
403
- p = createPlan(2, 'annuity1', n, 64)
403
+ p = createPlan(2, 'annuity1', n, 76)
404
404
  amount = 120
405
405
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
406
406
  p.setAllocationRatios('spouses', generic=[[0, 0, 100, 0], [0, 0, 100, 0]])
@@ -418,7 +418,7 @@ def test_annuity1_2():
418
418
 
419
419
  def test_annuity2():
420
420
  n = 18
421
- p = createPlan(1, 'annuity2', n, 64)
421
+ p = createPlan(1, 'annuity2', n, 76)
422
422
  amount = 120
423
423
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
424
424
  p.setAllocationRatios('individual', generic=[[[0, 0, 100, 0], [0, 0, 100, 0]]])
@@ -436,7 +436,7 @@ def test_annuity2():
436
436
 
437
437
  def test_annuity2_2():
438
438
  n = 18
439
- p = createPlan(2, 'annuity2', n, 64)
439
+ p = createPlan(2, 'annuity2', n, 78)
440
440
  amount = 120
441
441
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
442
442
  p.setAllocationRatios('spouses', generic=[[0, 0, 100, 0], [0, 0, 100, 0]])
@@ -454,7 +454,7 @@ def test_annuity2_2():
454
454
 
455
455
  def test_annuity3():
456
456
  n = 30
457
- p = createPlan(1, 'annuity2', n, 64)
457
+ p = createPlan(1, 'annuity2', n, 90)
458
458
  amount = 100
459
459
  p.setAccountBalances(taxable=[0], taxDeferred=[0], taxFree=[amount])
460
460
  p.setAllocationRatios('individual', generic=[[[0, 0, 100, 0], [0, 0, 100, 0]]])
@@ -472,7 +472,7 @@ def test_annuity3():
472
472
 
473
473
  def test_annuity3_2():
474
474
  n = 30
475
- p = createPlan(2, 'annuity2', n, 64)
475
+ p = createPlan(2, 'annuity2', n, 90)
476
476
  amount = 100
477
477
  p.setAccountBalances(taxable=[0, 0], taxDeferred=[0, 0], taxFree=[amount/2, amount/2])
478
478
  p.setAllocationRatios('spouses', generic=[[0, 0, 100, 0], [0, 0, 100, 0]])
@@ -93,10 +93,11 @@ else:
93
93
 
94
94
  col1, col2, col3, col4 = st.columns(4, gap="large", vertical_alignment="top")
95
95
  with col1:
96
+ maxValue = owb.TO if kz.getKey("varyingType") == "historical" else kz.getKey("yto") - 1
96
97
  st.number_input(
97
98
  "Starting year",
98
99
  min_value=owb.FROM,
99
- max_value=kz.getKey("yto") - 1,
100
+ max_value=maxValue,
100
101
  value=kz.getKey("yfrm"),
101
102
  on_change=updateRates,
102
103
  args=["yfrm"],
@@ -7,4 +7,4 @@ scipy
7
7
  streamlit
8
8
  toml
9
9
  # --extra-index-url https://test.pypi.org/simple
10
- owlplanner >= 2025.03.13
10
+ owlplanner >= 2025.03.14
@@ -1 +0,0 @@
1
- __version__ = "2025.03.13"
File without changes
File without changes
File without changes
File without changes