owlplanner 2025.2.21__py3-none-any.whl → 2025.2.23__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/config.py +49 -47
- owlplanner/plan.py +9 -0
- owlplanner/version.py +1 -1
- {owlplanner-2025.2.21.dist-info → owlplanner-2025.2.23.dist-info}/METADATA +1 -1
- {owlplanner-2025.2.21.dist-info → owlplanner-2025.2.23.dist-info}/RECORD +7 -7
- {owlplanner-2025.2.21.dist-info → owlplanner-2025.2.23.dist-info}/WHEEL +0 -0
- {owlplanner-2025.2.21.dist-info → owlplanner-2025.2.23.dist-info}/licenses/LICENSE +0 -0
owlplanner/config.py
CHANGED
|
@@ -19,7 +19,7 @@ from owlplanner import logging
|
|
|
19
19
|
from owlplanner.rates import FROM, TO
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def saveConfig(
|
|
22
|
+
def saveConfig(myplan, file, mylog):
|
|
23
23
|
"""
|
|
24
24
|
Save case parameters and return a dictionary containing all parameters.
|
|
25
25
|
"""
|
|
@@ -27,86 +27,87 @@ def saveConfig(plan, file, mylog):
|
|
|
27
27
|
accountTypes = ["taxable", "tax-deferred", "tax-free"]
|
|
28
28
|
|
|
29
29
|
diconf = {}
|
|
30
|
-
diconf["Plan Name"] =
|
|
30
|
+
diconf["Plan Name"] = myplan._name
|
|
31
|
+
diconf["Description"] = myplan._description
|
|
31
32
|
|
|
32
33
|
# Basic Info.
|
|
33
34
|
diconf["Basic Info"] = {
|
|
34
|
-
"Status": ["unknown", "single", "married"][
|
|
35
|
-
"Names":
|
|
36
|
-
"Birth year":
|
|
37
|
-
"Life expectancy":
|
|
38
|
-
"Start date":
|
|
35
|
+
"Status": ["unknown", "single", "married"][myplan.N_i],
|
|
36
|
+
"Names": myplan.inames,
|
|
37
|
+
"Birth year": myplan.yobs.tolist(),
|
|
38
|
+
"Life expectancy": myplan.expectancy.tolist(),
|
|
39
|
+
"Start date": myplan.startDate,
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
# Assets.
|
|
42
43
|
diconf["Assets"] = {}
|
|
43
|
-
for j in range(
|
|
44
|
-
amounts =
|
|
44
|
+
for j in range(myplan.N_j):
|
|
45
|
+
amounts = myplan.beta_ij[:, j] / 1000
|
|
45
46
|
diconf["Assets"][f"{accountTypes[j]} savings balances"] = amounts.tolist()
|
|
46
|
-
if
|
|
47
|
-
diconf["Assets"]["Beneficiary fractions"] =
|
|
48
|
-
diconf["Assets"]["Spousal surplus deposit fraction"] =
|
|
47
|
+
if myplan.N_i == 2:
|
|
48
|
+
diconf["Assets"]["Beneficiary fractions"] = myplan.phi_j.tolist()
|
|
49
|
+
diconf["Assets"]["Spousal surplus deposit fraction"] = myplan.eta
|
|
49
50
|
|
|
50
51
|
# Wages and Contributions.
|
|
51
|
-
diconf["Wages and Contributions"] = {"Contributions file name":
|
|
52
|
+
diconf["Wages and Contributions"] = {"Contributions file name": myplan.timeListsFileName}
|
|
52
53
|
|
|
53
54
|
# Fixed Income.
|
|
54
55
|
diconf["Fixed Income"] = {
|
|
55
|
-
"Pension amounts": (
|
|
56
|
-
"Pension ages":
|
|
57
|
-
"Pension indexed":
|
|
58
|
-
"Social security amounts": (
|
|
59
|
-
"Social security ages":
|
|
56
|
+
"Pension amounts": (myplan.pensionAmounts / 1000).tolist(),
|
|
57
|
+
"Pension ages": myplan.pensionAges.tolist(),
|
|
58
|
+
"Pension indexed": myplan.pensionIndexed,
|
|
59
|
+
"Social security amounts": (myplan.ssecAmounts / 1000).tolist(),
|
|
60
|
+
"Social security ages": myplan.ssecAges.tolist(),
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
# Rates Selection.
|
|
63
64
|
diconf["Rates Selection"] = {
|
|
64
|
-
"Heirs rate on tax-deferred estate": float(100 *
|
|
65
|
-
"Long-term capital gain tax rate": float(100 *
|
|
66
|
-
"Dividend tax rate": float(100 *
|
|
67
|
-
"TCJA expiration year":
|
|
68
|
-
"Method":
|
|
65
|
+
"Heirs rate on tax-deferred estate": float(100 * myplan.nu),
|
|
66
|
+
"Long-term capital gain tax rate": float(100 * myplan.psi),
|
|
67
|
+
"Dividend tax rate": float(100 * myplan.mu),
|
|
68
|
+
"TCJA expiration year": myplan.yTCJA,
|
|
69
|
+
"Method": myplan.rateMethod,
|
|
69
70
|
}
|
|
70
|
-
if
|
|
71
|
-
diconf["Rates Selection"]["Values"] = (100 *
|
|
72
|
-
if
|
|
73
|
-
diconf["Rates Selection"]["Standard deviations"] = (100 *
|
|
74
|
-
diconf["Rates Selection"]["Correlations"] =
|
|
75
|
-
if
|
|
76
|
-
diconf["Rates Selection"]["From"] = int(
|
|
77
|
-
diconf["Rates Selection"]["To"] = int(
|
|
71
|
+
if myplan.rateMethod in ["user", "stochastic"]:
|
|
72
|
+
diconf["Rates Selection"]["Values"] = (100 * myplan.rateValues).tolist()
|
|
73
|
+
if myplan.rateMethod in ["stochastic"]:
|
|
74
|
+
diconf["Rates Selection"]["Standard deviations"] = (100 * myplan.rateStdev).tolist()
|
|
75
|
+
diconf["Rates Selection"]["Correlations"] = myplan.rateCorr.tolist()
|
|
76
|
+
if myplan.rateMethod in ["historical average", "historical", "histochastic"]:
|
|
77
|
+
diconf["Rates Selection"]["From"] = int(myplan.rateFrm)
|
|
78
|
+
diconf["Rates Selection"]["To"] = int(myplan.rateTo)
|
|
78
79
|
else:
|
|
79
80
|
diconf["Rates Selection"]["From"] = int(FROM)
|
|
80
81
|
diconf["Rates Selection"]["To"] = int(TO)
|
|
81
82
|
|
|
82
83
|
# Asset Allocation.
|
|
83
84
|
diconf["Asset Allocation"] = {
|
|
84
|
-
"Interpolation method":
|
|
85
|
-
"Interpolation center": float(
|
|
86
|
-
"Interpolation width": float(
|
|
87
|
-
"Type":
|
|
85
|
+
"Interpolation method": myplan.interpMethod,
|
|
86
|
+
"Interpolation center": float(myplan.interpCenter),
|
|
87
|
+
"Interpolation width": float(myplan.interpWidth),
|
|
88
|
+
"Type": myplan.ARCoord,
|
|
88
89
|
}
|
|
89
|
-
if
|
|
90
|
+
if myplan.ARCoord == "account":
|
|
90
91
|
for accType in accountTypes:
|
|
91
|
-
diconf["Asset Allocation"][accType] =
|
|
92
|
+
diconf["Asset Allocation"][accType] = myplan.boundsAR[accType]
|
|
92
93
|
else:
|
|
93
|
-
diconf["Asset Allocation"]["generic"] =
|
|
94
|
+
diconf["Asset Allocation"]["generic"] = myplan.boundsAR["generic"]
|
|
94
95
|
|
|
95
96
|
# Optimization Parameters.
|
|
96
97
|
diconf["Optimization Parameters"] = {
|
|
97
|
-
"Spending profile":
|
|
98
|
-
"Surviving spouse spending percent": int(100 *
|
|
98
|
+
"Spending profile": myplan.spendingProfile,
|
|
99
|
+
"Surviving spouse spending percent": int(100 * myplan.chi),
|
|
99
100
|
}
|
|
100
|
-
if
|
|
101
|
-
diconf["Optimization Parameters"]["Smile dip"] = int(
|
|
102
|
-
diconf["Optimization Parameters"]["Smile increase"] = int(
|
|
103
|
-
diconf["Optimization Parameters"]["Smile delay"] = int(
|
|
101
|
+
if myplan.spendingProfile == "smile":
|
|
102
|
+
diconf["Optimization Parameters"]["Smile dip"] = int(myplan.smileDip)
|
|
103
|
+
diconf["Optimization Parameters"]["Smile increase"] = int(myplan.smileIncrease)
|
|
104
|
+
diconf["Optimization Parameters"]["Smile delay"] = int(myplan.smileDelay)
|
|
104
105
|
|
|
105
|
-
diconf["Optimization Parameters"]["Objective"] =
|
|
106
|
-
diconf["Solver Options"] =
|
|
106
|
+
diconf["Optimization Parameters"]["Objective"] = myplan.objective
|
|
107
|
+
diconf["Solver Options"] = myplan.solverOptions
|
|
107
108
|
|
|
108
109
|
# Results.
|
|
109
|
-
diconf["Results"] = {"Default plots":
|
|
110
|
+
diconf["Results"] = {"Default plots": myplan.defaultPlots}
|
|
110
111
|
|
|
111
112
|
if isinstance(file, str):
|
|
112
113
|
filename = file
|
|
@@ -185,6 +186,7 @@ def readConfig(file, *, verbose=True, logstreams=None, readContributions=True):
|
|
|
185
186
|
s = ["", "s"][icount - 1]
|
|
186
187
|
mylog.vprint(f"Plan for {icount} individual{s}: {inames}.")
|
|
187
188
|
p = plan.Plan(inames, yobs, expectancy, name, startDate=startDate, verbose=True, logstreams=logstreams)
|
|
189
|
+
p._description = diconf.get("Description", "")
|
|
188
190
|
|
|
189
191
|
# Assets.
|
|
190
192
|
balances = {}
|
owlplanner/plan.py
CHANGED
|
@@ -239,6 +239,7 @@ class Plan(object):
|
|
|
239
239
|
self.interpCenter = 15
|
|
240
240
|
self.interpWidth = 5
|
|
241
241
|
|
|
242
|
+
self._description = ''
|
|
242
243
|
self.defaultPlots = "nominal"
|
|
243
244
|
self.defaultSolver = "HiGHS"
|
|
244
245
|
|
|
@@ -406,6 +407,12 @@ class Plan(object):
|
|
|
406
407
|
self.mylog.vprint(f"Renaming plan {self._name} -> {newname}.")
|
|
407
408
|
self._name = newname
|
|
408
409
|
|
|
410
|
+
def setDescription(self, description):
|
|
411
|
+
"""
|
|
412
|
+
Set a text description of the plan.
|
|
413
|
+
"""
|
|
414
|
+
self._description = description
|
|
415
|
+
|
|
409
416
|
def setSpousalDepositFraction(self, eta):
|
|
410
417
|
"""
|
|
411
418
|
Set spousal deposit and withdrawal fraction. Default 0.5.
|
|
@@ -2069,6 +2076,8 @@ class Plan(object):
|
|
|
2069
2076
|
now = self.year_n[0]
|
|
2070
2077
|
dic = {}
|
|
2071
2078
|
# Results
|
|
2079
|
+
dic["Plan name"] = self._name
|
|
2080
|
+
dic["Brief description"] = self._description
|
|
2072
2081
|
dic[f"Net yearly spending basis in {now}$"] = u.d(self.g_n[0] / self.xi_n[0])
|
|
2073
2082
|
dic[f"Net yearly spending for year {now}"] = u.d(self.g_n[0] / self.yearFracLeft)
|
|
2074
2083
|
dic[f"Net spending remaining in year {now}"] = u.d(self.g_n[0])
|
owlplanner/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2025.02.
|
|
1
|
+
__version__ = "2025.02.23"
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
owlplanner/__init__.py,sha256=QqrdT0Qks20osBTg7h0vJHAxpP9lL7DA99xb0nYbtw4,254
|
|
2
2
|
owlplanner/abcapi.py,sha256=LbzW_KcNy0IeHp42MUHwGu_H67B2h_e1_vu-c2ACTkQ,6646
|
|
3
|
-
owlplanner/config.py,sha256=
|
|
3
|
+
owlplanner/config.py,sha256=bvqoOWrdPrWYQF9-PS_n00MoyZkAsmMOSczhTUFRGVU,12257
|
|
4
4
|
owlplanner/logging.py,sha256=tYMw04O-XYSzjTj36fmKJGLcE1VkK6k6oJNeqtKXzuc,2530
|
|
5
|
-
owlplanner/plan.py,sha256=
|
|
5
|
+
owlplanner/plan.py,sha256=RTkIT5tJ_Ar0hCBx0G1ScjaGLuxdsRDqwWC1eosp5po,113860
|
|
6
6
|
owlplanner/progress.py,sha256=8jlCvvtgDI89zXVNMBg1-lnEyhpPvKQS2X5oAIpoOVQ,384
|
|
7
7
|
owlplanner/rates.py,sha256=TN407qU4n-bac1oymkQ_n2QKEPwFQxy6JZVGwgIkLQU,15585
|
|
8
8
|
owlplanner/tax2025.py,sha256=B-A5eU3wxdcAaxRCbT3qI-JEKoD_ZeNbg_86XhNdQEI,7745
|
|
9
9
|
owlplanner/timelists.py,sha256=tYieZU67FT6TCcQQis36JaXGI7dT6NqD7RvdEjgJL4M,4026
|
|
10
10
|
owlplanner/utils.py,sha256=HM70W60qB41zfnbl2LltNwAuLYHyy5XYbwnbNcaa6FE,2351
|
|
11
|
-
owlplanner/version.py,sha256=
|
|
11
|
+
owlplanner/version.py,sha256=85qxkfG2OiCeMaWpV6ROXWN2-KYejMXo-QU_lI_mUOM,28
|
|
12
12
|
owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
|
|
14
|
-
owlplanner-2025.2.
|
|
15
|
-
owlplanner-2025.2.
|
|
16
|
-
owlplanner-2025.2.
|
|
17
|
-
owlplanner-2025.2.
|
|
14
|
+
owlplanner-2025.2.23.dist-info/METADATA,sha256=SfUn8ugA-N7NDg2XhNt0oBQOSbCi2GqARxGIqkYAMc8,53506
|
|
15
|
+
owlplanner-2025.2.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
owlplanner-2025.2.23.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
17
|
+
owlplanner-2025.2.23.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|