owlplanner 2025.3.16__py3-none-any.whl → 2025.3.30__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
@@ -307,6 +307,9 @@ class Plan(object):
307
307
  # Previous 2 years for Medicare.
308
308
  self.prevMAGI = np.zeros((2))
309
309
 
310
+ # Default slack on profile.
311
+ self.lambdha = 0
312
+
310
313
  # Scenario starts at the beginning of this year and ends at the end of the last year.
311
314
  s = ["", "s"][self.N_i - 1]
312
315
  self.mylog.vprint(f"Preparing scenario of {self.N_n} years for {self.N_i} individual{s}.")
@@ -1071,6 +1074,9 @@ class Plan(object):
1071
1074
  Cx = self.C["x"]
1072
1075
  Cz = self.C["z"]
1073
1076
 
1077
+ spLo = 1 - self.lambdha
1078
+ spHi = 1 + self.lambdha
1079
+
1074
1080
  tau_ijn = np.zeros((Ni, Nj, Nn))
1075
1081
  for i in range(Ni):
1076
1082
  for j in range(Nj):
@@ -1191,7 +1197,8 @@ class Plan(object):
1191
1197
  # Account for time elapsed in the current year.
1192
1198
  spending *= units * self.yearFracLeft
1193
1199
  # self.mylog.vprint('Maximizing bequest with desired net spending of:', u.d(spending))
1194
- A.addNewRow({_q1(Cg, 0): 1}, spending, spending)
1200
+ # To allow slack in first year, Cg can be made Nn+1 and store basis in g[Nn].
1201
+ A.addNewRow({_q1(Cg, 0, Nn): 1}, spending, spending)
1195
1202
 
1196
1203
  # Set initial balances through constraints.
1197
1204
  for i in range(Ni):
@@ -1297,8 +1304,10 @@ class Plan(object):
1297
1304
 
1298
1305
  # Impose income profile.
1299
1306
  for n in range(1, Nn):
1300
- rowDic = {_q1(Cg, 0, Nn): -self.xiBar_n[n], _q1(Cg, n, Nn): self.xiBar_n[0]}
1301
- A.addNewRow(rowDic, zero, zero)
1307
+ rowDic = {_q1(Cg, 0, Nn): -spLo * self.xiBar_n[n], _q1(Cg, n, Nn): self.xiBar_n[0]}
1308
+ A.addNewRow(rowDic, zero, inf)
1309
+ rowDic = {_q1(Cg, 0, Nn): spHi * self.xiBar_n[n], _q1(Cg, n, Nn): -self.xiBar_n[0]}
1310
+ A.addNewRow(rowDic, zero, inf)
1302
1311
 
1303
1312
  # Taxable ordinary income.
1304
1313
  for n in range(Nn):
@@ -1362,7 +1371,9 @@ class Plan(object):
1362
1371
  # Now build a solver-neutral objective vector.
1363
1372
  c = abc.Objective(self.nvars)
1364
1373
  if objective == "maxSpending":
1365
- c.setElem(_q1(Cg, 0, Nn), -1)
1374
+ # c.setElem(_q1(Cg, 0, Nn), -1) # Only OK in implemention without slack.
1375
+ for n in range(Nn):
1376
+ c.setElem(_q1(Cg, n, Nn), -1/self.gamma_n[n])
1366
1377
  elif objective == "maxBequest":
1367
1378
  for i in range(Ni):
1368
1379
  c.setElem(_q3(Cb, i, 0, Nn, Ni, Nj, Nn + 1), -1)
@@ -1608,6 +1619,7 @@ class Plan(object):
1608
1619
  "units",
1609
1620
  "maxRothConversion",
1610
1621
  "netSpending",
1622
+ "spendingSlack",
1611
1623
  "bequest",
1612
1624
  "bigM",
1613
1625
  "noRothConversions",
@@ -1642,6 +1654,7 @@ class Plan(object):
1642
1654
  if objective == "maxSpending" and "bequest" not in myoptions:
1643
1655
  self.mylog.vprint("Using bequest of $1.")
1644
1656
 
1657
+ self.prevMAGI = np.zeros(2)
1645
1658
  if "previousMAGIs" in myoptions:
