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.
Files changed (99) hide show
  1. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/PKG-INFO +1 -1
  2. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/pyproject.toml +1 -1
  3. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/plan.py +5 -4
  4. owlplanner-2025.2.19/src/owlplanner/version.py +1 -0
  5. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_repro.py +11 -11
  6. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Rates_Selection.py +7 -6
  7. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/requirements.txt +1 -1
  8. owlplanner-2025.2.15/src/owlplanner/version.py +0 -1
  9. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.devcontainer/devcontainer.json +0 -0
  10. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.flake8 +0 -0
  11. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.gitattributes +0 -0
  12. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.github/workflows/github-actions-runtests.yml +0 -0
  13. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/.gitignore +0 -0
  14. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/INSTALL.md +0 -0
  15. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/LICENSE +0 -0
  16. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/README.md +0 -0
  17. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/USER_GUIDE.md +0 -0
  18. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/Dockerfile +0 -0
  19. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/README.md +0 -0
  20. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/docker-compose.yml +0 -0
  21. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docker/fastentrypoint.sh +0 -0
  22. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/AD-taxDef.png +0 -0
  23. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/AD-taxFree.png +0 -0
  24. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/AD-taxable.png +0 -0
  25. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/Hist_Bequest.png +0 -0
  26. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/Hist_Spending.png +0 -0
  27. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/MC-tutorial2a.png +0 -0
  28. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/MC-tutorial2b.png +0 -0
  29. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/OwlUI.png +0 -0
  30. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/allocations.png +0 -0
  31. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/owl.png +0 -0
  32. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/profile.png +0 -0
  33. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/ratesCorrelations.png +0 -0
  34. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/ratesPlot.png +0 -0
  35. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/savingsPlot.png +0 -0
  36. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/sourcesPlot.png +0 -0
  37. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/spendingPlot.png +0 -0
  38. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/taxIncomePlot.png +0 -0
  39. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/images/taxesPlot.png +0 -0
  40. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/owl.pdf +0 -0
  41. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/docs/owl.tex +0 -0
  42. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_jack+jill.toml +0 -0
  43. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_joe.toml +0 -0
  44. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_john+sally.toml +0 -0
  45. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_kim+sam-bequest.toml +0 -0
  46. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/case_kim+sam-spending.toml +0 -0
  47. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/jack+jill.xlsx +0 -0
  48. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/joe.xlsx +0 -0
  49. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/john+sally.xlsx +0 -0
  50. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/examples/template.xlsx +0 -0
  51. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/john+sally.ipynb +0 -0
  52. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/kim+sam.ipynb +0 -0
  53. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/template.ipynb +0 -0
  54. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/tutorial_1.ipynb +0 -0
  55. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/tutorial_2.ipynb +0 -0
  56. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/notebooks/tutorial_3.ipynb +0 -0
  57. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/owlplanner.cmd +0 -0
  58. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/owlplanner.sh +0 -0
  59. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/requirements.txt +0 -0
  60. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/__init__.py +0 -0
  61. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/abcapi.py +0 -0
  62. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/config.py +0 -0
  63. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/data/__init__.py +0 -0
  64. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/data/rates.csv +0 -0
  65. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/logging.py +0 -0
  66. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/progress.py +0 -0
  67. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/rates.py +0 -0
  68. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/tax2025.py +0 -0
  69. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/timelists.py +0 -0
  70. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/src/owlplanner/utils.py +0 -0
  71. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_logger.py +0 -0
  72. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_regressions.py +0 -0
  73. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_toml_cases.py +0 -0
  74. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/tests/test_units.py +0 -0
  75. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ttt.py +0 -0
  76. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/About_Owl.py +0 -0
  77. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Asset_Allocation.py +0 -0
  78. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Assets.py +0 -0
  79. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Create_Case.py +0 -0
  80. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Documentation.py +0 -0
  81. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Fixed_Income.py +0 -0
  82. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Graphs.py +0 -0
  83. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Historical_Range.py +0 -0
  84. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Logs.py +0 -0
  85. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Monte_Carlo.py +0 -0
  86. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Optimization_Parameters.py +0 -0
  87. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Output_Files.py +0 -0
  88. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Quick_Start.py +0 -0
  89. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/README.md +0 -0
  90. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Settings.py +0 -0
  91. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Wages_And_Contributions.py +0 -0
  92. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/Worksheets.py +0 -0
  93. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/main+fonts.py +0 -0
  94. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/main.py +0 -0
  95. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/owlbridge.py +0 -0
  96. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/plots.py +0 -0
  97. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/progress.py +0 -0
  98. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/sskeys.py +0 -0
  99. {owlplanner-2025.2.15 → owlplanner-2025.2.19}/ui/style.css +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.2.15
3
+ Version: 2025.2.19
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "owlplanner"
7
- version = "2025.02.15"
7
+ version = "2025.02.19"
8
8
  authors = [
9
9
  { name="Martin-D. Lacasse", email="martin.d.lacasse@gmail.com" },
10
10
  ]
@@ -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 income tax and RMD time series.
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(81918.4, abs=0.5)
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(592543, abs=0.5)
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(592543, abs=0.5)
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(592543, abs=0.5)
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(592543, abs=0.5)
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(592543, abs=0.5)
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(592543, abs=0.5)
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(592543, abs=0.5)
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(592543, abs=0.5)
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(92258, abs=0.5)
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(92258, abs=0.5)
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(2, gap="large", vertical_alignment="top")
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(2, gap="large", vertical_alignment="top")
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)
@@ -7,4 +7,4 @@ scipy
7
7
  streamlit
8
8
  toml
9
9
  # --extra-index-url https://test.pypi.org/simple
10
- owlplanner >= 2025.02.15
10
+ owlplanner >= 2025.02.19
@@ -1 +0,0 @@
1
- __version__ = "2025.02.15"
File without changes
File without changes
File without changes
File without changes