owlplanner 2025.12.20__py3-none-any.whl → 2026.2.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/__init__.py +20 -1
- owlplanner/abcapi.py +18 -17
- owlplanner/cli/README.md +50 -0
- owlplanner/cli/_main.py +52 -0
- owlplanner/cli/cli_logging.py +56 -0
- owlplanner/cli/cmd_list.py +83 -0
- owlplanner/cli/cmd_run.py +86 -0
- owlplanner/config.py +315 -118
- owlplanner/data/__init__.py +21 -0
- owlplanner/data/rates.csv +99 -98
- owlplanner/debts.py +36 -8
- owlplanner/fixedassets.py +95 -21
- owlplanner/mylogging.py +157 -25
- owlplanner/plan.py +938 -390
- owlplanner/plotting/__init__.py +16 -3
- owlplanner/plotting/base.py +17 -3
- owlplanner/plotting/factory.py +16 -3
- owlplanner/plotting/matplotlib_backend.py +30 -7
- owlplanner/plotting/plotly_backend.py +32 -9
- owlplanner/progress.py +16 -3
- owlplanner/rates.py +50 -34
- owlplanner/socialsecurity.py +28 -19
- owlplanner/tax2026.py +119 -38
- owlplanner/timelists.py +194 -18
- owlplanner/utils.py +179 -4
- owlplanner/version.py +20 -1
- {owlplanner-2025.12.20.dist-info → owlplanner-2026.2.2.dist-info}/METADATA +11 -3
- owlplanner-2026.2.2.dist-info/RECORD +35 -0
- owlplanner-2026.2.2.dist-info/entry_points.txt +2 -0
- owlplanner-2026.2.2.dist-info/licenses/AUTHORS +15 -0
- owlplanner/tax2025.py +0 -359
- owlplanner-2025.12.20.dist-info/RECORD +0 -29
- {owlplanner-2025.12.20.dist-info → owlplanner-2026.2.2.dist-info}/WHEEL +0 -0
- {owlplanner-2025.12.20.dist-info → owlplanner-2026.2.2.dist-info}/licenses/LICENSE +0 -0
owlplanner/data/rates.csv
CHANGED
|
@@ -1,98 +1,99 @@
|
|
|
1
|
-
year,S&P 500,Bonds Baa,
|
|
2
|
-
1928,43.81,3.22,
|
|
3
|
-
1929,-8.
|
|
4
|
-
1930,-25.12,0.54
|
|
5
|
-
1931,-43.84,-15.68,-
|
|
6
|
-
1932,-8.64,23.59,
|
|
7
|
-
1933,49.98,12.97,
|
|
8
|
-
1934,-1.19,18.82,
|
|
9
|
-
1935,46.74,13.31,
|
|
10
|
-
1936,31.94,11.38,
|
|
11
|
-
1937,-35.34,-4.42,-
|
|
12
|
-
1938,29.28,9.24,
|
|
13
|
-
1939,-1.
|
|
14
|
-
1940,-10.67,8.65
|
|
15
|
-
1941,-12.77,5.01
|
|
16
|
-
1942,19.17,5.18,
|
|
17
|
-
1943,25.06,8.04,
|
|
18
|
-
1944,19.03,6.57,
|
|
19
|
-
1945,35.82,6.
|
|
20
|
-
1946,-8.43,2.51
|
|
21
|
-
1947,5.
|
|
22
|
-
1948,5.
|
|
23
|
-
1949,18.
|
|
24
|
-
1950,30.81,4.24,
|
|
25
|
-
1951,23.68,-0.19
|
|
26
|
-
1952,18.15,4.44,
|
|
27
|
-
1953,-1.21,1.62
|
|
28
|
-
1954,52.56,6.16,
|
|
29
|
-
1955,32.
|
|
30
|
-
1956,7.44,-2.35,-
|
|
31
|
-
1957,-10.46,-0.72,-
|
|
32
|
-
1958,43.72,6.43,
|
|
33
|
-
1959,12.06,1.57,
|
|
34
|
-
1960,0.34,6.66
|
|
35
|
-
1961,26.64,5.
|
|
36
|
-
1962,-8.81,6.
|
|
37
|
-
1963,22.61,5.46,
|
|
38
|
-
1964,16.42,5.16,
|
|
39
|
-
1965,12.
|
|
40
|
-
1966,-9.97,-3.45,-
|
|
41
|
-
1967,23.
|
|
42
|
-
1968,10.81,4.85,
|
|
43
|
-
1969,-8.24,-2.03,-
|
|
44
|
-
1970,3.56,5.65
|
|
45
|
-
1971,14.22,14.
|
|
46
|
-
1972,18.76,11.41,
|
|
47
|
-
1973,-14.31,4.32
|
|
48
|
-
1974,-25.
|
|
49
|
-
1975,37.
|
|
50
|
-
1976,23.83,19.75,
|
|
51
|
-
1977,-6.98,9.95,
|
|
52
|
-
1978,6.51,3.14,
|
|
53
|
-
1979,18.52,-2.01
|
|
54
|
-
1980,31.74,-3.32
|
|
55
|
-
1981,-4.
|
|
56
|
-
1982,20.42,29.05,
|
|
57
|
-
1983,22.34,16.19,
|
|
58
|
-
1984,6.15,15.62
|
|
59
|
-
1985,31.24,23.86,
|
|
60
|
-
1986,18.49,
|
|
61
|
-
1987,5.81,
|
|
62
|
-
1988,16.54,
|
|
63
|
-
1989,31.48,
|
|
64
|
-
1990,-3.06,
|
|
65
|
-
1991,30.23,
|
|
66
|
-
1992,7.49,
|
|
67
|
-
1993,9.97,
|
|
68
|
-
1994,1.33,-
|
|
69
|
-
1995,37.
|
|
70
|
-
1996,22.68,
|
|
71
|
-
1997,33.
|
|
72
|
-
1998,28.34,
|
|
73
|
-
1999,20.89,0.
|
|
74
|
-
2000,-9.03,9.
|
|
75
|
-
2001,-11.85,8.
|
|
76
|
-
2002,-21.97,12.
|
|
77
|
-
2003,28.36,12.
|
|
78
|
-
2004,10.74,10.
|
|
79
|
-
2005,4.83,5.3
|
|
80
|
-
2006,15.61,5.
|
|
81
|
-
2007,5.48,4.
|
|
82
|
-
2008,-36.55,-3.
|
|
83
|
-
2009,25.94,
|
|
84
|
-
2010,14.82,9.
|
|
85
|
-
2011,2.
|
|
86
|
-
2012,15.89,9.
|
|
87
|
-
2013,32.15,-
|
|
88
|
-
2014,13.52,10.
|
|
89
|
-
2015,1.38,-1.
|
|
90
|
-
2016,11.77,11.52,
|
|
91
|
-
2017,21.61,9.
|
|
92
|
-
2018,-4.23,-3.
|
|
93
|
-
2019,31.21,15.25,
|
|
94
|
-
2020,18.02,10.
|
|
95
|
-
2021,28.47,
|
|
96
|
-
2022,-18.04,-15.
|
|
97
|
-
2023,26.06,8.74,
|
|
98
|
-
2024,24.88,1.74,
|
|
1
|
+
year,S&P 500,Bonds Baa,real estate,TBills,TNotes,Inflation
|
|
2
|
+
1928,43.81,3.22,62.15,3.08,0.84,-1.16
|
|
3
|
+
1929,-8.30,3.02,-46.08,3.16,4.20,0.58
|
|
4
|
+
1930,-25.12,0.54,-48.35,4.55,4.54,-6.40
|
|
5
|
+
1931,-43.84,-15.68,-43.62,2.31,-2.56,-9.32
|
|
6
|
+
1932,-8.64,23.59,28.65,1.07,8.79,-10.27
|
|
7
|
+
1933,49.98,12.97,146.60,0.96,1.86,0.76
|
|
8
|
+
1934,-1.19,18.82,23.07,0.28,7.96,1.52
|
|
9
|
+
1935,46.74,13.31,54.90,0.17,4.47,2.99
|
|
10
|
+
1936,31.94,11.38,96.41,0.17,5.02,1.45
|
|
11
|
+
1937,-35.34,-4.42,-53.94,0.28,1.38,2.86
|
|
12
|
+
1938,29.28,9.24,5.16,0.06,4.21,-2.78
|
|
13
|
+
1939,-1.10,7.98,-4.86,0.05,4.41,0.00
|
|
14
|
+
1940,-10.67,8.65,-32.88,0.04,5.40,0.71
|
|
15
|
+
1941,-12.77,5.01,-6.75,0.13,-2.02,9.93
|
|
16
|
+
1942,19.17,5.18,63.01,0.34,2.29,9.03
|
|
17
|
+
1943,25.06,8.04,143.02,0.38,2.49,2.96
|
|
18
|
+
1944,19.03,6.57,71.15,0.38,2.58,2.30
|
|
19
|
+
1945,35.82,6.80,94.41,0.38,3.80,2.25
|
|
20
|
+
1946,-8.43,2.51,-13.73,0.38,3.13,18.13
|
|
21
|
+
1947,5.20,0.26,-1.74,0.60,0.92,8.84
|
|
22
|
+
1948,5.70,3.44,-0.01,1.04,1.95,2.99
|
|
23
|
+
1949,18.30,5.38,27.60,1.12,4.66,-2.07
|
|
24
|
+
1950,30.81,4.24,52.81,1.20,0.43,5.93
|
|
25
|
+
1951,23.68,-0.19,3.87,1.52,-0.30,6.00
|
|
26
|
+
1952,18.15,4.44,1.02,1.72,2.27,0.75
|
|
27
|
+
1953,-1.21,1.62,-5.97,1.89,4.14,0.75
|
|
28
|
+
1954,52.56,6.16,64.97,0.94,3.29,-0.74
|
|
29
|
+
1955,32.60,2.04,26.72,1.72,-1.34,0.37
|
|
30
|
+
1956,7.44,-2.35,-0.89,2.62,-2.26,2.99
|
|
31
|
+
1957,-10.46,-0.72,-15.19,3.22,6.80,2.90
|
|
32
|
+
1958,43.72,6.43,68.80,1.77,-2.10,1.76
|
|
33
|
+
1959,12.06,1.57,12.70,3.39,-2.65,1.73
|
|
34
|
+
1960,0.34,6.66,-3.57,2.87,11.64,1.36
|
|
35
|
+
1961,26.64,5.10,29.45,2.35,2.06,0.67
|
|
36
|
+
1962,-8.81,6.50,-9.78,2.77,5.69,1.33
|
|
37
|
+
1963,22.61,5.46,19.65,3.16,1.68,1.64
|
|
38
|
+
1964,16.42,5.16,23.25,3.55,3.73,0.97
|
|
39
|
+
1965,12.40,3.19,45.24,3.95,0.72,1.92
|
|
40
|
+
1966,-9.97,-3.45,-9.47,4.86,2.91,3.46
|
|
41
|
+
1967,23.80,0.90,115.87,4.29,-1.58,3.04
|
|
42
|
+
1968,10.81,4.85,60.69,5.34,3.27,4.72
|
|
43
|
+
1969,-8.24,-2.03,-32.95,6.67,-5.01,6.20
|
|
44
|
+
1970,3.56,5.65,-18.78,6.39,16.75,5.57
|
|
45
|
+
1971,14.22,14.00,15.96,4.33,9.79,3.27
|
|
46
|
+
1972,18.76,11.41,0.16,4.06,2.82,3.41
|
|
47
|
+
1973,-14.31,4.32,-38.80,7.04,3.66,8.71
|
|
48
|
+
1974,-25.90,-4.38,-26.90,7.85,1.99,12.34
|
|
49
|
+
1975,37.00,11.05,59.68,5.79,3.61,6.94
|
|
50
|
+
1976,23.83,19.75,48.62,4.98,15.98,4.86
|
|
51
|
+
1977,-6.98,9.95,30.29,5.26,1.29,6.70
|
|
52
|
+
1978,6.51,3.14,28.89,7.18,-0.78,9.02
|
|
53
|
+
1979,18.52,-2.01,41.69,10.05,0.67,13.29
|
|
54
|
+
1980,31.74,-3.32,41.92,11.39,-2.99,12.52
|
|
55
|
+
1981,-4.70,8.46,-4.29,14.04,8.20,8.92
|
|
56
|
+
1982,20.42,29.05,26.85,11.09,32.81,3.83
|
|
57
|
+
1983,22.34,16.19,34.86,8.95,3.20,3.79
|
|
58
|
+
1984,6.15,15.62,-14.50,9.92,13.73,3.95
|
|
59
|
+
1985,31.24,23.86,24.51,7.72,25.71,3.80
|
|
60
|
+
1986,18.49,22.15,2.09,6.15,24.28,1.10
|
|
61
|
+
1987,5.81,1.12,-14.00,5.96,-4.96,4.43
|
|
62
|
+
1988,16.54,15.68,17.15,6.89,8.22,4.42
|
|
63
|
+
1989,31.48,16.31,6.96,8.39,17.69,4.65
|
|
64
|
+
1990,-3.06,5.65,-27.77,7.75,6.24,6.11
|
|
65
|
+
1991,30.23,16.40,46.07,5.54,15.00,3.06
|
|
66
|
+
1992,7.49,13.68,25.34,3.51,9.36,2.90
|
|
67
|
+
1993,9.97,16.44,25.56,3.07,14.21,2.75
|
|
68
|
+
1994,1.33,-1.23,-4.76,4.37,-8.04,2.67
|
|
69
|
+
1995,37.20,20.09,32.12,5.66,23.48,2.54
|
|
70
|
+
1996,22.68,5.28,14.79,5.15,1.43,3.32
|
|
71
|
+
1997,33.10,11.30,22.06,5.20,9.94,1.70
|
|
72
|
+
1998,28.34,8.10,-13.47,4.91,14.92,1.61
|
|
73
|
+
1999,20.89,0.97,37.71,4.78,-8.25,2.68
|
|
74
|
+
2000,-9.03,9.38,-9.13,6.00,16.66,3.39
|
|
75
|
+
2001,-11.85,8.60,32.14,3.48,5.57,1.55
|
|
76
|
+
2002,-21.97,12.06,-3.61,1.64,15.12,2.38
|
|
77
|
+
2003,28.36,12.38,91.23,1.03,0.38,1.88
|
|
78
|
+
2004,10.74,10.33,17.30,1.40,4.49,3.26
|
|
79
|
+
2005,4.83,5.13,3.78,3.22,2.87,3.42
|
|
80
|
+
2006,15.61,5.27,18.40,4.85,1.96,2.54
|
|
81
|
+
2007,5.48,4.90,-9.11,4.48,10.21,4.08
|
|
82
|
+
2008,-36.55,-3.44,-44.68,1.40,20.10,0.09
|
|
83
|
+
2009,25.94,19.96,46.94,0.15,-11.12,2.72
|
|
84
|
+
2010,14.82,9.40,27.73,0.14,8.46,1.50
|
|
85
|
+
2011,2.10,12.26,-14.04,0.05,16.04,2.96
|
|
86
|
+
2012,15.89,9.40,18.96,0.09,2.97,1.74
|
|
87
|
+
2013,32.15,-1.13,50.30,0.06,-9.10,1.50
|
|
88
|
+
2014,13.52,10.75,1.53,0.03,10.75,0.76
|
|
89
|
+
2015,1.38,-1.50,-9.12,0.05,1.28,0.73
|
|
90
|
+
2016,11.77,11.52,17.02,0.32,0.69,2.07
|
|
91
|
+
2017,21.61,9.15,15.13,0.95,2.80,2.11
|
|
92
|
+
2018,-4.23,-3.18,-16.21,1.97,-0.02,1.91
|
|
93
|
+
2019,31.21,15.25,11.92,2.11,9.64,2.29
|
|
94
|
+
2020,18.02,10.60,34.16,0.36,11.33,1.36
|
|
95
|
+
2021,28.47,1.02,22.41,0.04,-4.42,7.04
|
|
96
|
+
2022,-18.04,-15.23,-22.90,2.09,-17.83,6.45
|
|
97
|
+
2023,26.06,8.74,5.19,5.28,3.88,3.35
|
|
98
|
+
2024,24.88,1.74,8.70,5.18,-1.64,2.89
|
|
99
|
+
2025,17.78,6.96,16.53,4.21,8.29,2.74
|
owlplanner/debts.py
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
"""
|
|
2
|
+
Debt management and calculation module.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
This module provides functions for handling debts including mortgage calculations,
|
|
5
|
+
loan amortization, and debt-related financial planning.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Copyright (C) 2025-2026 The Owlplanner Authors
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
This program is free software: you can redistribute it and/or modify
|
|
10
|
+
it under the terms of the GNU General Public License as published by
|
|
11
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
12
|
+
(at your option) any later version.
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
This program is distributed in the hope that it will be useful,
|
|
15
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
GNU General Public License for more details.
|
|
10
18
|
|
|
19
|
+
You should have received a copy of the GNU General Public License
|
|
20
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
11
21
|
"""
|
|
12
22
|
|
|
13
23
|
######################################################################
|
|
@@ -15,6 +25,8 @@ import numpy as np
|
|
|
15
25
|
import pandas as pd # noqa: F401
|
|
16
26
|
from datetime import date
|
|
17
27
|
|
|
28
|
+
from . import utils as u
|
|
29
|
+
|
|
18
30
|
|
|
19
31
|
def calculate_monthly_payment(principal, annual_rate, term_years):
|
|
20
32
|
"""
|
|
@@ -130,12 +142,16 @@ def get_debt_payments_for_year(debts_df, year):
|
|
|
130
142
|
float
|
|
131
143
|
Total annual debt payments for the year
|
|
132
144
|
"""
|
|
133
|
-
if debts_df
|
|
145
|
+
if u.is_dataframe_empty(debts_df):
|
|
134
146
|
return 0.0
|
|
135
147
|
|
|
136
148
|
total_payments = 0.0
|
|
137
149
|
|
|
138
150
|
for _, debt in debts_df.iterrows():
|
|
151
|
+
# Skip if active column exists and is False (treat NaN/None as True)
|
|
152
|
+
if not u.is_row_active(debt):
|
|
153
|
+
continue
|
|
154
|
+
|
|
139
155
|
start_year = int(debt["year"])
|
|
140
156
|
term = int(debt["term"])
|
|
141
157
|
end_year = start_year + term
|
|
@@ -168,12 +184,16 @@ def get_debt_balances_for_year(debts_df, year):
|
|
|
168
184
|
float
|
|
169
185
|
Total remaining debt balances at end of year
|
|
170
186
|
"""
|
|
171
|
-
if debts_df
|
|
187
|
+
if u.is_dataframe_empty(debts_df):
|
|
172
188
|
return 0.0
|
|
173
189
|
|
|
174
190
|
total_balance = 0.0
|
|
175
191
|
|
|
176
192
|
for _, debt in debts_df.iterrows():
|
|
193
|
+
# Skip if active column exists and is False (treat NaN/None as True)
|
|
194
|
+
if not u.is_row_active(debt):
|
|
195
|
+
continue
|
|
196
|
+
|
|
177
197
|
start_year = int(debt["year"])
|
|
178
198
|
term = int(debt["term"])
|
|
179
199
|
end_year = start_year + term
|
|
@@ -216,12 +236,16 @@ def get_debt_payments_array(debts_df, N_n, thisyear=None):
|
|
|
216
236
|
if thisyear is None:
|
|
217
237
|
thisyear = date.today().year
|
|
218
238
|
|
|
219
|
-
if debts_df
|
|
239
|
+
if u.is_dataframe_empty(debts_df):
|
|
220
240
|
return np.zeros(N_n)
|
|
221
241
|
|
|
222
242
|
payments_n = np.zeros(N_n)
|
|
223
243
|
|
|
224
244
|
for _, debt in debts_df.iterrows():
|
|
245
|
+
# Skip if active column exists and is False (treat NaN/None as True)
|
|
246
|
+
if not u.is_row_active(debt):
|
|
247
|
+
continue
|
|
248
|
+
|
|
225
249
|
start_year = int(debt["year"])
|
|
226
250
|
term = int(debt["term"])
|
|
227
251
|
end_year = start_year + term
|
|
@@ -264,13 +288,17 @@ def get_remaining_debt_balance(debts_df, N_n, thisyear=None):
|
|
|
264
288
|
if thisyear is None:
|
|
265
289
|
thisyear = date.today().year
|
|
266
290
|
|
|
267
|
-
if debts_df
|
|
291
|
+
if u.is_dataframe_empty(debts_df):
|
|
268
292
|
return 0.0
|
|
269
293
|
|
|
270
294
|
end_year = thisyear + N_n - 1
|
|
271
295
|
total_balance = 0.0
|
|
272
296
|
|
|
273
297
|
for _, debt in debts_df.iterrows():
|
|
298
|
+
# Skip if active column exists and is False (treat NaN/None as True)
|
|
299
|
+
if not u.is_row_active(debt):
|
|
300
|
+
continue
|
|
301
|
+
|
|
274
302
|
start_year = int(debt["year"])
|
|
275
303
|
term = int(debt["term"])
|
|
276
304
|
loan_end_year = start_year + term
|
owlplanner/fixedassets.py
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
"""
|
|
2
|
+
Fixed assets management and tax calculation module.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
This module provides functions for handling fixed assets (such as real estate)
|
|
5
|
+
and calculating tax implications when they are sold or disposed of, including
|
|
6
|
+
primary residence exclusion rules.
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
tax implications when they are sold or disposed of.
|
|
8
|
+
Copyright (C) 2025-2026 The Owlplanner Authors
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
This program is free software: you can redistribute it and/or modify
|
|
11
|
+
it under the terms of the GNU General Public License as published by
|
|
12
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
13
|
+
(at your option) any later version.
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
This program is distributed in the hope that it will be useful,
|
|
16
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18
|
+
GNU General Public License for more details.
|
|
11
19
|
|
|
20
|
+
You should have received a copy of the GNU General Public License
|
|
21
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
22
|
"""
|
|
13
23
|
|
|
14
24
|
######################################################################
|
|
@@ -16,6 +26,8 @@ import numpy as np
|
|
|
16
26
|
import pandas as pd # noqa: F401
|
|
17
27
|
from datetime import date
|
|
18
28
|
|
|
29
|
+
from . import utils as u
|
|
30
|
+
|
|
19
31
|
|
|
20
32
|
# Primary residence exclusion limits (2025 tax year)
|
|
21
33
|
RESIDENCE_EXCLUSION_SINGLE = 250000
|
|
@@ -55,7 +67,9 @@ def get_fixed_assets_arrays(fixed_assets_df, N_n, thisyear=None, filing_status="
|
|
|
55
67
|
Parameters:
|
|
56
68
|
-----------
|
|
57
69
|
fixed_assets_df : pd.DataFrame
|
|
58
|
-
DataFrame with columns: name, type, basis, value, rate, yod, commission
|
|
70
|
+
DataFrame with columns: name, type, year, basis, value, rate, yod, commission
|
|
71
|
+
where 'year' is the reference year (this year or after). Basis and
|
|
72
|
+
value are in reference-year dollars.
|
|
59
73
|
N_n : int
|
|
60
74
|
Number of years in the plan (length of output arrays)
|
|
61
75
|
thisyear : int, optional
|
|
@@ -76,7 +90,7 @@ def get_fixed_assets_arrays(fixed_assets_df, N_n, thisyear=None, filing_status="
|
|
|
76
90
|
if thisyear is None:
|
|
77
91
|
thisyear = date.today().year
|
|
78
92
|
|
|
79
|
-
if fixed_assets_df
|
|
93
|
+
if u.is_dataframe_empty(fixed_assets_df):
|
|
80
94
|
return np.zeros(N_n), np.zeros(N_n), np.zeros(N_n)
|
|
81
95
|
|
|
82
96
|
tax_free_n = np.zeros(N_n)
|
|
@@ -90,23 +104,54 @@ def get_fixed_assets_arrays(fixed_assets_df, N_n, thisyear=None, filing_status="
|
|
|
90
104
|
residence_exclusion = RESIDENCE_EXCLUSION_SINGLE
|
|
91
105
|
|
|
92
106
|
for _, asset in fixed_assets_df.iterrows():
|
|
107
|
+
# Skip if active column exists and is False (treat NaN/None as True)
|
|
108
|
+
if not u.is_row_active(asset):
|
|
109
|
+
continue
|
|
110
|
+
|
|
93
111
|
asset_type = str(asset["type"]).lower()
|
|
94
112
|
basis = float(asset["basis"])
|
|
95
|
-
|
|
113
|
+
value_at_reference = float(asset["value"]) # Value at reference year
|
|
96
114
|
annual_rate = float(asset["rate"])
|
|
115
|
+
# Get reference year, defaulting to thisyear for backward compatibility
|
|
116
|
+
if "year" in asset.index and not pd.isna(asset["year"]):
|
|
117
|
+
reference_year = int(asset["year"])
|
|
118
|
+
else:
|
|
119
|
+
reference_year = thisyear
|
|
97
120
|
yod = int(asset["yod"]) # Year of disposition
|
|
98
121
|
commission_pct = float(asset["commission"]) / 100.0
|
|
99
122
|
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
end_year = thisyear + N_n - 1 # Last year of the plan
|
|
124
|
+
|
|
125
|
+
# Skip if asset reference year is after the plan ends
|
|
126
|
+
if reference_year > end_year:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
# Account for negative or null yod with reference to end of plan
|
|
130
|
+
if yod <= 0:
|
|
131
|
+
yod = end_year + yod + 1
|
|
132
|
+
|
|
133
|
+
# Skip if disposition is before reference year (invalid)
|
|
134
|
+
if yod < reference_year:
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
# Skip if disposition is before the plan starts
|
|
138
|
+
if yod < thisyear:
|
|
103
139
|
continue
|
|
104
140
|
|
|
141
|
+
# Only process assets disposed during the plan (yod <= end_year)
|
|
142
|
+
# IMPORTANT: Assets with yod > end_year are NOT processed here to avoid double counting.
|
|
143
|
+
# They are handled separately in get_fixed_assets_bequest_value().
|
|
144
|
+
if yod > end_year:
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
# Disposition at beginning of yod (within plan duration)
|
|
105
148
|
n = yod - thisyear
|
|
149
|
+
# Asset assessed at beginning of reference_year, disposed at beginning of yod
|
|
150
|
+
# Growth period: from start of reference_year to start of yod = (yod - reference_year) years
|
|
151
|
+
years_from_reference_to_disposition = yod - reference_year
|
|
106
152
|
|
|
107
153
|
# Calculate future value at disposition
|
|
108
|
-
|
|
109
|
-
future_value = calculate_future_value(current_value, annual_rate, years_to_disposition)
|
|
154
|
+
future_value = calculate_future_value(value_at_reference, annual_rate, years_from_reference_to_disposition)
|
|
110
155
|
|
|
111
156
|
# Calculate proceeds after commission
|
|
112
157
|
commission_amount = future_value * commission_pct
|
|
@@ -170,7 +215,9 @@ def get_fixed_assets_bequest_value(fixed_assets_df, N_n, thisyear=None):
|
|
|
170
215
|
Parameters:
|
|
171
216
|
-----------
|
|
172
217
|
fixed_assets_df : pd.DataFrame
|
|
173
|
-
DataFrame with columns: name, type, basis, value, rate, yod, commission
|
|
218
|
+
DataFrame with columns: name, type, year, basis, value, rate, yod, commission
|
|
219
|
+
where 'year' is the reference year (this year or after). Basis and
|
|
220
|
+
value are in reference-year dollars.
|
|
174
221
|
N_n : int
|
|
175
222
|
Number of years in the plan
|
|
176
223
|
thisyear : int, optional
|
|
@@ -186,29 +233,56 @@ def get_fixed_assets_bequest_value(fixed_assets_df, N_n, thisyear=None):
|
|
|
186
233
|
if thisyear is None:
|
|
187
234
|
thisyear = date.today().year
|
|
188
235
|
|
|
189
|
-
if fixed_assets_df
|
|
236
|
+
if u.is_dataframe_empty(fixed_assets_df):
|
|
190
237
|
return 0.0
|
|
191
238
|
|
|
192
|
-
|
|
239
|
+
end_year = thisyear + N_n - 1 # Last year of the plan
|
|
193
240
|
total_bequest_value = 0.0
|
|
194
241
|
|
|
195
242
|
for _, asset in fixed_assets_df.iterrows():
|
|
243
|
+
# Skip if active column exists and is False (treat NaN/None as True)
|
|
244
|
+
if not u.is_row_active(asset):
|
|
245
|
+
continue
|
|
246
|
+
|
|
247
|
+
# Get reference year, defaulting to thisyear for backward compatibility
|
|
248
|
+
if "year" in asset.index and not pd.isna(asset["year"]):
|
|
249
|
+
reference_year = int(asset["year"])
|
|
250
|
+
else:
|
|
251
|
+
reference_year = thisyear
|
|
196
252
|
yod = int(asset["yod"]) # Year of disposition
|
|
197
253
|
|
|
198
|
-
#
|
|
199
|
-
if
|
|
200
|
-
|
|
254
|
+
# Skip if asset reference year is after the plan ends
|
|
255
|
+
if reference_year > end_year:
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
# Account for negative or null yod with reference to end of plan
|
|
259
|
+
if yod <= 0:
|
|
260
|
+
yod = end_year + yod + 1
|
|
261
|
+
|
|
262
|
+
# Skip if disposition is before reference year (invalid)
|
|
263
|
+
if yod < reference_year:
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
# Only consider assets with yod past the end of the plan (not disposed during the plan)
|
|
267
|
+
# IMPORTANT: Assets with yod <= end_year are NOT processed here to avoid double counting.
|
|
268
|
+
# They are handled separately in get_fixed_assets_arrays() where they are disposed during the plan.
|
|
269
|
+
# These assets (yod > end_year) are assumed to be liquidated at the end of the plan and added to the bequest
|
|
270
|
+
if yod > end_year:
|
|
271
|
+
value_at_reference = float(asset["value"]) # Value at reference year
|
|
201
272
|
annual_rate = float(asset["rate"])
|
|
202
273
|
commission_pct = float(asset["commission"]) / 100.0
|
|
203
274
|
|
|
204
275
|
# Calculate future value at the end of the plan
|
|
205
|
-
|
|
276
|
+
# Asset assessed at beginning of reference_year, liquidated at end of end_year
|
|
277
|
+
# Growth period: from start of reference_year to end of end_year = (end_year - reference_year + 1) years
|
|
278
|
+
years_from_reference_to_end = end_year - reference_year + 1
|
|
279
|
+
future_value = calculate_future_value(value_at_reference, annual_rate, years_from_reference_to_end)
|
|
206
280
|
|
|
207
281
|
# Calculate proceeds after commission
|
|
208
282
|
commission_amount = future_value * commission_pct
|
|
209
283
|
proceeds = future_value - commission_amount
|
|
210
284
|
|
|
211
|
-
# Add to total bequest value (full proceeds, no tax)
|
|
285
|
+
# Add to total bequest value (full proceeds, no tax - step-up in basis for heirs)
|
|
212
286
|
total_bequest_value += proceeds
|
|
213
287
|
|
|
214
288
|
return total_bequest_value
|