owlplanner 2025.2.15__tar.gz → 2025.2.20__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.20}/PKG-INFO +1 -1
  2. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/owl.pdf +0 -0
  3. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/owl.tex +15 -13
  4. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/pyproject.toml +1 -1
  5. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/plan.py +6 -5
  6. owlplanner-2025.2.20/src/owlplanner/version.py +1 -0
  7. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/tests/test_repro.py +11 -11
  8. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Quick_Start.py +3 -2
  9. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Rates_Selection.py +7 -6
  10. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/requirements.txt +1 -1
  11. owlplanner-2025.2.15/src/owlplanner/version.py +0 -1
  12. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/.devcontainer/devcontainer.json +0 -0
  13. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/.flake8 +0 -0
  14. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/.gitattributes +0 -0
  15. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/.github/workflows/github-actions-runtests.yml +0 -0
  16. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/.gitignore +0 -0
  17. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/INSTALL.md +0 -0
  18. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/LICENSE +0 -0
  19. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/README.md +0 -0
  20. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/USER_GUIDE.md +0 -0
  21. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docker/Dockerfile +0 -0
  22. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docker/README.md +0 -0
  23. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docker/docker-compose.yml +0 -0
  24. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docker/fastentrypoint.sh +0 -0
  25. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/AD-taxDef.png +0 -0
  26. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/AD-taxFree.png +0 -0
  27. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/AD-taxable.png +0 -0
  28. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/Hist_Bequest.png +0 -0
  29. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/Hist_Spending.png +0 -0
  30. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/MC-tutorial2a.png +0 -0
  31. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/MC-tutorial2b.png +0 -0
  32. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/OwlUI.png +0 -0
  33. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/allocations.png +0 -0
  34. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/owl.png +0 -0
  35. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/profile.png +0 -0
  36. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/ratesCorrelations.png +0 -0
  37. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/ratesPlot.png +0 -0
  38. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/savingsPlot.png +0 -0
  39. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/sourcesPlot.png +0 -0
  40. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/spendingPlot.png +0 -0
  41. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/taxIncomePlot.png +0 -0
  42. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/docs/images/taxesPlot.png +0 -0
  43. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/case_jack+jill.toml +0 -0
  44. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/case_joe.toml +0 -0
  45. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/case_john+sally.toml +0 -0
  46. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/case_kim+sam-bequest.toml +0 -0
  47. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/case_kim+sam-spending.toml +0 -0
  48. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/jack+jill.xlsx +0 -0
  49. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/joe.xlsx +0 -0
  50. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/john+sally.xlsx +0 -0
  51. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/examples/template.xlsx +0 -0
  52. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/notebooks/john+sally.ipynb +0 -0
  53. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/notebooks/kim+sam.ipynb +0 -0
  54. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/notebooks/template.ipynb +0 -0
  55. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/notebooks/tutorial_1.ipynb +0 -0
  56. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/notebooks/tutorial_2.ipynb +0 -0
  57. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/notebooks/tutorial_3.ipynb +0 -0
  58. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/owlplanner.cmd +0 -0
  59. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/owlplanner.sh +0 -0
  60. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/requirements.txt +0 -0
  61. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/__init__.py +0 -0
  62. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/abcapi.py +0 -0
  63. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/config.py +0 -0
  64. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/data/__init__.py +0 -0
  65. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/data/rates.csv +0 -0
  66. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/logging.py +0 -0
  67. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/progress.py +0 -0
  68. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/rates.py +0 -0
  69. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/tax2025.py +0 -0
  70. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/timelists.py +0 -0
  71. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/src/owlplanner/utils.py +0 -0
  72. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/tests/test_logger.py +0 -0
  73. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/tests/test_regressions.py +0 -0
  74. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/tests/test_toml_cases.py +0 -0
  75. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/tests/test_units.py +0 -0
  76. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ttt.py +0 -0
  77. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/About_Owl.py +0 -0
  78. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Asset_Allocation.py +0 -0
  79. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Assets.py +0 -0
  80. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Create_Case.py +0 -0
  81. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Documentation.py +0 -0
  82. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Fixed_Income.py +0 -0
  83. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Graphs.py +0 -0
  84. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Historical_Range.py +0 -0
  85. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Logs.py +0 -0
  86. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Monte_Carlo.py +0 -0
  87. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Optimization_Parameters.py +0 -0
  88. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Output_Files.py +0 -0
  89. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/README.md +0 -0
  90. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Settings.py +0 -0
  91. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Wages_And_Contributions.py +0 -0
  92. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/Worksheets.py +0 -0
  93. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/main+fonts.py +0 -0
  94. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/main.py +0 -0
  95. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/owlbridge.py +0 -0
  96. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/plots.py +0 -0
  97. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/progress.py +0 -0
  98. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/ui/sskeys.py +0 -0
  99. {owlplanner-2025.2.15 → owlplanner-2025.2.20}/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.20
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
@@ -160,7 +160,8 @@ Parameter values are either set by the user, historical data, or by the tax code
160
160
  \gamma_n = \prod_{n' = 0}^{n-1} (1 + \tau_{3n'}),
