owlplanner 2025.1.28__py3-none-any.whl → 2025.2__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
@@ -295,6 +295,9 @@ class Plan(object):
295
295
  self.myRothX_in = np.zeros((self.N_i, self.N_n))
296
296
  self.kappa_ijn = np.zeros((self.N_i, self.N_j, self.N_n))
297
297
 
298
+ # Previous 2 years for Medicare.
299
+ self.prevMAGI = np.zeros((2))
300
+
298
301
  # Scenario starts at the beginning of this year and ends at the end of the last year.
299
302
  self.mylog.vprint('Preparing scenario of %d years for %d individual%s.'
300
303
  % (self.N_n, self.N_i, ['', 's'][self.N_i - 1]))
@@ -403,8 +406,6 @@ class Plan(object):
403
406
  self.mylog.vprint('Renaming plan %s -> %s.' % (self._name, newname))
404
407
  self._name = newname
405
408
 
406
- return None
407
-
408
409
  def setSpousalDepositFraction(self, eta):
409
410
  """
410
411
  Set spousal deposit and withdrawal fraction. Default 0.5.
@@ -424,8 +425,6 @@ class Plan(object):
424
425
  self.mylog.vprint('\t%s: %.1f, %s: %.1f' % (self.inames[0], (1 - eta), self.inames[1], eta))
425
426
  self.eta = eta
426
427
 
427
- return None
428
-
429
428
  def setDefaultPlots(self, value):
430
429
  """
431
430
  Set plots between nominal values or today's $.
@@ -434,8 +433,6 @@ class Plan(object):
434
433
  self.defaultPlots = self._checkValue(value)
435
434
  self.mylog.vprint('Setting plots default value to %s.' % value)
436
435
 
437
- return None
438
-
439
436
  def setDividendRate(self, mu):
440
437
  """
441
438
  Set dividend rate on equities. Rate is in percent. Default 2%.
@@ -446,8 +443,6 @@ class Plan(object):
446
443
  self.mu = mu
447
444
  self.caseStatus = 'modified'
448
445
 
449
- return None
450
-
451
446
  def setLongTermCapitalTaxRate(self, psi):
452
447
  """
453
448
  Set long-term income tax rate. Rate is in percent. Default 15%.
@@ -458,8 +453,6 @@ class Plan(object):
458
453
  self.psi = psi
459
454
  self.caseStatus = 'modified'
460
455
 
461
- return None
462
-
463
456
  def setBeneficiaryFractions(self, phi):
464
457
  """
465
458
  Set fractions of savings accounts that is left to surviving spouse.
@@ -477,8 +470,6 @@ class Plan(object):
477
470
  self.mylog.vprint('Consider changing spousal deposit fraction for better convergence.')
478
471
  self.mylog.vprint('\tRecommended: setSpousalDepositFraction(%d)' % self.i_d)
479
472
 
480
- return None
481
-
482
473
  def setHeirsTaxRate(self, nu):
483
474
  """
484
475
  Set the heirs tax rate on the tax-deferred portion of the estate.
@@ -490,8 +481,6 @@ class Plan(object):
490
481
  self.nu = nu
491
482
  self.caseStatus = 'modified'
492
483
 
493
- return None
494
-
495
484
  def setPension(self, amounts, ages, units='k'):
496
485
  """
497
486
  Set value of pension for each individual and commencement age.
@@ -521,8 +510,6 @@ class Plan(object):
521
510
  self.pensionAges = np.array(ages, dtype=np.int32)
522
511
  self.caseStatus = 'modified'
523
512
 
524
- return None
525
-
526
513
  def setSocialSecurity(self, amounts, ages, units='k'):
527
514
  """
528
515
  Set value of social security for each individual and commencement age.
@@ -560,8 +547,6 @@ class Plan(object):
560
547
  self.caseStatus = 'modified'
561
548
  self._adjustedParameters = False
562
549
 
563
- return None
564
-
565
550
  def setSpendingProfile(self, profile, percent=60, dip=15, increase=12, delay=0):
