owlplanner 2025.1.29__py3-none-any.whl → 2025.2.1__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 +43 -40
- owlplanner/timelists.py +8 -8
- owlplanner/version.py +1 -1
- {owlplanner-2025.1.29.dist-info → owlplanner-2025.2.1.dist-info}/METADATA +1 -1
- {owlplanner-2025.1.29.dist-info → owlplanner-2025.2.1.dist-info}/RECORD +7 -7
- {owlplanner-2025.1.29.dist-info → owlplanner-2025.2.1.dist-info}/WHEEL +0 -0
- {owlplanner-2025.1.29.dist-info → owlplanner-2025.2.1.dist-info}/licenses/LICENSE +0 -0
owlplanner/plan.py
CHANGED
|
@@ -397,16 +397,6 @@ class Plan(object):
|
|
|
397
397
|
|
|
398
398
|
return None
|
|
399
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
|
-
|
|
410
400
|
def rename(self, newname):
|
|
411
401
|
"""
|
|
412
402
|
Override name of the plan. Plan name is used
|
|
@@ -869,12 +859,12 @@ class Plan(object):
|
|
|
869
859
|
|
|
870
860
|
'year',
|
|
871
861
|
'anticipated wages',
|
|
872
|
-
'ctrb
|
|
873
|
-
'ctrb
|
|
874
|
-
'
|
|
875
|
-
'ctrb
|
|
876
|
-
'
|
|
877
|
-
'Roth
|
|
862
|
+
'taxable ctrb',
|
|
863
|
+
'401k ctrb',
|
|
864
|
+
'Roth 401k ctrb',
|
|
865
|
+
'IRA ctrb',
|
|
866
|
+
'Roth IRA ctrb',
|
|
867
|
+
'Roth conv',
|
|
878
868
|
'big-ticket items'
|
|
879
869
|
|
|
880
870
|
in any order. A template is provided as an example.
|
|
@@ -900,12 +890,12 @@ class Plan(object):
|
|
|
900
890
|
for i, iname in enumerate(self.inames):
|
|
901
891
|
h = self.horizons[i]
|
|
902
892
|
self.omega_in[i, :h] = self.timeLists[iname]['anticipated wages'].iloc[:h]
|
|
903
|
-
self.kappa_ijn[i, 0, :h] = self.timeLists[iname]['ctrb
|
|
904
|
-
self.kappa_ijn[i, 1, :h] = self.timeLists[iname]['ctrb
|
|
905
|
-
self.kappa_ijn[i, 2, :h] = self.timeLists[iname]['
|
|
906
|
-
self.kappa_ijn[i, 1, :h] += self.timeLists[iname]['ctrb
|
|
907
|
-
self.kappa_ijn[i, 2, :h] += self.timeLists[iname]['
|
|
908
|
-
self.myRothX_in[i, :h] = self.timeLists[iname]['Roth
|
|
893
|
+
self.kappa_ijn[i, 0, :h] = self.timeLists[iname]['taxable ctrb'].iloc[:h]
|
|
894
|
+
self.kappa_ijn[i, 1, :h] = self.timeLists[iname]['401k ctrb'].iloc[:h]
|
|
895
|
+
self.kappa_ijn[i, 2, :h] = self.timeLists[iname]['Roth 401k ctrb'].iloc[:h]
|
|
896
|
+
self.kappa_ijn[i, 1, :h] += self.timeLists[iname]['IRA ctrb'].iloc[:h]
|
|
897
|
+
self.kappa_ijn[i, 2, :h] += self.timeLists[iname]['Roth IRA ctrb'].iloc[:h]
|
|
898
|
+
self.myRothX_in[i, :h] = self.timeLists[iname]['Roth conv'].iloc[:h]
|
|
909
899
|
self.Lambda_in[i, :h] = self.timeLists[iname]['big-ticket items'].iloc[:h]
|
|
910
900
|
|
|
911
901
|
# In 1st year, reduce wages and contribution depending on starting date.
|
|
@@ -957,8 +947,8 @@ class Plan(object):
|
|
|
957
947
|
self.myRothX_in[:, :] = 0.
|
|
958
948
|
self.kappa_ijn[:, :, :] = 0.
|
|
959
949
|
|
|
960
|
-
cols = ['year', 'anticipated wages', 'ctrb
|
|
961
|
-
'
|
|
950
|
+
cols = ['year', 'anticipated wages', 'taxable ctrb', '401k ctrb',
|
|
951
|
+
'Roth 401k ctrb', 'IRA ctrb', 'Roth IRA ctrb', 'Roth conv', 'big-ticket items']
|
|
962
952
|
for i, iname in enumerate(self.inames):
|
|
963
953
|
h = self.horizons[i]
|
|
964
954
|
df = pd.DataFrame(0, index=np.arange(h), columns=cols)
|
|
@@ -1619,6 +1609,7 @@ class Plan(object):
|
|
|
1619
1609
|
'noRothConversions',
|
|
1620
1610
|
'withMedicare',
|
|
1621
1611
|
'solver',
|
|
1612
|
+
'previousMAGIs',
|
|
1622
1613
|
]
|
|
1623
1614
|
# We will modify options if required.
|
|
1624
1615
|
if options is None:
|
|
@@ -1647,6 +1638,17 @@ class Plan(object):
|
|
|
1647
1638
|
if objective == 'maxSpending' and 'bequest' not in myoptions:
|
|
1648
1639
|
self.mylog.vprint('Using bequest of $1.')
|
|
1649
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
|
+
|
|
1650
1652
|
self._adjustParameters()
|
|
1651
1653
|
|
|
1652
1654
|
if 'solver' in options:
|
|
@@ -1991,8 +1993,8 @@ class Plan(object):
|
|
|
1991
1993
|
'wages',
|
|
1992
1994
|
'ssec',
|
|
1993
1995
|
'pension',
|
|
1994
|
-
'dist',
|
|
1995
|
-
'
|
|
1996
|
+
'+dist',
|
|
1997
|
+
'RMD',
|
|
1996
1998
|
'RothX',
|
|
1997
1999
|
'wdrwl taxable',
|
|
1998
2000
|
'wdrwl tax-free',
|
|
@@ -2002,12 +2004,12 @@ class Plan(object):
|
|
|
2002
2004
|
sources['wages'] = self.omega_in
|
|
2003
2005
|
sources['ssec'] = self.zetaBar_in
|
|
2004
2006
|
sources['pension'] = self.pi_in
|
|
2005
|
-
sources['wdrwl
|
|
2006
|
-
sources['
|
|
2007
|
-
sources['dist'] = self.dist_in
|
|
2007
|
+
sources['taxable wdrwl'] = self.w_ijn[:, 0, :]
|
|
2008
|
+
sources['RMD'] = self.rmd_in
|
|
2009
|
+
sources['+dist'] = self.dist_in
|
|
2008
2010
|
sources['RothX'] = self.x_in
|
|
2009
|
-
sources['
|
|
2010
|
-
sources['
|
|
2011
|
+
sources['tax-free wdrwl'] = self.w_ijn[:, 2, :]
|
|
2012
|
+
sources['BTI'] = self.Lambda_in
|
|
2011
2013
|
|
|
2012
2014
|
savings = {}
|
|
2013
2015
|
savings['taxable'] = self.b_ijn[:, 0, :]
|
|
@@ -2717,7 +2719,7 @@ class Plan(object):
|
|
|
2717
2719
|
'all wages': np.sum(self.omega_in, axis=0),
|
|
2718
2720
|
'all pensions': np.sum(self.pi_in, axis=0),
|
|
2719
2721
|
'all soc sec': np.sum(self.zetaBar_in, axis=0),
|
|
2720
|
-
"all
|
|
2722
|
+
"all BTI's": np.sum(self.Lambda_in, axis=0),
|
|
2721
2723
|
'all wdrwls': np.sum(self.w_ijn, axis=(0, 1)),
|
|
2722
2724
|
'all deposits': -np.sum(self.d_in, axis=0),
|
|
2723
2725
|
'ord taxes': -self.T_n,
|
|
@@ -2733,12 +2735,12 @@ class Plan(object):
|
|
|
2733
2735
|
'wages': self.sources_in['wages'],
|
|
2734
2736
|
'social sec': self.sources_in['ssec'],
|
|
2735
2737
|
'pension': self.sources_in['pension'],
|
|
2736
|
-
'txbl acc wdrwl': self.sources_in['wdrwl
|
|
2737
|
-
'RMDs': self.sources_in['
|
|
2738
|
-
'+distributions': self.sources_in['dist'],
|
|
2739
|
-
'Roth
|
|
2740
|
-
'tax-free wdrwl': self.sources_in['
|
|
2741
|
-
'big-ticket items': self.sources_in['
|
|
2738
|
+
'txbl acc wdrwl': self.sources_in['txbl acc wdrwl'],
|
|
2739
|
+
'RMDs': self.sources_in['RMD'],
|
|
2740
|
+
'+distributions': self.sources_in['+dist'],
|
|
2741
|
+
'Roth conv': self.sources_in['RothX'],
|
|
2742
|
+
'tax-free wdrwl': self.sources_in['tax-free wdrwl'],
|
|
2743
|
+
'big-ticket items': self.sources_in['BTI'],
|
|
2742
2744
|
}
|
|
2743
2745
|
|
|
2744
2746
|
for i in range(self.N_i):
|
|
@@ -2749,13 +2751,14 @@ class Plan(object):
|
|
|
2749
2751
|
# Account balances except final year.
|
|
2750
2752
|
accDic = {
|
|
2751
2753
|
'taxable bal': self.b_ijn[:, 0, :-1],
|
|
2754
|
+
'taxable ctrb': self.kappa_ijn[:, 0, :],
|
|
2752
2755
|
'taxable dep': self.d_in,
|
|
2753
2756
|
'taxable wdrwl': self.w_ijn[:, 0, :],
|
|
2754
2757
|
'tax-deferred bal': self.b_ijn[:, 1, :-1],
|
|
2755
2758
|
'tax-deferred ctrb': self.kappa_ijn[:, 1, :],
|
|
2756
2759
|
'tax-deferred wdrwl': self.w_ijn[:, 1, :],
|
|
2757
2760
|
'(included RMDs)': self.rmd_in[:, :],
|
|
2758
|
-
'Roth
|
|
2761
|
+
'Roth conv': self.x_in,
|
|
2759
2762
|
'tax-free bal': self.b_ijn[:, 2, :-1],
|
|
2760
2763
|
'tax-free ctrb': self.kappa_ijn[:, 2, :],
|
|
2761
2764
|
'tax-free wdrwl': self.w_ijn[:, 2, :],
|
|
@@ -2854,7 +2857,7 @@ class Plan(object):
|
|
|
2854
2857
|
planData[self.inames[i] + ' tx-def ctrb'] = self.kappa_ijn[i, 1, :]
|
|
2855
2858
|
planData[self.inames[i] + ' tx-def wdrl'] = self.w_ijn[i, 1, :]
|
|
2856
2859
|
planData[self.inames[i] + ' (RMD)'] = self.rmd_in[i, :]
|
|
2857
|
-
planData[self.inames[i] + ' Roth
|
|
2860
|
+
planData[self.inames[i] + ' Roth conv'] = self.x_in[i, :]
|
|
2858
2861
|
planData[self.inames[i] + ' tx-free bal'] = self.b_ijn[i, 2, :-1]
|
|
2859
2862
|
planData[self.inames[i] + ' tx-free ctrb'] = self.kappa_ijn[i, 2, :]
|
|
2860
2863
|
planData[self.inames[i] + ' tax-free wdrwl'] = self.w_ijn[i, 2, :]
|
owlplanner/timelists.py
CHANGED
|
@@ -23,12 +23,12 @@ import pandas as pd
|
|
|
23
23
|
timeHorizonItems = [
|
|
24
24
|
'year',
|
|
25
25
|
'anticipated wages',
|
|
26
|
-
'ctrb
|
|
27
|
-
'ctrb
|
|
28
|
-
'
|
|
29
|
-
'ctrb
|
|
30
|
-
'
|
|
31
|
-
'Roth
|
|
26
|
+
'taxable ctrb',
|
|
27
|
+
'401k ctrb',
|
|
28
|
+
'Roth 401k ctrb',
|
|
29
|
+
'IRA ctrb',
|
|
30
|
+
'Roth IRA ctrb',
|
|
31
|
+
'Roth conv',
|
|
32
32
|
'big-ticket items',
|
|
33
33
|
]
|
|
34
34
|
|
|
@@ -38,8 +38,8 @@ def read(finput, inames, horizons, mylog):
|
|
|
38
38
|
Read listed parameters from an excel spreadsheet or through
|
|
39
39
|
a dictionary of dataframes through Pandas.
|
|
40
40
|
Use one sheet for each individual with the following 9 columns:
|
|
41
|
-
year, anticipated wages,
|
|
42
|
-
|
|
41
|
+
year, anticipated wages, taxable ctrb, 401k ctrb, Roth 401k ctrb,
|
|
42
|
+
IRA ctrb, Roth IRA ctrb, Roth conv, and big-ticket items.
|
|
43
43
|
Supports xls, xlsx, xlsm, xlsb, odf, ods, and odt file extensions.
|
|
44
44
|
Returs a dictionary of dataframes by individual's names.
|
|
45
45
|
"""
|
owlplanner/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2025.
|
|
1
|
+
__version__ = "2025.02.01"
|
|
@@ -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=
|
|
5
|
+
owlplanner/plan.py,sha256=kVuv7OfnLnoTxO6LZgRY56Np-yWifGNg_vtlB8R_jZQ,115626
|
|
6
6
|
owlplanner/progress.py,sha256=YZjL5_m4MMgKPlWlhhKacPLt54tVhVGF1eXxxZapMYs,386
|
|
7
7
|
owlplanner/rates.py,sha256=aKOmau8i3uqxZGi7HQJpzooT3X-yAZhga5MZJ56pBzk,15627
|
|
8
8
|
owlplanner/tax2025.py,sha256=b2RgM6TBQa8ggo6ODyh0p_J7j79UUm8z5NiENqa1l_k,7016
|
|
9
|
-
owlplanner/timelists.py,sha256=
|
|
9
|
+
owlplanner/timelists.py,sha256=WwymsYAGWcrEzMtc-wrLbn1EVA2fhqXGN4NHLJsH3Fs,4110
|
|
10
10
|
owlplanner/utils.py,sha256=adIwqGVQFfvekke0JCxYJD3PKHbptVCj3NrQT2TQIB4,2351
|
|
11
|
-
owlplanner/version.py,sha256=
|
|
11
|
+
owlplanner/version.py,sha256=0gSeVv3M2dXUJh4tEDh_HPMJgzKSY9PHw3rX05-ggH8,28
|
|
12
12
|
owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
|
|
14
|
-
owlplanner-2025.1.
|
|
15
|
-
owlplanner-2025.1.
|
|
16
|
-
owlplanner-2025.1.
|
|
17
|
-
owlplanner-2025.1.
|
|
14
|
+
owlplanner-2025.2.1.dist-info/METADATA,sha256=FUY7f2ri_UxQlUMZDllEohMHAVge-wmCp5bhnpi-gkA,64514
|
|
15
|
+
owlplanner-2025.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
owlplanner-2025.2.1.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
17
|
+
owlplanner-2025.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|