161
161
  \end{equation}
162
162
  with $\gamma_0 := 1$, and where $n'$ is a dummy index.
163
- As the time span of interest goes from the first year to the end of the last year,
163
+ As the time span of interest goes from the first year to the beginning
164
+ of the year following the last year,
164
165
  variable $\gamma_n$ will have $N_n + 1$ elements.
165
166
  Parameters indexed for inflation will be indicated by a bar on top as in $\bar{\sigma}_n$.
166
167
  \item [$\sigma_n$]
@@ -218,7 +219,7 @@ Parameter values are either set by the user, historical data, or by the tax code
218
219
  brackets are adjusted accordingly.
219
220
  \item [$\theta_{tn}$]
220
221
  Tax rate for tax bracket $t$ in year $n$. Using $N_t$ time series allows to adjust income
221
- tax rates in foreseeable future.
222
+ tax rates in the foreseeable future.
222
223
  For example, in 2024 the rates (in decimal) are .10, .12, .22, .24, .32, .35, and .37.
223
224
  It is speculated that the rates will revert back to 2017 rates in 2026 with
224
225
  .10, .15, .25, .28, .33, .35, and .396. See Eq.~(\ref{Eq:IncTax0}) for its use.
@@ -286,8 +287,8 @@ or an s-curve as in
286
287
  (e.g., sell a house, inheritance) or negative (e.g., buy a house, large gifts).
287
288
  \item [$\pi_{in}$]
288
289
  Sum of pension benefits for individual $i$ in year $n$. These amounts are typically
289
- specified along with the ages at which these benefits begin. Owl currently assumes
290
- that pensions are not indexed for inflation, but that functionality can easily be added.
290
+ specified along with the ages at which these benefits begin. Pensions
291
+ can optionally be indexed for inflation.
291
292
  \item [$\zeta_{in}$]
292
293
  Social security benefits for individual $i$ in year $n$. Starting age and the passing
293
294
  of one individual for spouses will determine the time series. $\bar{\zeta}_{in}$ is
@@ -298,7 +299,8 @@ or an s-curve as in
298
299
  for the heirs expressed in today's dollars. See parameter $\nu$ for the heirs tax rate.
299
300
  \item [$\kappa_{ijn}$]
300
301
  Sum of contributions to savings account $j$ made by individual $i$ during year $n$.
301
- We assume that contributions are made at half-year to balance regular contributions.
302
+ We assume that contributions are made at half-year to better represent periodic
303
+ contributions made throughout the year.
302
304
  In practice, a contribution
303
305
  amount $\kappa_{ijn}$ is specified in which case the contribution to each asset
304
306
  class is
@@ -309,7 +311,7 @@ or an s-curve as in
309
311
  Sum of wages obtained by individual $i$ during year $n$.
310
312
  Do not confuse wages $\omega$ with withdrawals $w$.
311
313
  \item [$\mu$]
312
- Dividend return rate in taxable accounts. Average is little above 2\% for S\&P 500.
314
+ Dividend return rate for equities in taxable accounts. Average is little above 2\% for S\&P 500.
313
315
  \item [$\nu$]
314
316
  Heirs income tax rate to be applied on the tax-deferred portion of the estate. This is not an estate tax