566
551
  """
567
552
  Generate time series for spending profile. Surviving spouse fraction can be specified
@@ -589,8 +574,6 @@ class Plan(object):
589
574
  self.smileDelay = delay
590
575
  self.caseStatus = 'modified'
591
576
 
592
- return None
593
-
594
577
  def setRates(self, method, frm=None, to=None, values=None, stdev=None, corr=None):
595
578
  """
596
579
  Generate rates for return and inflation based on the method and
@@ -632,8 +615,6 @@ class Plan(object):
632
615
  self._adjustedParameters = False
633
616
  self.caseStatus = 'modified'
634
617
 
635
- return None
636
-
637
618
  def regenRates(self):
638
619
  """
639
620
  Regenerate the rates using the arguments specified during last setRates() call.
@@ -648,8 +629,6 @@ class Plan(object):
648
629
  corr=self.rateCorr,
649
630
  )
650
631
 
651
- return None
652
-
653
632
  def value(self, amount, year):
654
633
  """
655
634
  Return value of amount deflated or inflated at the beginning
@@ -711,8 +690,6 @@ class Plan(object):
711
690
  u.d(np.sum(taxable) + 0.7 * np.sum(taxDeferred) + np.sum(taxFree)),
712
691
  )
713
692
 
714
- return None
715
-
716
693
  def setInterpolationMethod(self, method, center=15, width=5):
717
694
  """
718
695
  Interpolate assets allocation ratios from initial value (today) to
@@ -739,8 +716,6 @@ class Plan(object):
739
716
 
740
717
  self.mylog.vprint('Asset allocation interpolation method set to %s.' % method)
741
718
 
742
- return None
743
-
744
719
  def setAllocationRatios(self, allocType, taxable=None, taxDeferred=None, taxFree=None, generic=None):
745
720
  """
746
721
  Single function for setting all types of asset allocations.
@@ -874,8 +849,6 @@ class Plan(object):
874
849
 
875
850
  self.mylog.vprint('Interpolating assets allocation ratios using', self.interpMethod, 'method.')
876
851
 
877
- return None
878
-
879
852
  def readContributions(self, filename):
880
853
  """
881
854
  Provide the name of the file containing the financial events
@@ -1636,6 +1609,7 @@ class Plan(object):
1636
1609
  'noRothConversions',
1637
1610
  'withMedicare',
1638
1611
  'solver',
1612
+ 'previousMAGIs',
1639
1613
  ]
1640
1614
  # We will modify options if required.
1641
1615
  if options is None:
@@ -1664,6 +1638,17 @@ class Plan(object):
1664
1638
  if objective == 'maxSpending' and 'bequest' not in myoptions:
1665
1639
  self.mylog.vprint('Using bequest of $1.')
1666
1640
 
1641
+ if 'previousMAGIs' in myoptions:
1642
+ magi = myoptions['previousMAGIs']
1643
+ if len(magi) != 2:
1644
+ raise ValueError("previousMAGIs must have two values.")
1645
+
1646
+ if 'units' in options:
1647
+ units = u.getUnits(options['units'])
1648
+ else:
1649
+ units = 1000
1650
+ self.prevMAGI = units * np.array(magi)
1651
+
1667
1652
  self._adjustParameters()
1668
1653
 
1669
1654
  if 'solver' in options:
@@ -1897,7 +1882,7 @@ class Plan(object):
1897
1882
  self.F_tn = self.F_tn.reshape((self.N_t, self.N_n))
1898
1883
  MAGI_n = np.sum(self.F_tn, axis=0) + np.array(x[self.C['e']:self.C['F']])
1899
1884
 
1900
- self.M_n = tx.mediCosts(self.yobs, self.horizons, MAGI_n, self.gamma_n[:-1], self.N_n)
1885
+ self.M_n = tx.mediCosts(self.yobs, self.horizons, MAGI_n, self.prevMAGI, self.gamma_n[:-1], self.N_n)
1901
1886
 
1902
1887
  return None
1903
1888
 
owlplanner/tax2025.py CHANGED
@@ -69,7 +69,7 @@ extra65Deduction_2025 = np.array([2000, 1600])
69
69
  ##############################################################################
70
70
 
71
71
 
72
- def mediCosts(yobs, horizons, magi, gamma_n, Nn):
72
+ def mediCosts(yobs, horizons, magi, prevmagi, gamma_n, Nn):
73
73
  """
