owlplanner 2025.1.28__py3-none-any.whl → 2025.1.29__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]))
@@ -394,6 +397,16 @@ class Plan(object):
394
397
 
395
398
  return None
396
399
 
400
+ def setPreviousMAGI(self, magi, units='k'):
401
+ """
402
+ Set MAGI for two previous years to the plan. Values are in nominal $k.
403
+ """
404
+ assert len(magi) == 2, "MAGI must have two values."
405
+ fac = u.getUnits(units)
406
+ u.rescale(magi, fac)
407
+ self.mylog.vprint('Setting previous years MAGI to:', [u.d(magi[i]) for i in range(2)])
408
+ self.prevMAGI = np.array(magi)
409
+
397
410
  def rename(self, newname):
398
411
  """
399
412
  Override name of the plan. Plan name is used
@@ -403,8 +416,6 @@ class Plan(object):
403
416
  self.mylog.vprint('Renaming plan %s -> %s.' % (self._name, newname))
404
417
  self._name = newname
405
418
 
406
- return None
407
-
408
419
  def setSpousalDepositFraction(self, eta):
409
420
  """
410
421
  Set spousal deposit and withdrawal fraction. Default 0.5.
@@ -424,8 +435,6 @@ class Plan(object):
424
435
  self.mylog.vprint('\t%s: %.1f, %s: %.1f' % (self.inames[0], (1 - eta), self.inames[1], eta))
425
436
  self.eta = eta
426
437
 
427
- return None
428
-
429
438
  def setDefaultPlots(self, value):
430
439
  """
431
440
  Set plots between nominal values or today's $.
@@ -434,8 +443,6 @@ class Plan(object):
434
443
  self.defaultPlots = self._checkValue(value)
435
444
  self.mylog.vprint('Setting plots default value to %s.' % value)
436
445
 
437
- return None
438
-
439
446
  def setDividendRate(self, mu):
440
447
  """
441
448
  Set dividend rate on equities. Rate is in percent. Default 2%.
@@ -446,8 +453,6 @@ class Plan(object):
446
453
  self.mu = mu
447
454
  self.caseStatus = 'modified'
448
455
 
449
- return None
450
-
451
456
  def setLongTermCapitalTaxRate(self, psi):
452
457
  """
453
458
  Set long-term income tax rate. Rate is in percent. Default 15%.
@@ -458,8 +463,6 @@ class Plan(object):
458
463
  self.psi = psi
459
464
  self.caseStatus = 'modified'
460
465
 
461
- return None
462
-
463
466
  def setBeneficiaryFractions(self, phi):
464
467
  """
465
468
  Set fractions of savings accounts that is left to surviving spouse.
@@ -477,8 +480,6 @@ class Plan(object):
477
480
  self.mylog.vprint('Consider changing spousal deposit fraction for better convergence.')
478
481
  self.mylog.vprint('\tRecommended: setSpousalDepositFraction(%d)' % self.i_d)
479
482
 
480
- return None
481
-
482
483
  def setHeirsTaxRate(self, nu):
483
484
  """
484
485
  Set the heirs tax rate on the tax-deferred portion of the estate.
@@ -490,8 +491,6 @@ class Plan(object):
490
491
  self.nu = nu
491
492
  self.caseStatus = 'modified'
492
493
 
493
- return None
494
-
495
494
  def setPension(self, amounts, ages, units='k'):
496
495
  """
497
496
  Set value of pension for each individual and commencement age.
@@ -521,8 +520,6 @@ class Plan(object):
521
520
  self.pensionAges = np.array(ages, dtype=np.int32)
522
521
  self.caseStatus = 'modified'
523
522
 
524
- return None
525
-
526
523
  def setSocialSecurity(self, amounts, ages, units='k'):
527
524
  """
528
525
  Set value of social security for each individual and commencement age.
@@ -560,8 +557,6 @@ class Plan(object):
560
557
  self.caseStatus = 'modified'
561
558
  self._adjustedParameters = False
562
559
 
563
- return None
564
-
565
560
  def setSpendingProfile(self, profile, percent=60, dip=15, increase=12, delay=0):
566
561
  """
567
562
  Generate time series for spending profile. Surviving spouse fraction can be specified
@@ -589,8 +584,6 @@ class Plan(object):
589
584
  self.smileDelay = delay
590
585
  self.caseStatus = 'modified'
591
586
 
592
- return None
593
-
594
587
  def setRates(self, method, frm=None, to=None, values=None, stdev=None, corr=None):
595
588
  """
596
589
  Generate rates for return and inflation based on the method and
@@ -632,8 +625,6 @@ class Plan(object):
632
625
  self._adjustedParameters = False
633
626
  self.caseStatus = 'modified'
634
627
 
635
- return None
636
-
637
628
  def regenRates(self):
638
629
  """
639
630
  Regenerate the rates using the arguments specified during last setRates() call.
@@ -648,8 +639,6 @@ class Plan(object):
648
639
  corr=self.rateCorr,
649
640
  )
650
641
 
651
- return None
652
-
653
642
  def value(self, amount, year):
654
643
  """
655
644
  Return value of amount deflated or inflated at the beginning
@@ -711,8 +700,6 @@ class Plan(object):
711
700
  u.d(np.sum(taxable) + 0.7 * np.sum(taxDeferred) + np.sum(taxFree)),
712
701
  )
713
702
 
714
- return None
715
-
716
703
  def setInterpolationMethod(self, method, center=15, width=5):
717
704
  """
718
705
  Interpolate assets allocation ratios from initial value (today) to
@@ -739,8 +726,6 @@ class Plan(object):
739
726
 
740
727
  self.mylog.vprint('Asset allocation interpolation method set to %s.' % method)
741
728
 
742
- return None
743
-
744
729
  def setAllocationRatios(self, allocType, taxable=None, taxDeferred=None, taxFree=None, generic=None):
745
730
  """
746
731
  Single function for setting all types of asset allocations.
@@ -874,8 +859,6 @@ class Plan(object):
874
859
 
875
860
  self.mylog.vprint('Interpolating assets allocation ratios using', self.interpMethod, 'method.')
876
861
 
877
- return None
878
-
879
862
  def readContributions(self, filename):
880
863
  """
881
864
  Provide the name of the file containing the financial events
@@ -1897,7 +1880,7 @@ class Plan(object):
1897
1880
  self.F_tn = self.F_tn.reshape((self.N_t, self.N_n))
1898
1881
  MAGI_n = np.sum(self.F_tn, axis=0) + np.array(x[self.C['e']:self.C['F']])
1899
1882
 
1900
- self.M_n = tx.mediCosts(self.yobs, self.horizons, MAGI_n, self.gamma_n[:-1], self.N_n)
1883
+ self.M_n = tx.mediCosts(self.yobs, self.horizons, MAGI_n, self.prevMAGI, self.gamma_n[:-1], self.N_n)
1901
1884
 
1902
1885
  return None
1903
1886
 
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.1.29"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.1.28
3
+ Version: 2025.1.29
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=4Kuulsp3uUAt9w0sLKPPGPry2tPnY74SMNSOTcVIs40,115575
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=31a542RcfLaOTSP2qZRfQPdngqGSa6FlROIHXM1qDdc,27
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.1.29.dist-info/METADATA,sha256=EreFCtwuHeBO7xbqqQrLBET1Gdy5e-o9VsOeMvLN_rQ,64515
15
+ owlplanner-2025.1.29.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ owlplanner-2025.1.29.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
17
+ owlplanner-2025.1.29.dist-info/RECORD,,