1646
1659
  magi = myoptions["previousMAGIs"]
1647
1660
  if len(magi) != 2:
@@ -1653,6 +1666,13 @@ class Plan(object):
1653
1666
  units = 1000
1654
1667
  self.prevMAGI = units * np.array(magi)
1655
1668
 
1669
+ self.lambdha = 0
1670
+ if "spendingSlack" in myoptions:
1671
+ lambdha = myoptions["spendingSlack"]
1672
+ if lambdha < 0 or lambdha > 50:
1673
+ raise ValueError(f"Slack value out of range {lambdha}.")
1674
+ self.lambdha = lambdha / 100
1675
+
1656
1676
  self._adjustParameters()
1657
1677
 
1658
1678
  if "solver" in options:
owlplanner/tax2025.py CHANGED
@@ -36,7 +36,7 @@ rates_nonTCJA = np.array([0.10, 0.15, 0.25, 0.28, 0.33, 0.35, 0.396])
36
36
  taxBrackets_TCJA = np.array(
37
37
  [
38
38
  [11925, 48475, 103350, 197300, 250525, 626350, 9999999],
39
- [23850, 96950, 206700, 394600, 501050, 751700, 9999999],
39
+ [23850, 96950, 206700, 394600, 501050, 751600, 9999999],
40
40
  ]
41
41
  )
42
42
 
owlplanner/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2025.03.16"
1
+ __version__ = "2025.03.30"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.3.16
3
+ Version: 2025.3.30
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
@@ -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=uJ6jZ9Rx2CB2P5cFRscsUCXOW6uml7I4pta2ozjW0uo,12263
4
4
  owlplanner/logging.py,sha256=tYMw04O-XYSzjTj36fmKJGLcE1VkK6k6oJNeqtKXzuc,2530
5
- owlplanner/plan.py,sha256=SWTqNMC0Mk2BUO3CBeeyBaz_cCbLddlSgPJaIj2Cc-I,116090
5
+ owlplanner/plan.py,sha256=Y3CDTI4T9hXJ3Epn64eox3n__yY5QdB6uk3lkj5oAsk,116953
6
6
  owlplanner/progress.py,sha256=8jlCvvtgDI89zXVNMBg1-lnEyhpPvKQS2X5oAIpoOVQ,384
7
7
  owlplanner/rates.py,sha256=gJaoe-gJqWCQV5qVLlHp-Yn9TSJs-PJzeTbOwMCbqWs,15682
8
- owlplanner/tax2025.py,sha256=B-A5eU3wxdcAaxRCbT3qI-JEKoD_ZeNbg_86XhNdQEI,7745
8
+ owlplanner/tax2025.py,sha256=HEXfL0HfwUvZOQRjivXO2jFeoVZ5m_yk_hoMiVi-hR0,7745
9
9
  owlplanner/timelists.py,sha256=tYieZU67FT6TCcQQis36JaXGI7dT6NqD7RvdEjgJL4M,4026
10
10
  owlplanner/utils.py,sha256=WpJgn79YZfH8UCkcmhd-AZlxlGuz1i1-UDBRXImsY6I,2485
11
- owlplanner/version.py,sha256=Jcl8LadUqoYB6SO4YGpB9C3ZbDxgmTQBNGqCWqKkUzE,28
11
+ owlplanner/version.py,sha256=fEQSK7uDPSbqsQ3DPZ39_7pwhTGTqWEEoeAsff3bjKU,28
12
12
  owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
14
- owlplanner-2025.3.16.dist-info/METADATA,sha256=C1iW41089-MQsqEn1cp1-L7kTcwQWnSKGgGOLiZdt-g,53801
15
- owlplanner-2025.3.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- owlplanner-2025.3.16.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
17
- owlplanner-2025.3.16.dist-info/RECORD,,
14
+ owlplanner-2025.3.30.dist-info/METADATA,sha256=_8LSN7_z0_80_0IL37XAcDRRDKCm42qtcJtaQ3J9qK4,53801
15
+ owlplanner-2025.3.30.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ owlplanner-2025.3.30.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
17
+ owlplanner-2025.3.30.dist-info/RECORD,,