74
74
  Compute Medicare costs directly.
75
75
  """
@@ -79,14 +79,14 @@ def mediCosts(yobs, horizons, magi, gamma_n, Nn):
79
79
  for n in range(Nn):
80
80
  for i in range(Ni):
81
81
  if thisyear + n - yobs[i] >= 65 and n < horizons[i]:
82
- # The standard Medicare part B premium
82
+ # Start with the (indexed) basic Medicare part B premium.
83
83
  costs[n] += gamma_n[n] * irmaaFees_2025[0]
84
84
  if n < 2:
85
- nn = n
85
+ mymagi = prevmagi[n]
86
86
  else:
87
- nn = 2
87
+ mymagi = magi[n - 2]
88
88
  for q in range(1, 6):
89
- if magi[n - nn] > gamma_n[n] * irmaaBrackets_2025[Ni - 1][q]:
89
+ if mymagi > gamma_n[n] * irmaaBrackets_2025[Ni - 1][q]:
90
90
  costs[n] += gamma_n[n] * irmaaFees_2025[q]
91
91
 
92
92
  return costs
owlplanner/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2025.1.28"
1
+ __version__ = "2025.02"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.1.28
3
+ Version: 2025.2
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=eemIsdbtzdWCIj5VuuswgphxXMcxJ_GZfUlDi6lttFM,6658
3
3
  owlplanner/config.py,sha256=ouADb6YES5Zgv0UwnEK9Axwvs8drp-ahboQjI4WTrr0,12069
4
4
  owlplanner/logging.py,sha256=pXg_mMgBll-kklqaDRLDNVUFo-5DAa-yqTKtiVrhNWw,2530
5
- owlplanner/plan.py,sha256=eRX04KT8DVkWD6sFzqm18OZZazSONQviuIYe7WNW7BM,115405
5
+ owlplanner/plan.py,sha256=hZhx-Dp_t1Lli2q2VdtXkM03qsdPdM7KcDu4wgAxDIE,115577
6
6
  owlplanner/progress.py,sha256=YZjL5_m4MMgKPlWlhhKacPLt54tVhVGF1eXxxZapMYs,386
7
7
  owlplanner/rates.py,sha256=aKOmau8i3uqxZGi7HQJpzooT3X-yAZhga5MZJ56pBzk,15627
8
- owlplanner/tax2025.py,sha256=W3yXKC3rgcqPjZMguOyejgsox9J42w3ogBNN1mIBHBI,6965
8
+ owlplanner/tax2025.py,sha256=b2RgM6TBQa8ggo6ODyh0p_J7j79UUm8z5NiENqa1l_k,7016
9
9
  owlplanner/timelists.py,sha256=ifxbyMlRW3IMwsiu8zsoodA1CKJQthgk3iPq50vQIds,4104
10
10
  owlplanner/utils.py,sha256=adIwqGVQFfvekke0JCxYJD3PKHbptVCj3NrQT2TQIB4,2351
11
- owlplanner/version.py,sha256=IsA0LMM6U46XS2G3Hhe2lWjxXVKcighNZH7REVFvu3M,27
11
+ owlplanner/version.py,sha256=1_pf7w0m_4k4eWcDSLOiWoM_lv54EH6pA9nuPETGRtk,25
12
12
  owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
14
- owlplanner-2025.1.28.dist-info/METADATA,sha256=ZLZXTjtEXy-eQaYHJbhKP9RfJcYf06jg95TIrr4Gpmk,64515
15
- owlplanner-2025.1.28.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- owlplanner-2025.1.28.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
17
- owlplanner-2025.1.28.dist-info/RECORD,,
14
+ owlplanner-2025.2.dist-info/METADATA,sha256=xII-8KNAoNNr_19KWjuS-9seZBzoBK4P9hEKLfcprto,64512
15
+ owlplanner-2025.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ owlplanner-2025.2.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
17
+ owlplanner-2025.2.dist-info/RECORD,,