fundedness 0.1.0__py3-none-any.whl → 0.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.

Potentially problematic release.


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

@@ -0,0 +1,286 @@
1
+ """Merton optimal spending policy based on utility maximization."""
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ import numpy as np
6
+
7
+ from fundedness.merton import (
8
+ merton_optimal_spending_rate,
9
+ certainty_equivalent_return,
10
+ )
11
+ from fundedness.models.market import MarketModel
12
+ from fundedness.models.utility import UtilityModel
13
+ from fundedness.withdrawals.base import (
14
+ BaseWithdrawalPolicy,
15
+ WithdrawalContext,
16
+ WithdrawalDecision,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class MertonOptimalSpendingPolicy(BaseWithdrawalPolicy):
22
+ """Spending policy based on Merton optimal consumption theory.
23
+
24
+ This policy determines spending by applying the Merton optimal spending
25
+ rate to current wealth, adjusted for the remaining time horizon.
26
+
27
+ Key characteristics:
28
+ - Spending rate starts low (~2-3%) and rises with age
29
+ - Rate depends on risk aversion, time preference, and market assumptions
30
+ - Adapts to actual wealth (not locked to initial withdrawal amount)
31
+ - Optional smoothing to reduce year-to-year volatility
32
+
33
+ Attributes:
34
+ market_model: Market return and risk assumptions
35
+ utility_model: Utility parameters including risk aversion
36
+ starting_age: Age at retirement/simulation start
37
+ end_age: Assumed maximum age for planning
38
+ smoothing_factor: Blend current with previous spending (0-1, 0=no smoothing)
39
+ min_spending_rate: Minimum spending rate floor
40
+ max_spending_rate: Maximum spending rate ceiling
41
+ """
42
+
43
+ market_model: MarketModel = field(default_factory=MarketModel)
44
+ utility_model: UtilityModel = field(default_factory=UtilityModel)
45
+ starting_age: int = 65
46
+ end_age: int = 100
47
+ smoothing_factor: float = 0.5
48
+ min_spending_rate: float = 0.02
49
+ max_spending_rate: float = 0.15
50
+
51
+ @property
52
+ def name(self) -> str:
53
+ return "Merton Optimal"
54
+
55
+ @property
56
+ def description(self) -> str:
57
+ gamma = self.utility_model.gamma
58
+ return f"Utility-optimal spending (gamma={gamma})"
59
+
60
+ def get_optimal_rate(self, remaining_years: float) -> float:
61
+ """Get the optimal spending rate for given remaining years.
62
+
63
+ Args:
64
+ remaining_years: Years until end of planning horizon
65
+
66
+ Returns:
67
+ Optimal spending rate as decimal
68
+ """
69
+ rate = merton_optimal_spending_rate(
70
+ market_model=self.market_model,
71
+ utility_model=self.utility_model,
72
+ remaining_years=remaining_years,
73
+ )
74
+ return np.clip(rate, self.min_spending_rate, self.max_spending_rate)
75
+
76
+ def calculate_withdrawal(self, context: WithdrawalContext) -> WithdrawalDecision:
77
+ """Calculate withdrawal using Merton optimal spending rate.
78
+
79
+ Args:
80
+ context: Current state information
81
+
82
+ Returns:
83
+ WithdrawalDecision with amount and metadata
84
+ """
85
+ # Determine current age
86
+ if context.age is not None:
87
+ current_age = context.age
88
+ else:
89
+ current_age = self.starting_age + context.year
90
+
91
+ remaining_years = max(1, self.end_age - current_age)
92
+
93
+ # Get optimal spending rate
94
+ rate = self.get_optimal_rate(remaining_years)
95
+
96
+ # Handle vectorized wealth
97
+ if isinstance(context.current_wealth, np.ndarray):
98
+ wealth = context.current_wealth
99
+ else:
100
+ wealth = context.current_wealth
101
+
102
+ # Calculate raw spending
103
+ raw_spending = wealth * rate
104
+
105
+ # Apply smoothing if we have previous spending
106
+ if self.smoothing_factor > 0 and context.previous_spending is not None:
107
+ # Adjust previous spending for inflation
108
+ prev_real = context.previous_spending / context.inflation_cumulative
109
+ smoothed = (
110
+ self.smoothing_factor * prev_real * context.inflation_cumulative
111
+ + (1 - self.smoothing_factor) * raw_spending
112
+ )
113
+ spending = smoothed
114
+ else:
115
+ spending = raw_spending
116
+
117
+ # Apply guardrails
118
+ amount, is_floor_breach, is_ceiling_hit = self.apply_guardrails(
119
+ spending, context.current_wealth
120
+ )
121
+
122
+ return WithdrawalDecision(
123
+ amount=amount,
124
+ is_floor_breach=is_floor_breach,
125
+ is_ceiling_hit=is_ceiling_hit,
126
+ notes=f"Rate: {rate:.1%}, Remaining: {remaining_years}y",
127
+ )
128
+
129
+ def get_initial_withdrawal(self, initial_wealth: float) -> float:
130
+ """Calculate first year withdrawal.
131
+
132
+ Args:
133
+ initial_wealth: Starting portfolio value
134
+
135
+ Returns:
136
+ First year withdrawal amount
137
+ """
138
+ remaining_years = self.end_age - self.starting_age
139
+ rate = self.get_optimal_rate(remaining_years)
140
+ return initial_wealth * rate
141
+
142
+ def get_spending(
143
+ self,
144
+ wealth: np.ndarray,
145
+ year: int,
146
+ initial_wealth: float,
147
+ ) -> np.ndarray:
148
+ """Get spending for simulation (vectorized interface).
149
+
150
+ This method is used by the Monte Carlo simulation engine.
151
+
152
+ Args:
153
+ wealth: Current portfolio values (n_simulations,)
154
+ year: Current simulation year
155
+ initial_wealth: Starting portfolio value
156
+
157
+ Returns:
158
+ Spending amounts for each simulation path
159
+ """
160
+ current_age = self.starting_age + year
161
+ remaining_years = max(1, self.end_age - current_age)
162
+ rate = self.get_optimal_rate(remaining_years)
163
+
164
+ spending = wealth * rate
165
+
166
+ # Ensure non-negative and bounded by wealth
167
+ spending = np.maximum(spending, 0)
168
+ spending = np.minimum(spending, np.maximum(wealth, 0))
169
+
170
+ # Apply floor if set
171
+ if self.floor_spending is not None:
172
+ spending = np.maximum(spending, self.floor_spending)
173
+ # But still can't spend more than we have
174
+ spending = np.minimum(spending, np.maximum(wealth, 0))
175
+
176
+ return spending
177
+
178
+
179
+ @dataclass
180
+ class SmoothedMertonPolicy(MertonOptimalSpendingPolicy):
181
+ """Merton optimal with aggressive smoothing for stable spending.
182
+
183
+ This variant applies stronger smoothing to reduce spending volatility,
184
+ trading off some optimality for a more stable spending experience.
185
+ """
186
+
187
+ smoothing_factor: float = 0.7
188
+ adaptation_rate: float = 0.1
189
+
190
+ @property
191
+ def name(self) -> str:
192
+ return "Smoothed Merton"
193
+
194
+ @property
195
+ def description(self) -> str:
196
+ return "Merton optimal with spending smoothing"
197
+
198
+ def get_spending(
199
+ self,
200
+ wealth: np.ndarray,
201
+ year: int,
202
+ initial_wealth: float,
203
+ ) -> np.ndarray:
204
+ """Get smoothed spending for simulation.
205
+
206
+ Uses exponential smoothing of the optimal spending amount.
207
+
208
+ Args:
209
+ wealth: Current portfolio values
210
+ year: Current simulation year
211
+ initial_wealth: Starting portfolio value
212
+
213
+ Returns:
214
+ Smoothed spending amounts
215
+ """
216
+ # Get raw Merton optimal spending
217
+ current_age = self.starting_age + year
218
+ remaining_years = max(1, self.end_age - current_age)
219
+ rate = self.get_optimal_rate(remaining_years)
220
+
221
+ optimal_spending = wealth * rate
222
+
223
+ # For first year or if tracking isn't set up, use optimal directly
224
+ # In practice, smoothing would be applied via simulation state
225
+ spending = optimal_spending
226
+
227
+ # Apply floor if set
228
+ if self.floor_spending is not None:
229
+ spending = np.maximum(spending, self.floor_spending)
230
+ spending = np.minimum(spending, np.maximum(wealth, 0))
231
+
232
+ return spending
233
+
234
+
235
+ @dataclass
236
+ class FloorAdjustedMertonPolicy(MertonOptimalSpendingPolicy):
237
+ """Merton optimal that accounts for subsistence floor in spending.
238
+
239
+ This variant only applies the optimal rate to wealth above the
240
+ floor-supporting level, ensuring floor spending is always protected.
241
+ """
242
+
243
+ years_of_floor_to_protect: int = 5
244
+
245
+ @property
246
+ def name(self) -> str:
247
+ return "Floor-Protected Merton"
248
+
249
+ @property
250
+ def description(self) -> str:
251
+ return f"Merton optimal protecting {self.years_of_floor_to_protect}y floor"
252
+
253
+ def get_spending(
254
+ self,
255
+ wealth: np.ndarray,
256
+ year: int,
257
+ initial_wealth: float,
258
+ ) -> np.ndarray:
259
+ """Get spending that protects floor for several years.
260
+
261
+ Args:
262
+ wealth: Current portfolio values
263
+ year: Current simulation year
264
+ initial_wealth: Starting portfolio value
265
+
266
+ Returns:
267
+ Floor-protected spending amounts
268
+ """
269
+ current_age = self.starting_age + year
270
+ remaining_years = max(1, self.end_age - current_age)
271
+ rate = self.get_optimal_rate(remaining_years)
272
+
273
+ floor = self.utility_model.subsistence_floor
274
+ protected_wealth = floor * self.years_of_floor_to_protect
275
+
276
+ # Only apply rate to wealth above protected level
277
+ excess_wealth = np.maximum(wealth - protected_wealth, 0)
278
+ flex_spending = excess_wealth * rate
279
+
280
+ # Total spending = floor + flexible portion
281
+ spending = floor + flex_spending
282
+
283
+ # Can't spend more than we have
284
+ spending = np.minimum(spending, np.maximum(wealth, 0))
285
+
286
+ return spending
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fundedness
3
- Version: 0.1.0
3
+ Version: 0.2.1
4
4
  Summary: A Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful visualizations
5
5
  Project-URL: Homepage, https://github.com/engineerinvestor/financial-health-calculator
6
6
  Project-URL: Documentation, https://engineerinvestor.github.io/financial-health-calculator/
@@ -54,15 +54,22 @@ Description-Content-Type: text/markdown
54
54
 
55
55
  # Financial Health Calculator
56
56
 
57
- A comprehensive Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful Plotly visualizations.
58
-
57
+ [![PyPI version](https://img.shields.io/pypi/v/fundedness.svg)](https://pypi.org/project/fundedness/)
58
+ [![Python versions](https://img.shields.io/pypi/pyversions/fundedness.svg)](https://pypi.org/project/fundedness/)
59
+ [![CI](https://github.com/engineerinvestor/financial-health-calculator/actions/workflows/ci.yml/badge.svg)](https://github.com/engineerinvestor/financial-health-calculator/actions/workflows/ci.yml)
60
+ [![codecov](https://codecov.io/gh/engineerinvestor/financial-health-calculator/branch/main/graph/badge.svg)](https://codecov.io/gh/engineerinvestor/financial-health-calculator)
61
+ [![Documentation](https://img.shields.io/badge/docs-mkdocs-blue.svg)](https://engineerinvestor.github.io/financial-health-calculator/)
62
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
59
63
  [![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
64
 
65
+ A comprehensive Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful Plotly visualizations.
66
+
61
67
  ## Features
62
68
 
63
69
  - **CEFR (Certainty-Equivalent Funded Ratio)**: A fundedness metric that accounts for taxes, liquidity, and concentration risk
64
70
  - **Monte Carlo Simulations**: Project retirement outcomes with configurable market assumptions
65
- - **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW, and RMD-style
71
+ - **Withdrawal Strategy Lab**: Compare strategies including fixed SWR, guardrails, VPW, RMD-style, and Merton optimal
72
+ - **Utility Optimization**: Merton optimal spending and allocation based on lifetime utility maximization
66
73
  - **Beautiful Visualizations**: Interactive Plotly charts with fan charts, waterfalls, and survival curves
67
74
  - **REST API**: FastAPI backend for programmatic access
68
75
  - **Streamlit App**: User-friendly web interface
@@ -72,12 +79,12 @@ A comprehensive Python financial planning toolkit with CEFR calculations, Monte
72
79
  ### Installation
73
80
 
74
81
  ```bash
75
- pip install git+https://github.com/engineerinvestor/financial-health-calculator.git
82
+ pip install fundedness
76
83
  ```
77
84
 
78
85
  For development with all extras:
79
86
  ```bash
80
- pip install "git+https://github.com/engineerinvestor/financial-health-calculator.git#egg=fundedness[all]"
87
+ pip install "fundedness[all]"
81
88
  ```
82
89
 
83
90
  ### Basic Usage
@@ -176,6 +183,22 @@ Where τ = tax rate, λ = liquidity factor, ρ = reliability factor
176
183
  | Guardrails | Adjustable with floor/ceiling | Balance |
177
184
  | VPW | Age-based variable percentage | Maximizing spending |
178
185
  | RMD-Style | IRS distribution table based | Tax efficiency |
186
+ | Merton Optimal | Utility-maximizing spending rate | Optimality |
187
+
188
+ ### Utility Optimization
189
+
190
+ The toolkit includes Merton's optimal consumption and portfolio choice framework, as applied in modern retirement planning research<sup>[1]</sup>:
191
+
192
+ - **Optimal Equity Allocation**: `k* = (μ - r) / (γ × σ²)`
193
+ - **Wealth-Adjusted Allocation**: Reduces equity as wealth approaches subsistence floor
194
+ - **Optimal Spending Rate**: Increases with age as horizon shortens
195
+ - **Expected Lifetime Utility**: Track utility across Monte Carlo paths
196
+
197
+ Key insights from this methodology:
198
+ 1. Optimal spending starts low (~2-3%) and rises with age
199
+ 2. Allocation should decrease as wealth approaches the floor
200
+ 3. Risk aversion (gamma) is the critical input parameter
201
+ 4. The 4% rule is suboptimal from a utility perspective
179
202
 
180
203
  ## Development
181
204
 
@@ -207,13 +230,16 @@ financial-health-calculator/
207
230
  ├── fundedness/ # Core Python package
208
231
  │ ├── models/ # Pydantic data models
209
232
  │ ├── viz/ # Plotly visualizations
210
- │ ├── withdrawals/ # Withdrawal strategies
211
- │ ├── allocation/ # Asset allocation strategies
233
+ │ ├── withdrawals/ # Withdrawal strategies (SWR, guardrails, VPW, Merton)
234
+ │ ├── allocation/ # Asset allocation strategies (constant, glidepath, Merton)
212
235
  │ ├── cefr.py # CEFR calculation
213
- │ ├── simulate.py # Monte Carlo engine
236
+ │ ├── simulate.py # Monte Carlo engine with utility tracking
237
+ │ ├── merton.py # Merton optimal formulas
238
+ │ ├── optimize.py # Policy parameter optimization
214
239
  │ └── policies.py # Spending/allocation policies
215
240
  ├── api/ # FastAPI REST API
216
241
  ├── streamlit_app/ # Streamlit web application
242
+ │ └── pages/ # Includes Utility Optimization page
217
243
  ├── examples/ # Jupyter notebooks
218
244
  └── tests/ # pytest tests
219
245
  ```
@@ -228,6 +254,12 @@ financial-health-calculator/
228
254
 
229
255
  MIT License
230
256
 
257
+ ## References
258
+
259
+ 1. Haghani, V., & White, J. (2023). *The Missing Billionaires: A Guide to Better Financial Decisions*. Wiley. See also [Elm Wealth](https://elmwealth.com/) for related research on optimal spending and allocation.
260
+
261
+ 2. Merton, R. C. (1969). Lifetime Portfolio Selection under Uncertainty: The Continuous-Time Case. *The Review of Economics and Statistics*, 51(3), 247-257.
262
+
231
263
  ## Disclaimer
232
264
 
233
265
  This tool is for educational purposes only and does not constitute financial advice. Consult a qualified financial advisor for personalized recommendations.
@@ -1,14 +1,17 @@
1
- fundedness/__init__.py,sha256=P8LNYSUUlk4-8Wa34FlhCyA_echWenZvg1hk7ccdK58,735
1
+ fundedness/__init__.py,sha256=7cGKTGVcoHzzjwZDy5yZ0N1bmDAXCFjWjf133dlVknQ,1678
2
2
  fundedness/cefr.py,sha256=hCImRrjz-DCPJsuF3HShDZK5tw1vslakh-DrNBHsh2k,7728
3
3
  fundedness/liabilities.py,sha256=uKvEV6JQnQdrd7b6VsCc2qf9S2DJdK0XhEX9qwca2Ws,6662
4
4
  fundedness/liquidity.py,sha256=ncZF70AfgoVoYZnozpA_CHemfaHmI4KKCe3vITp5ljM,1713
5
+ fundedness/merton.py,sha256=VUKVxkNimoI0gsQJROb1wrO-SW4ubiNvcYDL2aH69AU,9218
6
+ fundedness/optimize.py,sha256=21ksSkNgtDY_UGTub0KRlIvfaFA363csfSfHnLJrmJs,15567
5
7
  fundedness/policies.py,sha256=pyIYz86Z3sMYAW2rNhPLnXQ3PQCDCa-oxf9NhWyawy8,5854
6
8
  fundedness/risk.py,sha256=Lpls6Q2wVq5SX4p9ZQwCtTM73AcqrGCm3hE2QcgKuq0,2750
7
- fundedness/simulate.py,sha256=JOZeFx6oyOY2nyzejMgwPKnbNvzQZfYdslN-CwnxfwM,13769
8
- fundedness/allocation/__init__.py,sha256=6cYoAK3xbC6WphbAQ33XtHILCOY59NfXebpDXNeND18,408
9
+ fundedness/simulate.py,sha256=A0_ZScMTF8YJcjVHys_nmxMG4x-c81sU_W8iMApscNE,19678
10
+ fundedness/allocation/__init__.py,sha256=5p85uRFltSfEwXjengr56FNY1s4KvcMMxuH05I_Omfs,677
9
11
  fundedness/allocation/base.py,sha256=kDypu2t86fsn2DyL5an1fPjbPbQWNmqydz3axWUgjLI,763
10
12
  fundedness/allocation/constant.py,sha256=6QcUJ0aBQcJcRJtOMhs7uUu13wJtgcPU7ToQzQZhcgw,531
11
13
  fundedness/allocation/glidepath.py,sha256=-q6Jb5WpRuZjJ68akT5WgeRIx3QSIDtlNih0_Iz-_cc,3440
14
+ fundedness/allocation/merton_optimal.py,sha256=krRd7sKbvXXxfewLJuF9RWbMLYUHbehvdbdYSjwnczk,7702
12
15
  fundedness/models/__init__.py,sha256=EyW6tGUI9T6QMbIfzsG3igqg5--DtZHSTAuMX6foOiA,874
13
16
  fundedness/models/assets.py,sha256=ZKgxl4YaqdRVfDErL9weRhh4St_azo8ruN4g15T3IsQ,5013
14
17
  fundedness/models/household.py,sha256=t64FjvO0uQJemddLtCPpK3NNYdZCXU6KjA1VZshaqEk,4671
@@ -17,22 +20,24 @@ fundedness/models/market.py,sha256=c3jskREsB7m_mjzuBwJ7oo6hlvLmR423t0JT8_a50eg,5
17
20
  fundedness/models/simulation.py,sha256=0jtIJWISDL-TlKVjr6KxcRfAj5Mrbp-VNyA4Bu7IUfU,2224
18
21
  fundedness/models/tax.py,sha256=0XhoBNZqfRqF1_acakrkYgMS5L6Pknq-TloQQBrFw1U,3859
19
22
  fundedness/models/utility.py,sha256=0AoJdccTb2hKBrTz5LExm8SrSsaWhhBf7y2NNaUzWQ0,4678
20
- fundedness/viz/__init__.py,sha256=PRP4PtkBr8PlO4Rz3AUPjj6dKL6168_0ogM8qSqoi-Q,687
23
+ fundedness/viz/__init__.py,sha256=Om8qlI13w13MfDmyB_xeR6hgr7LpWX1tjAf_x3eDbEQ,1168
21
24
  fundedness/viz/colors.py,sha256=kbYyCLvLARoIAMztHjnJZDxMGF9-an5z4obORa6JFEU,3204
22
25
  fundedness/viz/comparison.py,sha256=DoQW6xr0r2ko55qyli4ue8ndj__opun97brbbHSnKVA,8530
23
26
  fundedness/viz/fan_chart.py,sha256=GqdrOKdW12K7Y8sFQJ853QbYG3ASxvEzfY9cC-EKK70,6102
24
27
  fundedness/viz/histogram.py,sha256=i56g330-S84WxUU8GGya40yNUQbnAI2D0vzUbFy51DI,6784
28
+ fundedness/viz/optimal.py,sha256=w5vI2O-4O6F1Dobsfg55pznlfD100SRnSgZqLIpcT9c,16056
25
29
  fundedness/viz/survival.py,sha256=xtaAHkJ3jlTUCYnLrTvwWlOlfLao6NC5cRxT5XKWrxM,6515
26
30
  fundedness/viz/tornado.py,sha256=1dZzAfKTDCcyyfbDUBE39f44e6k48yviF8kTRee-pwQ,6684
27
31
  fundedness/viz/waterfall.py,sha256=6LD0SbZDUzJrA0Zh1Y1qDx0I4BM5ZN_f2eVCXNK5HRc,5586
28
- fundedness/withdrawals/__init__.py,sha256=v5wKqet3lNMr9UDMZC4d3UPOrRPbzQ6VvdP-ADAiBrg,646
32
+ fundedness/withdrawals/__init__.py,sha256=FiXfK7QBzUp4Iwf25VK0jbZyguF8bLlZAWWMjg8tFPo,886
29
33
  fundedness/withdrawals/base.py,sha256=WXBuo-tJtp4V2Ottp2PC2YtyK3mGVJ9Cgq21zGUWObg,3472
30
34
  fundedness/withdrawals/comparison.py,sha256=eiobGMHmeQpGnr0_HcY_EKrv5_Nu7EaVf5Y9DdDNAyg,7833
31
- fundedness/withdrawals/fixed_swr.py,sha256=jwbVZHJuVzaSfDCpo-Zp6wdJmI-RzHvg46s1PFxhfA4,3639
35
+ fundedness/withdrawals/fixed_swr.py,sha256=2ZgkQ_xZakpcdZQIop46TAFML7ESz7nyTVdVfI-1Z18,5469
32
36
  fundedness/withdrawals/guardrails.py,sha256=rZKVojKbl9LLJLi9LlK1I2JXb537MN_AbZfn4hIU8is,5468
37
+ fundedness/withdrawals/merton_optimal.py,sha256=mSxRAKGx8sr_YdlhQMtIvnojqfuG0MiYtBp18xboLHs,9269
33
38
  fundedness/withdrawals/rmd_style.py,sha256=E5FfrUYxFJwULKxzxbtZFLoLspLzUUomTQRUWLGLDpg,6373
34
39
  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,,
40
+ fundedness-0.2.1.dist-info/METADATA,sha256=Lg-LFdrLs2mcL9oyBjk3-X1qt9bB7U-K01zFHJXY6eI,10381
41
+ fundedness-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
42
+ fundedness-0.2.1.dist-info/entry_points.txt,sha256=Oh-Hg08i044YHuSHViCdfoD8CenIGcKrVuUVysvN9sY,51
43
+ fundedness-0.2.1.dist-info/RECORD,,