fundedness 0.1.0__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.

Potentially problematic release.


This version of fundedness might be problematic. Click here for more details.

@@ -0,0 +1,203 @@
1
+ """RMD-style withdrawal strategy."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import numpy as np
6
+
7
+ from fundedness.withdrawals.base import (
8
+ BaseWithdrawalPolicy,
9
+ WithdrawalContext,
10
+ WithdrawalDecision,
11
+ )
12
+
13
+
14
+ # IRS Uniform Lifetime Table (2024)
15
+ # Maps age to distribution period (divisor)
16
+ RMD_TABLE = {
17
+ 72: 27.4, 73: 26.5, 74: 25.5, 75: 24.6, 76: 23.7,
18
+ 77: 22.9, 78: 22.0, 79: 21.1, 80: 20.2, 81: 19.4,
19
+ 82: 18.5, 83: 17.7, 84: 16.8, 85: 16.0, 86: 15.2,
20
+ 87: 14.4, 88: 13.7, 89: 12.9, 90: 12.2, 91: 11.5,
21
+ 92: 10.8, 93: 10.1, 94: 9.5, 95: 8.9, 96: 8.4,
22
+ 97: 7.8, 98: 7.3, 99: 6.8, 100: 6.4, 101: 6.0,
23
+ 102: 5.6, 103: 5.2, 104: 4.9, 105: 4.6, 106: 4.3,
24
+ 107: 4.1, 108: 3.9, 109: 3.7, 110: 3.5, 111: 3.4,
25
+ 112: 3.3, 113: 3.1, 114: 3.0, 115: 2.9, 116: 2.8,
26
+ 117: 2.7, 118: 2.5, 119: 2.3, 120: 2.0,
27
+ }
28
+
29
+
30
+ def get_rmd_divisor(age: int) -> float:
31
+ """Get RMD distribution period for given age.
32
+
33
+ Args:
34
+ age: Current age
35
+
36
+ Returns:
37
+ Distribution period (divisor)
38
+ """
39
+ if age < 72:
40
+ # Extrapolate backwards (not actual RMD, but useful for strategy)
41
+ return 27.4 + (72 - age) * 1.0 # Approximate slope
42
+ elif age > 120:
43
+ return 2.0
44
+ else:
45
+ return RMD_TABLE.get(age, 2.0)
46
+
47
+
48
+ @dataclass
49
+ class RMDStylePolicy(BaseWithdrawalPolicy):
50
+ """RMD-style withdrawal strategy.
51
+
52
+ Uses IRS Required Minimum Distribution table to determine withdrawals.
53
+ Withdrawal = Portfolio Value / Distribution Period
54
+
55
+ This approach automatically increases withdrawal rate as you age,
56
+ similar to how RMDs work for tax-deferred accounts.
57
+ """
58
+
59
+ starting_age: int = 65
60
+ multiplier: float = 1.0 # Scale factor (1.0 = exact RMD, 1.5 = 150% of RMD)
61
+ start_before_72: bool = True # Apply RMD-style before actual RMD age
62
+
63
+ @property
64
+ def name(self) -> str:
65
+ mult_str = f" × {self.multiplier}" if self.multiplier != 1.0 else ""
66
+ return f"RMD-Style{mult_str}"
67
+
68
+ @property
69
+ def description(self) -> str:
70
+ return (
71
+ "Withdraw based on IRS RMD table divisors. "
72
+ "Withdrawal rate automatically increases with age."
73
+ )
74
+
75
+ def get_initial_withdrawal(self, initial_wealth: float) -> float:
76
+ """Calculate first year withdrawal."""
77
+ divisor = get_rmd_divisor(self.starting_age)
78
+ return (initial_wealth / divisor) * self.multiplier
79
+
80
+ def calculate_withdrawal(self, context: WithdrawalContext) -> WithdrawalDecision:
81
+ """Calculate RMD-style withdrawal.
82
+
83
+ Args:
84
+ context: Current state including age or year
85
+
86
+ Returns:
87
+ WithdrawalDecision based on RMD table
88
+ """
89
+ # Determine current age
90
+ if context.age is not None:
91
+ current_age = context.age
92
+ else:
93
+ current_age = self.starting_age + context.year
94
+
95
+ # Get divisor
96
+ divisor = get_rmd_divisor(current_age)
97
+
98
+ # Calculate withdrawal
99
+ if isinstance(context.current_wealth, np.ndarray):
100
+ amount = (context.current_wealth / divisor) * self.multiplier
101
+ else:
102
+ amount = (context.current_wealth / divisor) * self.multiplier
103
+
104
+ # Apply guardrails
105
+ amount, is_floor_breach, is_ceiling_hit = self.apply_guardrails(
106
+ amount, context.current_wealth
107
+ )
108
+
109
+ withdrawal_rate = 1 / divisor * self.multiplier
110
+
111
+ return WithdrawalDecision(
112
+ amount=amount,
113
+ is_floor_breach=is_floor_breach,
114
+ is_ceiling_hit=is_ceiling_hit,
115
+ notes=f"Age {current_age}, divisor: {divisor:.1f}, rate: {withdrawal_rate:.2%}",
116
+ )
117
+
118
+
119
+ @dataclass
120
+ class AmortizationPolicy(BaseWithdrawalPolicy):
121
+ """Amortization-based withdrawal strategy.
122
+
123
+ Treats the portfolio like a mortgage in reverse - calculates the level
124
+ payment that would exhaust the portfolio over the planning horizon
125
+ given expected returns.
126
+ """
127
+
128
+ starting_age: int = 65
129
+ planning_age: int = 95 # Age to plan to
130
+ expected_return: float = 0.04 # Expected real return
131
+ recalculate_annually: bool = True # Recalculate each year
132
+
133
+ @property
134
+ def name(self) -> str:
135
+ return "Amortization"
136
+
137
+ @property
138
+ def description(self) -> str:
139
+ return (
140
+ f"Calculate level payment to exhaust portfolio by age {self.planning_age} "
141
+ f"assuming {self.expected_return:.1%} real return."
142
+ )
143
+
144
+ def _calculate_pmt(self, wealth: float, years_remaining: int) -> float:
145
+ """Calculate amortization payment.
146
+
147
+ PMT = PV * r / (1 - (1+r)^-n)
148
+ """
149
+ if years_remaining <= 0:
150
+ return wealth # Spend it all
151
+
152
+ r = self.expected_return
153
+ n = years_remaining
154
+
155
+ if r == 0:
156
+ return wealth / n
157
+
158
+ # Standard amortization formula
159
+ pmt = wealth * r / (1 - (1 + r) ** (-n))
160
+ return pmt
161
+
162
+ def get_initial_withdrawal(self, initial_wealth: float) -> float:
163
+ """Calculate first year withdrawal."""
164
+ years = self.planning_age - self.starting_age
165
+ return self._calculate_pmt(initial_wealth, years)
166
+
167
+ def calculate_withdrawal(self, context: WithdrawalContext) -> WithdrawalDecision:
168
+ """Calculate amortization-based withdrawal.
169
+
170
+ Args:
171
+ context: Current state
172
+
173
+ Returns:
174
+ WithdrawalDecision based on amortization formula
175
+ """
176
+ # Determine current age and years remaining
177
+ if context.age is not None:
178
+ current_age = context.age
179
+ else:
180
+ current_age = self.starting_age + context.year
181
+
182
+ years_remaining = max(1, self.planning_age - current_age)
183
+
184
+ # Calculate payment
185
+ if isinstance(context.current_wealth, np.ndarray):
186
+ amount = np.array([
187
+ self._calculate_pmt(w, years_remaining)
188
+ for w in context.current_wealth
189
+ ])
190
+ else:
191
+ amount = self._calculate_pmt(context.current_wealth, years_remaining)
192
+
193
+ # Apply guardrails
194
+ amount, is_floor_breach, is_ceiling_hit = self.apply_guardrails(
195
+ amount, context.current_wealth
196
+ )
197
+
198
+ return WithdrawalDecision(
199
+ amount=amount,
200
+ is_floor_breach=is_floor_breach,
201
+ is_ceiling_hit=is_ceiling_hit,
202
+ notes=f"Years remaining: {years_remaining}",
203
+ )
@@ -0,0 +1,136 @@
1
+ """Variable Percentage Withdrawal (VPW) strategy."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import numpy as np
6
+
7
+ from fundedness.withdrawals.base import (
8
+ BaseWithdrawalPolicy,
9
+ WithdrawalContext,
10
+ WithdrawalDecision,
11
+ )
12
+
13
+
14
+ # VPW percentage table based on age and asset allocation
15
+ # Source: Bogleheads VPW methodology
16
+ # These are the percentages of portfolio to withdraw at each age
17
+ VPW_TABLE = {
18
+ # Age: {stock_pct: withdrawal_pct}
19
+ # Simplified table - in practice would interpolate
20
+ 60: {0: 0.037, 25: 0.039, 50: 0.042, 75: 0.046, 100: 0.051},
21
+ 65: {0: 0.041, 25: 0.044, 50: 0.047, 75: 0.052, 100: 0.058},
22
+ 70: {0: 0.047, 25: 0.050, 50: 0.054, 75: 0.060, 100: 0.068},
23
+ 75: {0: 0.054, 25: 0.058, 50: 0.064, 75: 0.071, 100: 0.081},
24
+ 80: {0: 0.064, 25: 0.069, 50: 0.076, 75: 0.086, 100: 0.099},
25
+ 85: {0: 0.078, 25: 0.084, 50: 0.093, 75: 0.106, 100: 0.124},
26
+ 90: {0: 0.097, 25: 0.106, 50: 0.118, 75: 0.135, 100: 0.160},
27
+ 95: {0: 0.127, 25: 0.139, 50: 0.156, 75: 0.180, 100: 0.214},
28
+ }
29
+
30
+
31
+ def get_vpw_rate(age: int, stock_allocation: int = 50) -> float:
32
+ """Get VPW withdrawal rate for given age and allocation.
33
+
34
+ Args:
35
+ age: Current age
36
+ stock_allocation: Stock allocation as integer percentage (0-100)
37
+
38
+ Returns:
39
+ Withdrawal rate as decimal
40
+ """
41
+ # Find bracketing ages
42
+ ages = sorted(VPW_TABLE.keys())
43
+
44
+ if age <= ages[0]:
45
+ age_key = ages[0]
46
+ elif age >= ages[-1]:
47
+ age_key = ages[-1]
48
+ else:
49
+ # Find closest age
50
+ age_key = min(ages, key=lambda x: abs(x - age))
51
+
52
+ # Find closest allocation
53
+ allocations = sorted(VPW_TABLE[age_key].keys())
54
+ if stock_allocation <= allocations[0]:
55
+ alloc_key = allocations[0]
56
+ elif stock_allocation >= allocations[-1]:
57
+ alloc_key = allocations[-1]
58
+ else:
59
+ alloc_key = min(allocations, key=lambda x: abs(x - stock_allocation))
60
+
61
+ return VPW_TABLE[age_key][alloc_key]
62
+
63
+
64
+ @dataclass
65
+ class VPWPolicy(BaseWithdrawalPolicy):
66
+ """Variable Percentage Withdrawal (VPW) strategy.
67
+
68
+ Withdrawal rate varies based on age and remaining life expectancy.
69
+ Uses actuarial tables to determine appropriate withdrawal percentage.
70
+ """
71
+
72
+ starting_age: int = 65
73
+ stock_allocation: int = 50 # As integer percentage
74
+ smoothing_factor: float = 0.0 # 0 = pure VPW, 1 = fully smoothed
75
+
76
+ @property
77
+ def name(self) -> str:
78
+ return "VPW"
79
+
80
+ @property
81
+ def description(self) -> str:
82
+ return (
83
+ "Variable Percentage Withdrawal based on age and life expectancy. "
84
+ "Withdrawal rate increases as you age."
85
+ )
86
+
87
+ def get_initial_withdrawal(self, initial_wealth: float) -> float:
88
+ """Calculate first year withdrawal."""
89
+ rate = get_vpw_rate(self.starting_age, self.stock_allocation)
90
+ return initial_wealth * rate
91
+
92
+ def calculate_withdrawal(self, context: WithdrawalContext) -> WithdrawalDecision:
93
+ """Calculate VPW withdrawal.
94
+
95
+ Args:
96
+ context: Current state including age or year
97
+
98
+ Returns:
99
+ WithdrawalDecision based on VPW table
100
+ """
101
+ # Determine current age
102
+ if context.age is not None:
103
+ current_age = context.age
104
+ else:
105
+ current_age = self.starting_age + context.year
106
+
107
+ # Get VPW rate for current age
108
+ vpw_rate = get_vpw_rate(current_age, self.stock_allocation)
109
+
110
+ # Calculate base withdrawal
111
+ if isinstance(context.current_wealth, np.ndarray):
112
+ base_amount = context.current_wealth * vpw_rate
113
+ else:
114
+ base_amount = context.current_wealth * vpw_rate
115
+
116
+ # Apply smoothing if requested
117
+ if self.smoothing_factor > 0 and context.previous_spending is not None:
118
+ smoothed = (
119
+ self.smoothing_factor * context.previous_spending
120
+ + (1 - self.smoothing_factor) * base_amount
121
+ )
122
+ amount = smoothed
123
+ else:
124
+ amount = base_amount
125
+
126
+ # Apply guardrails
127
+ amount, is_floor_breach, is_ceiling_hit = self.apply_guardrails(
128
+ amount, context.current_wealth
129
+ )
130
+
131
+ return WithdrawalDecision(
132
+ amount=amount,
133
+ is_floor_breach=is_floor_breach,
134
+ is_ceiling_hit=is_ceiling_hit,
135
+ notes=f"Age {current_age}, VPW rate: {vpw_rate:.2%}",
136
+ )
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: fundedness
3
+ Version: 0.1.0
4
+ Summary: A Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful visualizations
5
+ Project-URL: Homepage, https://github.com/engineerinvestor/financial-health-calculator
6
+ Project-URL: Documentation, https://engineerinvestor.github.io/financial-health-calculator/
7
+ Project-URL: Repository, https://github.com/engineerinvestor/financial-health-calculator
8
+ Author-email: Engineer Investor <egr.investor@gmail.com>
9
+ License-Expression: MIT
10
+ Keywords: CEFR,Monte Carlo,finance,planning,retirement,withdrawal
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Office/Business :: Financial :: Investment
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: numpy>=1.24.0
22
+ Requires-Dist: pandas>=2.0.0
23
+ Requires-Dist: plotly>=5.18.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: scipy>=1.11.0
26
+ Provides-Extra: all
27
+ Requires-Dist: fastapi>=0.108.0; extra == 'all'
28
+ Requires-Dist: hypothesis>=6.92.0; extra == 'all'
29
+ Requires-Dist: mkdocs-material>=9.5.0; extra == 'all'
30
+ Requires-Dist: mkdocs>=1.5.0; extra == 'all'
31
+ Requires-Dist: mkdocstrings[python]>=0.24.0; extra == 'all'
32
+ Requires-Dist: mypy>=1.8.0; extra == 'all'
33
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'all'
34
+ Requires-Dist: pytest>=7.4.0; extra == 'all'
35
+ Requires-Dist: ruff>=0.1.9; extra == 'all'
36
+ Requires-Dist: streamlit>=1.29.0; extra == 'all'
37
+ Requires-Dist: uvicorn[standard]>=0.25.0; extra == 'all'
38
+ Provides-Extra: api
39
+ Requires-Dist: fastapi>=0.108.0; extra == 'api'
40
+ Requires-Dist: uvicorn[standard]>=0.25.0; extra == 'api'
41
+ Provides-Extra: dev
42
+ Requires-Dist: hypothesis>=6.92.0; extra == 'dev'
43
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
44
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
45
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
46
+ Requires-Dist: ruff>=0.1.9; extra == 'dev'
47
+ Provides-Extra: docs
48
+ Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
49
+ Requires-Dist: mkdocs>=1.5.0; extra == 'docs'
50
+ Requires-Dist: mkdocstrings[python]>=0.24.0; extra == 'docs'
51
+ Provides-Extra: streamlit
52
+ Requires-Dist: streamlit>=1.29.0; extra == 'streamlit'
53
+ Description-Content-Type: text/markdown
54
+
55
+ # Financial Health Calculator
56
+
57
+ A comprehensive Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful Plotly visualizations.
58
+
59
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/engineerinvestor/financial-health-calculator/blob/main/examples/01_cefr_basics.ipynb)
60
+
61
+ ## Features
62
+
63
+ - **CEFR (Certainty-Equivalent Funded Ratio)**: A fundedness metric that accounts for taxes, liquidity, and concentration risk
64
+ - **Monte Carlo Simulations**: Project retirement outcomes with configurable market assumptions
65
+ - **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW, and RMD-style
66
+ - **Beautiful Visualizations**: Interactive Plotly charts with fan charts, waterfalls, and survival curves
67
+ - **REST API**: FastAPI backend for programmatic access
68
+ - **Streamlit App**: User-friendly web interface
69
+
70
+ ## Quick Start
71
+
72
+ ### Installation
73
+
74
+ ```bash
75
+ pip install git+https://github.com/engineerinvestor/financial-health-calculator.git
76
+ ```
77
+
78
+ For development with all extras:
79
+ ```bash
80
+ pip install "git+https://github.com/engineerinvestor/financial-health-calculator.git#egg=fundedness[all]"
81
+ ```
82
+
83
+ ### Basic Usage
84
+
85
+ ```python
86
+ from fundedness import Asset, BalanceSheet, Liability, compute_cefr
87
+ from fundedness.models.assets import AccountType, LiquidityClass, ConcentrationLevel
88
+
89
+ # Define your assets
90
+ assets = [
91
+ Asset(
92
+ name="401(k)",
93
+ value=500_000,
94
+ account_type=AccountType.TAX_DEFERRED,
95
+ liquidity_class=LiquidityClass.RETIREMENT,
96
+ concentration_level=ConcentrationLevel.DIVERSIFIED,
97
+ ),
98
+ Asset(
99
+ name="Roth IRA",
100
+ value=200_000,
101
+ account_type=AccountType.TAX_EXEMPT,
102
+ liquidity_class=LiquidityClass.RETIREMENT,
103
+ concentration_level=ConcentrationLevel.DIVERSIFIED,
104
+ ),
105
+ ]
106
+
107
+ # Define your spending
108
+ liabilities = [
109
+ Liability(name="Living Expenses", annual_amount=50_000, is_essential=True),
110
+ Liability(name="Travel", annual_amount=20_000, is_essential=False),
111
+ ]
112
+
113
+ # Calculate CEFR
114
+ result = compute_cefr(
115
+ balance_sheet=BalanceSheet(assets=assets),
116
+ liabilities=liabilities,
117
+ planning_horizon=30,
118
+ )
119
+
120
+ print(f"CEFR: {result.cefr:.2f}")
121
+ print(f"Funded: {result.is_funded}")
122
+ print(result.get_interpretation())
123
+ ```
124
+
125
+ ## Tutorials
126
+
127
+ - [CEFR Basics](https://colab.research.google.com/github/engineerinvestor/financial-health-calculator/blob/main/examples/01_cefr_basics.ipynb) - Introduction to the CEFR metric
128
+ - [Time Distribution Analysis](https://colab.research.google.com/github/engineerinvestor/financial-health-calculator/blob/main/examples/02_time_distribution.ipynb) - Monte Carlo simulations
129
+ - [Withdrawal Strategy Comparison](https://colab.research.google.com/github/engineerinvestor/financial-health-calculator/blob/main/examples/03_withdrawal_comparison.ipynb) - Compare different approaches
130
+
131
+ ## Running the Apps
132
+
133
+ ### Streamlit Web App
134
+
135
+ ```bash
136
+ streamlit run streamlit_app/app.py
137
+ ```
138
+
139
+ ### FastAPI REST API
140
+
141
+ ```bash
142
+ uvicorn api.main:app --reload
143
+ ```
144
+
145
+ API documentation available at `http://localhost:8000/docs`
146
+
147
+ ## Key Concepts
148
+
149
+ ### CEFR (Certainty-Equivalent Funded Ratio)
150
+
151
+ CEFR measures how well-funded your retirement is after accounting for:
152
+
153
+ - **Tax Haircuts**: What you'll owe when withdrawing from different account types
154
+ - **Liquidity Haircuts**: How easily you can access your assets
155
+ - **Reliability Haircuts**: Risk from concentrated positions
156
+
157
+ **Formula:**
158
+ ```
159
+ CEFR = Σ(Asset × (1-τ) × λ × ρ) / PV(Liabilities)
160
+ ```
161
+
162
+ Where τ = tax rate, λ = liquidity factor, ρ = reliability factor
163
+
164
+ **Interpretation:**
165
+ - CEFR ≥ 2.0: Excellent - Very well-funded
166
+ - CEFR 1.5-2.0: Strong - Well-funded with margin
167
+ - CEFR 1.0-1.5: Adequate - Fully funded
168
+ - CEFR < 1.0: Underfunded - Action needed
169
+
170
+ ### Withdrawal Strategies
171
+
172
+ | Strategy | Description | Best For |
173
+ |----------|-------------|----------|
174
+ | Fixed SWR | 4% of initial portfolio, adjusted for inflation | Predictability |
175
+ | % of Portfolio | Fixed % of current value | Market adaptation |
176
+ | Guardrails | Adjustable with floor/ceiling | Balance |
177
+ | VPW | Age-based variable percentage | Maximizing spending |
178
+ | RMD-Style | IRS distribution table based | Tax efficiency |
179
+
180
+ ## Development
181
+
182
+ ### Setup
183
+
184
+ ```bash
185
+ git clone https://github.com/engineerinvestor/financial-health-calculator.git
186
+ cd financial-health-calculator
187
+ pip install -e ".[dev]"
188
+ ```
189
+
190
+ ### Running Tests
191
+
192
+ ```bash
193
+ pytest
194
+ ```
195
+
196
+ ### Code Quality
197
+
198
+ ```bash
199
+ ruff check .
200
+ mypy fundedness
201
+ ```
202
+
203
+ ## Project Structure
204
+
205
+ ```
206
+ financial-health-calculator/
207
+ ├── fundedness/ # Core Python package
208
+ │ ├── models/ # Pydantic data models
209
+ │ ├── viz/ # Plotly visualizations
210
+ │ ├── withdrawals/ # Withdrawal strategies
211
+ │ ├── allocation/ # Asset allocation strategies
212
+ │ ├── cefr.py # CEFR calculation
213
+ │ ├── simulate.py # Monte Carlo engine
214
+ │ └── policies.py # Spending/allocation policies
215
+ ├── api/ # FastAPI REST API
216
+ ├── streamlit_app/ # Streamlit web application
217
+ ├── examples/ # Jupyter notebooks
218
+ └── tests/ # pytest tests
219
+ ```
220
+
221
+ ## Contact
222
+
223
+ - Twitter: [@egr_investor](https://x.com/egr_investor)
224
+ - GitHub: [engineerinvestor](https://github.com/engineerinvestor)
225
+ - Email: egr.investor@gmail.com
226
+
227
+ ## License
228
+
229
+ MIT License
230
+
231
+ ## Disclaimer
232
+
233
+ This tool is for educational purposes only and does not constitute financial advice. Consult a qualified financial advisor for personalized recommendations.
@@ -0,0 +1,38 @@
1
+ fundedness/__init__.py,sha256=P8LNYSUUlk4-8Wa34FlhCyA_echWenZvg1hk7ccdK58,735
2
+ fundedness/cefr.py,sha256=hCImRrjz-DCPJsuF3HShDZK5tw1vslakh-DrNBHsh2k,7728
3
+ fundedness/liabilities.py,sha256=uKvEV6JQnQdrd7b6VsCc2qf9S2DJdK0XhEX9qwca2Ws,6662
4
+ fundedness/liquidity.py,sha256=ncZF70AfgoVoYZnozpA_CHemfaHmI4KKCe3vITp5ljM,1713
5
+ fundedness/policies.py,sha256=pyIYz86Z3sMYAW2rNhPLnXQ3PQCDCa-oxf9NhWyawy8,5854
6
+ fundedness/risk.py,sha256=Lpls6Q2wVq5SX4p9ZQwCtTM73AcqrGCm3hE2QcgKuq0,2750
7
+ fundedness/simulate.py,sha256=JOZeFx6oyOY2nyzejMgwPKnbNvzQZfYdslN-CwnxfwM,13769
8
+ fundedness/allocation/__init__.py,sha256=6cYoAK3xbC6WphbAQ33XtHILCOY59NfXebpDXNeND18,408
9
+ fundedness/allocation/base.py,sha256=kDypu2t86fsn2DyL5an1fPjbPbQWNmqydz3axWUgjLI,763
10
+ fundedness/allocation/constant.py,sha256=6QcUJ0aBQcJcRJtOMhs7uUu13wJtgcPU7ToQzQZhcgw,531
11
+ fundedness/allocation/glidepath.py,sha256=-q6Jb5WpRuZjJ68akT5WgeRIx3QSIDtlNih0_Iz-_cc,3440
12
+ fundedness/models/__init__.py,sha256=EyW6tGUI9T6QMbIfzsG3igqg5--DtZHSTAuMX6foOiA,874
13
+ fundedness/models/assets.py,sha256=ZKgxl4YaqdRVfDErL9weRhh4St_azo8ruN4g15T3IsQ,5013
14
+ fundedness/models/household.py,sha256=t64FjvO0uQJemddLtCPpK3NNYdZCXU6KjA1VZshaqEk,4671
15
+ fundedness/models/liabilities.py,sha256=iqGl5RU841k1clGOwnFParUvxzCJclq2JzNJoNPywuo,3416
16
+ fundedness/models/market.py,sha256=c3jskREsB7m_mjzuBwJ7oo6hlvLmR423t0JT8_a50eg,5551
17
+ fundedness/models/simulation.py,sha256=0jtIJWISDL-TlKVjr6KxcRfAj5Mrbp-VNyA4Bu7IUfU,2224
18
+ fundedness/models/tax.py,sha256=0XhoBNZqfRqF1_acakrkYgMS5L6Pknq-TloQQBrFw1U,3859
19
+ fundedness/models/utility.py,sha256=0AoJdccTb2hKBrTz5LExm8SrSsaWhhBf7y2NNaUzWQ0,4678
20
+ fundedness/viz/__init__.py,sha256=PRP4PtkBr8PlO4Rz3AUPjj6dKL6168_0ogM8qSqoi-Q,687
21
+ fundedness/viz/colors.py,sha256=kbYyCLvLARoIAMztHjnJZDxMGF9-an5z4obORa6JFEU,3204
22
+ fundedness/viz/comparison.py,sha256=DoQW6xr0r2ko55qyli4ue8ndj__opun97brbbHSnKVA,8530
23
+ fundedness/viz/fan_chart.py,sha256=GqdrOKdW12K7Y8sFQJ853QbYG3ASxvEzfY9cC-EKK70,6102
24
+ fundedness/viz/histogram.py,sha256=i56g330-S84WxUU8GGya40yNUQbnAI2D0vzUbFy51DI,6784
25
+ fundedness/viz/survival.py,sha256=xtaAHkJ3jlTUCYnLrTvwWlOlfLao6NC5cRxT5XKWrxM,6515
26
+ fundedness/viz/tornado.py,sha256=1dZzAfKTDCcyyfbDUBE39f44e6k48yviF8kTRee-pwQ,6684
27
+ fundedness/viz/waterfall.py,sha256=6LD0SbZDUzJrA0Zh1Y1qDx0I4BM5ZN_f2eVCXNK5HRc,5586
28
+ fundedness/withdrawals/__init__.py,sha256=v5wKqet3lNMr9UDMZC4d3UPOrRPbzQ6VvdP-ADAiBrg,646
29
+ fundedness/withdrawals/base.py,sha256=WXBuo-tJtp4V2Ottp2PC2YtyK3mGVJ9Cgq21zGUWObg,3472
30
+ fundedness/withdrawals/comparison.py,sha256=eiobGMHmeQpGnr0_HcY_EKrv5_Nu7EaVf5Y9DdDNAyg,7833
31
+ fundedness/withdrawals/fixed_swr.py,sha256=jwbVZHJuVzaSfDCpo-Zp6wdJmI-RzHvg46s1PFxhfA4,3639
32
+ fundedness/withdrawals/guardrails.py,sha256=rZKVojKbl9LLJLi9LlK1I2JXb537MN_AbZfn4hIU8is,5468
33
+ fundedness/withdrawals/rmd_style.py,sha256=E5FfrUYxFJwULKxzxbtZFLoLspLzUUomTQRUWLGLDpg,6373
34
+ fundedness/withdrawals/vpw.py,sha256=aajHLAkHfsxh33uGRgzv0ozVuZREG-EmARJgWOU6mis,4389
35
+ fundedness-0.1.0.dist-info/METADATA,sha256=MLUiTj3UTO7RUaUhgbHk3ObQb173X44gpP6N9iK8tyM,8092
36
+ fundedness-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
37
+ fundedness-0.1.0.dist-info/entry_points.txt,sha256=Oh-Hg08i044YHuSHViCdfoD8CenIGcKrVuUVysvN9sY,51
38
+ fundedness-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ fundedness = fundedness.cli:main