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/tax2025.py
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Owl/tax2025
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
A retirement planner using linear programming optimization.
|
|
7
|
-
|
|
8
|
-
See companion document for a complete explanation and description
|
|
9
|
-
of all variables and parameters.
|
|
10
|
-
|
|
11
|
-
Module to handle all tax calculations.
|
|
12
|
-
|
|
13
|
-
Copyright © 2024 - Martin-D. Lacasse
|
|
14
|
-
|
|
15
|
-
Disclaimers: This code is for educational purposes only and does not constitute financial advice.
|
|
16
|
-
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
import numpy as np
|
|
20
|
-
from datetime import date
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
##############################################################################
|
|
24
|
-
# Prepare the data.
|
|
25
|
-
|
|
26
|
-
taxBracketNames = ["10%", "12/15%", "22/25%", "24/28%", "32/33%", "35%", "37/40%"]
|
|
27
|
-
|
|
28
|
-
rates_OBBBA = np.array([0.10, 0.12, 0.22, 0.24, 0.32, 0.35, 0.370])
|
|
29
|
-
rates_preTCJA = np.array([0.10, 0.15, 0.25, 0.28, 0.33, 0.35, 0.396])
|
|
30
|
-
|
|
31
|
-
###############################################################################
|
|
32
|
-
# Start of section where rates need to be actualized every year.
|
|
33
|
-
###############################################################################
|
|
34
|
-
# Single [0] and married filing jointly [1].
|
|
35
|
-
|
|
36
|
-
# These are 2025 current.
|
|
37
|
-
taxBrackets_OBBBA = np.array(
|
|
38
|
-
[
|
|
39
|
-
[11925, 48475, 103350, 197300, 250525, 626350, 9999999],
|
|
40
|
-
[23850, 96950, 206700, 394600, 501050, 751600, 9999999],
|
|
41
|
-
]
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
irmaaBrackets = np.array(
|
|
45
|
-
[
|
|
46
|
-
[0, 106000, 133000, 167000, 200000, 500000],
|
|
47
|
-
[0, 212000, 266000, 334000, 400000, 750000],
|
|
48
|
-
]
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
# Index [0] stores the standard Medicare part B premium.
|
|
52
|
-
# Following values are incremental IRMAA part B monthly fees.
|
|
53
|
-
irmaaFees = 12 * np.array([185.00, 74.00, 111.00, 110.90, 111.00, 37.00])
|
|
54
|
-
|
|
55
|
-
# Make projection for pre-TCJA using 2017 to current year.
|
|
56
|
-
# taxBrackets_2017 = np.array(
|
|
57
|
-
# [ [9325, 37950, 91900, 191650, 416700, 418400, 9999999],
|
|
58
|
-
# [18650, 75900, 153100, 233350, 416700, 470700, 9999999],
|
|
59
|
-
# ])
|
|
60
|
-
#
|
|
61
|
-
# stdDeduction_2017 = [6350, 12700]
|
|
62
|
-
#
|
|
63
|
-
# For 2025, I used a 30.5% adjustment from 2017, rounded to closest 50.
|
|
64
|
-
#
|
|
65
|
-
# These are speculated.
|
|
66
|
-
taxBrackets_preTCJA = np.array(
|
|
67
|
-
[
|
|
68
|
-
[12150, 49550, 119950, 250200, 544000, 546200, 9999999], # Single
|
|
69
|
-
[24350, 99100, 199850, 304600, 543950, 614450, 9999999], # MFJ
|
|
70
|
-
]
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
# These are 2025 current.
|
|
74
|
-
stdDeduction_OBBBA = np.array([15750, 31500]) # Single, MFJ
|
|
75
|
-
# These are speculated (adjusted for inflation).
|
|
76
|
-
stdDeduction_preTCJA = np.array([8300, 16600]) # Single, MFJ
|
|
77
|
-
|
|
78
|
-
# These are current (adjusted for inflation) per individual.
|
|
79
|
-
extra65Deduction = np.array([2000, 1600]) # Single, MFJ
|
|
80
|
-
|
|
81
|
-
# Thresholds setting capital gains brackets 0%, 15%, 20% (adjusted for inflation).
|
|
82
|
-
capGainRates = np.array(
|
|
83
|
-
[
|
|
84
|
-
[48350, 533400],
|
|
85
|
-
[96700, 600050],
|
|
86
|
-
]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
# Thresholds for net investment income tax (not adjusted for inflation).
|
|
90
|
-
niitThreshold = np.array([200000, 250000])
|
|
91
|
-
niitRate = 0.038
|
|
92
|
-
|
|
93
|
-
# Thresholds for 65+ bonus for circumventing tax on social security.
|
|
94
|
-
bonusThreshold = np.array([75000, 150000])
|
|
95
|
-
|
|
96
|
-
###############################################################################
|
|
97
|
-
# End of section where rates need to be actualized every year.
|
|
98
|
-
###############################################################################
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def mediVals(yobs, horizons, gamma_n, Nn, Nq):
|
|
102
|
-
"""
|
|
103
|
-
Return tuple (nm, L, C) of year index when Medicare starts and vectors L, and C
|
|
104
|
-
defining end points of constant piecewise linear functions representing IRMAA fees.
|
|
105
|
-
"""
|
|
106
|
-
thisyear = date.today().year
|
|
107
|
-
assert Nq == len(irmaaFees), f"Inconsistent value of Nq: {Nq}."
|
|
108
|
-
assert Nq == len(irmaaBrackets[0]), "Inconsistent IRMAA brackets array."
|
|
109
|
-
Ni = len(yobs)
|
|
110
|
-
# What index year will Medicare start? 65 - age.
|
|
111
|
-
nm = 65 - (thisyear - yobs)
|
|
112
|
-
nm = np.min(nm)
|
|
113
|
-
# Has it already started?
|
|
114
|
-
nm = max(0, nm)
|
|
115
|
-
Nmed = Nn - nm
|
|
116
|
-
|
|
117
|
-
L = np.zeros((Nmed, Nq-1))
|
|
118
|
-
C = np.zeros((Nmed, Nq))
|
|
119
|
-
|
|
120
|
-
# Year starts at offset nm in the plan.
|
|
121
|
-
for nn in range(Nmed):
|
|
122
|
-
imed = 0
|
|
123
|
-
n = nm + nn
|
|
124
|
-
if thisyear + n - yobs[0] >= 65 and n < horizons[0]:
|
|
125
|
-
imed += 1
|
|
126
|
-
if Ni == 2 and thisyear + n - yobs[1] >= 65 and n < horizons[1]:
|
|
127
|
-
imed += 1
|
|
128
|
-
if imed:
|
|
129
|
-
status = 0 if Ni == 1 else 1 if n < horizons[0] and n < horizons[1] else 0
|
|
130
|
-
L[nn] = gamma_n[n] * irmaaBrackets[status][1:]
|
|
131
|
-
C[nn] = imed * gamma_n[n] * irmaaFees
|
|
132
|
-
else:
|
|
133
|
-
raise RuntimeError("mediVals: This should never happen.")
|
|
134
|
-
|
|
135
|
-
return nm, L, C
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def capitalGainTaxRate(Ni, magi_n, gamma_n, nd, Nn):
|
|
139
|
-
"""
|
|
140
|
-
Return an array of decimal rates for capital gains.
|
|
141
|
-
Parameter nd is the index year of first passing of a spouse, if applicable,
|
|
142
|
-
nd == Nn for single individuals.
|
|
143
|
-
"""
|
|
144
|
-
status = Ni - 1
|
|
145
|
-
cgRate_n = np.zeros(Nn)
|
|
146
|
-
|
|
147
|
-
for n in range(Nn):
|
|
148
|
-
if status and n == nd:
|
|
149
|
-
status -= 1
|
|
150
|
-
|
|
151
|
-
if magi_n[n] > gamma_n[n] * capGainRates[status][1]:
|
|
152
|
-
cgRate_n[n] = 0.20
|
|
153
|
-
elif magi_n[n] > gamma_n[n] * capGainRates[status][0]:
|
|
154
|
-
cgRate_n[n] = 0.15
|
|
155
|
-
|
|
156
|
-
return cgRate_n
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def mediCosts(yobs, horizons, magi, prevmagi, gamma_n, Nn):
|
|
160
|
-
"""
|
|
161
|
-
Compute Medicare costs directly.
|
|
162
|
-
"""
|
|
163
|
-
thisyear = date.today().year
|
|
164
|
-
Ni = len(yobs)
|
|
165
|
-
costs = np.zeros(Nn)
|
|
166
|
-
for n in range(Nn):
|
|
167
|
-
status = 0 if Ni == 1 else 1 if n < horizons[0] and n < horizons[1] else 0
|
|
168
|
-
for i in range(Ni):
|
|
169
|
-
if thisyear + n - yobs[i] >= 65 and n < horizons[i]:
|
|
170
|
-
# Start with the (inflation-adjusted) basic Medicare part B premium.
|
|
171
|
-
costs[n] += gamma_n[n] * irmaaFees[0]
|
|
172
|
-
if n < 2:
|
|
173
|
-
mymagi = prevmagi[n]
|
|
174
|
-
else:
|
|
175
|
-
mymagi = magi[n - 2]
|
|
176
|
-
for q in range(1, 6):
|
|
177
|
-
if mymagi > gamma_n[n] * irmaaBrackets[status][q]:
|
|
178
|
-
costs[n] += gamma_n[n] * irmaaFees[q]
|
|
179
|
-
|
|
180
|
-
return costs
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def taxParams(yobs, i_d, n_d, N_n, gamma_n, MAGI_n, yOBBBA=2099):
|
|
184
|
-
"""
|
|
185
|
-
Input is year of birth, index of shortest-lived individual,
|
|
186
|
-
lifespan of shortest-lived individual, total number of years
|
|
187
|
-
in the plan, and the year that preTCJA rates might come back.
|
|
188
|
-
|
|
189
|
-
It returns 3 time series:
|
|
190
|
-
1) Standard deductions at year n (sigma_n).
|
|
191
|
-
2) Tax rate in year n (theta_tn)
|
|
192
|
-
3) Delta from top to bottom of tax brackets (Delta_tn)
|
|
193
|
-
This is pure speculation on future values.
|
|
194
|
-
Returned values are not indexed for inflation.
|
|
195
|
-
"""
|
|
196
|
-
# Compute the deltas in-place between brackets, starting from the end.
|
|
197
|
-
deltaBrackets_OBBBA = np.array(taxBrackets_OBBBA)
|
|
198
|
-
deltaBrackets_preTCJA = np.array(taxBrackets_preTCJA)
|
|
199
|
-
for t in range(6, 0, -1):
|
|
200
|
-
for i in range(2):
|
|
201
|
-
deltaBrackets_OBBBA[i, t] -= deltaBrackets_OBBBA[i, t - 1]
|
|
202
|
-
deltaBrackets_preTCJA[i, t] -= deltaBrackets_preTCJA[i, t - 1]
|
|
203
|
-
|
|
204
|
-
# Prepare the 3 arrays to return - use transpose for easy slicing.
|
|
205
|
-
sigmaBar = np.zeros((N_n))
|
|
206
|
-
Delta = np.zeros((N_n, 7))
|
|
207
|
-
theta = np.zeros((N_n, 7))
|
|
208
|
-
|
|
209
|
-
filingStatus = len(yobs) - 1
|
|
210
|
-
souls = list(range(len(yobs)))
|
|
211
|
-
thisyear = date.today().year
|
|
212
|
-
|
|
213
|
-
for n in range(N_n):
|
|
214
|
-
# First check if shortest-lived individual is still with us.
|
|
215
|
-
if n == n_d:
|
|
216
|
-
souls.remove(i_d)
|
|
217
|
-
filingStatus -= 1
|
|
218
|
-
|
|
219
|
-
if thisyear + n < yOBBBA:
|
|
220
|
-
sigmaBar[n] = stdDeduction_OBBBA[filingStatus] * gamma_n[n]
|
|
221
|
-
Delta[n, :] = deltaBrackets_OBBBA[filingStatus, :]
|
|
222
|
-
else:
|
|
223
|
-
sigmaBar[n] = stdDeduction_preTCJA[filingStatus] * gamma_n[n]
|
|
224
|
-
Delta[n, :] = deltaBrackets_preTCJA[filingStatus, :]
|
|
225
|
-
|
|
226
|
-
# Add 65+ additional exemption(s) and "bonus" phasing out.
|
|
227
|
-
for i in souls:
|
|
228
|
-
if thisyear + n - yobs[i] >= 65:
|
|
229
|
-
sigmaBar[n] += extra65Deduction[filingStatus] * gamma_n[n]
|
|
230
|
-
if thisyear + n <= 2028:
|
|
231
|
-
sigmaBar[n] += 6000 * max(0, 1 - 0.06*max(0, MAGI_n[n] - bonusThreshold[filingStatus]))
|
|
232
|
-
|
|
233
|
-
# Fill in future tax rates for year n.
|
|
234
|
-
if thisyear + n < yOBBBA:
|
|
235
|
-
theta[n, :] = rates_OBBBA[:]
|
|
236
|
-
else:
|
|
237
|
-
theta[n, :] = rates_preTCJA[:]
|
|
238
|
-
|
|
239
|
-
Delta = Delta.transpose()
|
|
240
|
-
theta = theta.transpose()
|
|
241
|
-
|
|
242
|
-
# Return series unadjusted for inflation, except for sigmaBar, in STD order.
|
|
243
|
-
return sigmaBar, theta, Delta
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def taxBrackets(N_i, n_d, N_n, yOBBBA=2099):
|
|
247
|
-
"""
|
|
248
|
-
Return dictionary containing future tax brackets
|
|
249
|
-
unadjusted for inflation for plotting.
|
|
250
|
-
"""
|
|
251
|
-
if not (0 < N_i <= 2):
|
|
252
|
-
raise ValueError(f"Cannot process {N_i} individuals.")
|
|
253
|
-
|
|
254
|
-
n_d = min(n_d, N_n)
|
|
255
|
-
status = N_i - 1
|
|
256
|
-
|
|
257
|
-
# Number of years left in OBBBA from this year.
|
|
258
|
-
thisyear = date.today().year
|
|
259
|
-
if yOBBBA < thisyear:
|
|
260
|
-
raise ValueError(f"Expiration year {yOBBBA} cannot be in the past.")
|
|
261
|
-
|
|
262
|
-
ytc = yOBBBA - thisyear
|
|
263
|
-
|
|
264
|
-
data = {}
|
|
265
|
-
for t in range(len(taxBracketNames) - 1):
|
|
266
|
-
array = np.zeros(N_n)
|
|
267
|
-
for n in range(N_n):
|
|
268
|
-
stat = status if n < n_d else 0
|
|
269
|
-
array[n] = taxBrackets_OBBBA[stat][t] if n < ytc else taxBrackets_preTCJA[stat][t]
|
|
270
|
-
|
|
271
|
-
data[taxBracketNames[t]] = array
|
|
272
|
-
|
|
273
|
-
return data
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
def computeNIIT(N_i, MAGI_n, I_n, Q_n, n_d, N_n):
|
|
277
|
-
"""
|
|
278
|
-
Compute ACA tax on Dividends (Q) and Interests (I).
|
|
279
|
-
For accounting for rent and/or trust income, one can easily add a column
|
|
280
|
-
to the Wages and Contributions file and add yearly amount to Q_n + I_n below.
|
|
281
|
-
"""
|
|
282
|
-
J_n = np.zeros(N_n)
|
|
283
|
-
status = N_i - 1
|
|
284
|
-
|
|
285
|
-
for n in range(N_n):
|
|
286
|
-
if status and n == n_d:
|
|
287
|
-
status -= 1
|
|
288
|
-
|
|
289
|
-
Gmax = niitThreshold[status]
|
|
290
|
-
if MAGI_n[n] > Gmax:
|
|
291
|
-
J_n[n] = niitRate * min(MAGI_n[n] - Gmax, I_n[n] + Q_n[n])
|
|
292
|
-
|
|
293
|
-
return J_n
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
def rho_in(yobs, N_n):
|
|
297
|
-
"""
|
|
298
|
-
Return Required Minimum Distribution fractions for each individual.
|
|
299
|
-
This implementation does not support spouses with more than
|
|
300
|
-
10-year difference.
|
|
301
|
-
It starts at age 73 until it goes to 75 in 2033.
|
|
302
|
-
"""
|
|
303
|
-
# Notice that table starts at age 72.
|
|
304
|
-
rmdTable = [
|
|
305
|
-
27.4,
|
|
306
|
-
26.5,
|
|
307
|
-
25.5,
|
|
308
|
-
24.6,
|
|
309
|
-
23.7,
|
|
310
|
-
22.9,
|
|
311
|
-
22.0,
|
|
312
|
-
21.1,
|
|
313
|
-
20.2,
|
|
314
|
-
19.4,
|
|
315
|
-
18.5,
|
|
316
|
-
17.7,
|
|
317
|
-
16.8,
|
|
318
|
-
16.0,
|
|
319
|
-
15.2,
|
|
320
|
-
14.4,
|
|
321
|
-
13.7,
|
|
322
|
-
12.9,
|
|
323
|
-
12.2,
|
|
324
|
-
11.5,
|
|
325
|
-
10.8,
|
|
326
|
-
10.1,
|
|
327
|
-
9.5,
|
|
328
|
-
8.9,
|
|
329
|
-
8.4,
|
|
330
|
-
7.8,
|
|
331
|
-
7.3,
|
|
332
|
-
6.8,
|
|
333
|
-
6.4,
|
|
334
|
-
6.0,
|
|
335
|
-
5.6,
|
|
336
|
-
5.2,
|
|
337
|
-
4.9,
|
|
338
|
-
4.6,
|
|
339
|
-
]
|
|
340
|
-
|
|
341
|
-
N_i = len(yobs)
|
|
342
|
-
if N_i == 2 and abs(yobs[0] - yobs[1]) > 10:
|
|
343
|
-
raise RuntimeError("RMD: Unsupported age difference of more than 10 years.")
|
|
344
|
-
|
|
345
|
-
rho = np.zeros((N_i, N_n))
|
|
346
|
-
thisyear = date.today().year
|
|
347
|
-
for i in range(N_i):
|
|
348
|
-
agenow = thisyear - yobs[i]
|
|
349
|
-
# Account for increase of RMD age between 2023 and 2032.
|
|
350
|
-
yrmd = 70 if yobs[i] < 1949 else 72 if 1949 <= yobs[i] <= 1950 else 73 if 1951 <= yobs[i] <= 1959 else 75
|
|
351
|
-
for n in range(N_n):
|
|
352
|
-
yage = agenow + n
|
|
353
|
-
|
|
354
|
-
if yage < yrmd:
|
|
355
|
-
pass # rho[i][n] = 0
|
|
356
|
-
else:
|
|
357
|
-
rho[i][n] = 1.0 / rmdTable[yage - 72]
|
|
358
|
-
|
|
359
|
-
return rho
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
owlplanner/__init__.py,sha256=hJ2i4m2JpHPAKyQLjYOXpJzeEsgcTcKD-Vhm0AIjjWg,592
|
|
2
|
-
owlplanner/abcapi.py,sha256=Eo2Z2doVsaao3HPZ0IICwqjhcgeerxrjSTSt-7f-BFY,6849
|
|
3
|
-
owlplanner/config.py,sha256=HzPLp9queRw_q73QMXlRTEkdif7XPBAAXG8g3eBXNLM,12527
|
|
4
|
-
owlplanner/debts.py,sha256=H3VrRZCjjpP4tsjiy2YRrJawypxE_8W2SSCHMTuUOVE,8628
|
|
5
|
-
owlplanner/fixedassets.py,sha256=BbnLEAMkThWx7DnQ8xFV7xtwHpIhaC1b9UkUoMNeHgM,7742
|
|
6
|
-
owlplanner/mylogging.py,sha256=OVGeDFO7LIZG91R6HMpZBzjno-B8PH8Fo00Jw2Pdgqw,2558
|
|
7
|
-
owlplanner/plan.py,sha256=x7oiw6dgMPPnLZZSOXJ1Wx1fT_SW6_MBfYsDACPlpEA,129482
|
|
8
|
-
owlplanner/progress.py,sha256=J1iaYUC6OqHDew-KdoTUWHgAagBV8Gvptlghts-WFx8,1843
|
|
9
|
-
owlplanner/rates.py,sha256=aBFYCD6U3St--SvL6nG46wfKIA1YAey4-LHKza-jQUs,14115
|
|
10
|
-
owlplanner/socialsecurity.py,sha256=0QizQjO-hzJQzQstqqkteSX8qzHXUiNOps4xzcz2GoA,6715
|
|
11
|
-
owlplanner/tax2025.py,sha256=qUKhVIN1KJdbiVGPWtp0ftbV5sMuP3aK_WwJZBgLDhE,11418
|
|
12
|
-
owlplanner/tax2026.py,sha256=IKILbI_waAnzSXDZmcbdB-R8tq2VjEKFmQNQ_La9TAM,12252
|
|
13
|
-
owlplanner/timelists.py,sha256=0n_sDwVWaBa6JErz2mFEWz89s2QpC4qY8hvkkC2EzqU,7783
|
|
14
|
-
owlplanner/utils.py,sha256=wnniCgvMl-0W68W6mjeCBWFlt1tuaGc03pt5KPXRCIs,3266
|
|
15
|
-
owlplanner/version.py,sha256=iVYPFVMpNOEb6-f3knyQzB17C4qKOmp_ApxaCpjAWCw,28
|
|
16
|
-
owlplanner/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
owlplanner/data/awi.csv,sha256=w9YwPEqhxqo1Czumh3ihLXLPtk8kJQgqWKClMysOq-c,1625
|
|
18
|
-
owlplanner/data/bendpoints.csv,sha256=h0a7_XqTuyHWe5ZlS3mjIcAWqOMG_93GoiETS-1xxUY,746
|
|
19
|
-
owlplanner/data/newawi.csv,sha256=w9YwPEqhxqo1Czumh3ihLXLPtk8kJQgqWKClMysOq-c,1625
|
|
20
|
-
owlplanner/data/rates.csv,sha256=6fxg56BVVORrj9wJlUGFdGXKvOX5r7CSca8uhUbbuIU,3734
|
|
21
|
-
owlplanner/plotting/__init__.py,sha256=uhxqtUi0OI-QWNOO2LkXgQViW_9yM3rYb-204Wit974,250
|
|
22
|
-
owlplanner/plotting/base.py,sha256=UimGKpMTV-dVm3BX5Apr_Ltorc7dlDLCRPRQ3RF_v7c,2578
|
|
23
|
-
owlplanner/plotting/factory.py,sha256=EDopIAPQr9zHRgemObko18FlCeRNhNCoLNNFAOq-X6s,1030
|
|
24
|
-
owlplanner/plotting/matplotlib_backend.py,sha256=AOEkapD94U5hGNoS0EdbRoe8mgdMHH4oOvkXADZS914,17957
|
|
25
|
-
owlplanner/plotting/plotly_backend.py,sha256=lv4AzrsHSOWzRgIFcaSYF5FfbkyGouX8dDpumLFXQdw,33151
|
|
26
|
-
owlplanner-2025.12.20.dist-info/METADATA,sha256=ZNqby1H7IzvVeXv4NvYBp_WlybFxb-Qr_rwSr0NptkY,46190
|
|
27
|
-
owlplanner-2025.12.20.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
28
|
-
owlplanner-2025.12.20.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
29
|
-
owlplanner-2025.12.20.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|