315
317
  but rather the federal income marginal tax rate for the heirs.
@@ -359,8 +361,8 @@ or an s-curve as in
359
361
  \label{Eq:IRMAA}
360
362
  \mathcal{M}_n = \sum_{iq} z_{iqn} \bar{C}_{qn}.
361
363
  \end{equation}
362
- If the plan does not have data from 2 years ago as Medicare starts,
363
- it will use last year's or this year's MAGI instead, in that order.
364
+ If the plan needs data from 1 or 2 years ago as Medicare has already started or will in the next years,
365
+ values for years before current year need to be provided.
364
366
 
365
367
  While this approach has been implemented and tested, the robustness of the {\em big M} approach
366
368
  is not guaranteed. Moreover, the introduction of a large number ($5\times N_i\times N_m$,
@@ -409,7 +411,7 @@ All intermediate variables are in uppercase letters.
409
411
 
410
412
  \item [$Q_n$]
411
413
  Qualified dividends and long-term capital gains obtained in year $n$.
412
- They only involve gains occurring in taxable savings accounts $(j=0)$ that
414
+ They only involve dividends occurring in the taxable savings accounts $(j=0)$ that
413
415
  were obtained from equities $(k=0)$, or sales of stocks due to withdrawals
414
416
  from taxable savings accounts.
415
417
  For simplicity, we assume that all equity sales only generate long-term capital gains and
@@ -422,10 +424,10 @@ All intermediate variables are in uppercase letters.
422
424
  A formulation where only a fraction of dividends are qualified can easily be
423
425
  implemented with the addition of another parameter.
424
426
  Notice that we are using return rates from the previous year.
425
- The first terms on the right-hand side represent the amount of equities $(k=0)$ in the $(j=0)$
426
- taxable savings account plus
427
- half the yearly contributions. The last terms account for withdrawals $w$ of equities assumed
428
- to have been purchased a year ago.
427
+ The first terms on the right-hand side represent dividends generated by
428
+ equities $(k=0)$ in the $(j=0)$ taxable savings account plus
429
+ half the yearly contributions. The second term account for withdrawals $w$
430
+ of equities assumed to have been purchased a year ago.
429
431
  It does not account for losses, but a market drop
430
432
  would most likely result in stock purchase rather than sale.
431
433
  For withdrawals, we make the assumption of
@@ -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.20"
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,11 +989,13 @@ 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
- self.piBar_in = self.pi_in
998
+ self.piBar_in = np.array(self.pi_in)
998
999
  for i in range(self.N_i):
999
1000
  if self.pensionIndexed[i]:
1000
1001
  self.piBar_in[i] *= self.gamma_n[:-1]
@@ -0,0 +1 @@
1
+ __version__ = "2025.02.20"
@@ -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)
@@ -24,8 +24,9 @@ This file is in *toml* format which is editable with a simple text editor.
24
24
  time table with anticipated wages, future contributions
25
25
  to savings accounts, and anticipated big-ticket items, which can be either expenses or income.
26
26
  This file is in Excel or LibreOffice format, and has one tab per individual in the plan.
27
+ If no file is provided, values will default to zero, but these values can be edited in the app.
27
28
 
28
- With these two files, a scenario can be created and solved in only a few steps. We will use the case
29
+ With these two files, a scenario can be created and solved with only a few steps. We will use the case
29
30
  of Jack and Jill provided here as an example:
30
31
  1) Download these two files from the GitHub repository
31
32
  (right-click on the link and select `Save link as...`):
@@ -46,7 +47,7 @@ experiment with different parameters.
46
47
 
47
48
  For creating your own cases, you can start
48
49
  from scratch by selecting `New Case...` in the selection box while on the **Create Case** page,
49
- and fill in the information needed on each page of the `Case Setup` section.
50
+ and fill in the information needed on each page in the `Case Setup` section.
50
51
  Once a case has been fully parameterized and successfully optimized,
51
52
  its parameters can be saved by using the `Download case file...` button on the `Output Files` page.
52
53
 
@@ -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.20
@@ -1 +0,0 @@
1
- __version__ = "2025.02.15"
File without changes
File without changes
File without changes
File without changes