fundedness 0.2.2__tar.gz → 0.2.3__tar.gz

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.

Files changed (97) hide show
  1. fundedness-0.2.3/.streamlit/config.toml +11 -0
  2. {fundedness-0.2.2 → fundedness-0.2.3}/PKG-INFO +2 -1
  3. {fundedness-0.2.2 → fundedness-0.2.3}/README.md +1 -0
  4. {fundedness-0.2.2 → fundedness-0.2.3}/docs/guide/utility-optimization.md +14 -14
  5. {fundedness-0.2.2 → fundedness-0.2.3}/docs/javascripts/mathjax.js +0 -7
  6. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/market.py +26 -15
  7. {fundedness-0.2.2 → fundedness-0.2.3}/pyproject.toml +1 -1
  8. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/app.py +6 -0
  9. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/pages/0_Inputs.py +4 -0
  10. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/pages/1_CEFR_Dashboard.py +4 -0
  11. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/pages/2_Time_Runway.py +4 -0
  12. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/pages/3_Withdrawal_Lab.py +4 -0
  13. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/pages/4_Sensitivity.py +4 -0
  14. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/pages/5_Utility_Optimization.py +4 -0
  15. {fundedness-0.2.2 → fundedness-0.2.3}/.github/workflows/docs.yml +0 -0
  16. {fundedness-0.2.2 → fundedness-0.2.3}/.github/workflows/publish.yml +0 -0
  17. {fundedness-0.2.2 → fundedness-0.2.3}/.gitignore +0 -0
  18. {fundedness-0.2.2 → fundedness-0.2.3}/CLAUDE.md +0 -0
  19. {fundedness-0.2.2 → fundedness-0.2.3}/api/__init__.py +0 -0
  20. {fundedness-0.2.2 → fundedness-0.2.3}/api/main.py +0 -0
  21. {fundedness-0.2.2 → fundedness-0.2.3}/api/routes/__init__.py +0 -0
  22. {fundedness-0.2.2 → fundedness-0.2.3}/api/routes/cefr.py +0 -0
  23. {fundedness-0.2.2 → fundedness-0.2.3}/api/routes/compare.py +0 -0
  24. {fundedness-0.2.2 → fundedness-0.2.3}/api/routes/simulate.py +0 -0
  25. {fundedness-0.2.2 → fundedness-0.2.3}/background_information.md +0 -0
  26. {fundedness-0.2.2 → fundedness-0.2.3}/docs/api/core.md +0 -0
  27. {fundedness-0.2.2 → fundedness-0.2.3}/docs/api/merton.md +0 -0
  28. {fundedness-0.2.2 → fundedness-0.2.3}/docs/api/models.md +0 -0
  29. {fundedness-0.2.2 → fundedness-0.2.3}/docs/api/viz.md +0 -0
  30. {fundedness-0.2.2 → fundedness-0.2.3}/docs/api/withdrawals.md +0 -0
  31. {fundedness-0.2.2 → fundedness-0.2.3}/docs/examples/tutorials.md +0 -0
  32. {fundedness-0.2.2 → fundedness-0.2.3}/docs/getting-started/installation.md +0 -0
  33. {fundedness-0.2.2 → fundedness-0.2.3}/docs/getting-started/quickstart.md +0 -0
  34. {fundedness-0.2.2 → fundedness-0.2.3}/docs/guide/cefr.md +0 -0
  35. {fundedness-0.2.2 → fundedness-0.2.3}/docs/guide/simulations.md +0 -0
  36. {fundedness-0.2.2 → fundedness-0.2.3}/docs/guide/visualizations.md +0 -0
  37. {fundedness-0.2.2 → fundedness-0.2.3}/docs/guide/withdrawals.md +0 -0
  38. {fundedness-0.2.2 → fundedness-0.2.3}/docs/index.md +0 -0
  39. {fundedness-0.2.2 → fundedness-0.2.3}/examples/01_cefr_basics.ipynb +0 -0
  40. {fundedness-0.2.2 → fundedness-0.2.3}/examples/02_time_distribution.ipynb +0 -0
  41. {fundedness-0.2.2 → fundedness-0.2.3}/examples/03_withdrawal_comparison.ipynb +0 -0
  42. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/__init__.py +0 -0
  43. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/allocation/__init__.py +0 -0
  44. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/allocation/base.py +0 -0
  45. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/allocation/constant.py +0 -0
  46. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/allocation/glidepath.py +0 -0
  47. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/allocation/merton_optimal.py +0 -0
  48. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/cefr.py +0 -0
  49. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/liabilities.py +0 -0
  50. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/liquidity.py +0 -0
  51. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/merton.py +0 -0
  52. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/__init__.py +0 -0
  53. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/assets.py +0 -0
  54. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/household.py +0 -0
  55. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/liabilities.py +0 -0
  56. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/simulation.py +0 -0
  57. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/tax.py +0 -0
  58. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/models/utility.py +0 -0
  59. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/optimize.py +0 -0
  60. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/policies.py +0 -0
  61. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/risk.py +0 -0
  62. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/simulate.py +0 -0
  63. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/__init__.py +0 -0
  64. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/colors.py +0 -0
  65. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/comparison.py +0 -0
  66. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/fan_chart.py +0 -0
  67. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/histogram.py +0 -0
  68. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/optimal.py +0 -0
  69. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/survival.py +0 -0
  70. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/tornado.py +0 -0
  71. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/viz/waterfall.py +0 -0
  72. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/__init__.py +0 -0
  73. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/base.py +0 -0
  74. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/comparison.py +0 -0
  75. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/fixed_swr.py +0 -0
  76. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/guardrails.py +0 -0
  77. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/merton_optimal.py +0 -0
  78. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/rmd_style.py +0 -0
  79. {fundedness-0.2.2 → fundedness-0.2.3}/fundedness/withdrawals/vpw.py +0 -0
  80. {fundedness-0.2.2 → fundedness-0.2.3}/mkdocs.yml +0 -0
  81. {fundedness-0.2.2 → fundedness-0.2.3}/requirements.txt +0 -0
  82. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/__init__.py +0 -0
  83. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/components/__init__.py +0 -0
  84. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/components/asset_editor.py +0 -0
  85. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/components/liability_editor.py +0 -0
  86. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/components/metrics_display.py +0 -0
  87. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/utils/__init__.py +0 -0
  88. {fundedness-0.2.2 → fundedness-0.2.3}/streamlit_app/utils/session_state.py +0 -0
  89. {fundedness-0.2.2 → fundedness-0.2.3}/tests/__init__.py +0 -0
  90. {fundedness-0.2.2 → fundedness-0.2.3}/tests/conftest.py +0 -0
  91. {fundedness-0.2.2 → fundedness-0.2.3}/tests/test_api.py +0 -0
  92. {fundedness-0.2.2 → fundedness-0.2.3}/tests/test_cefr.py +0 -0
  93. {fundedness-0.2.2 → fundedness-0.2.3}/tests/test_liabilities.py +0 -0
  94. {fundedness-0.2.2 → fundedness-0.2.3}/tests/test_merton.py +0 -0
  95. {fundedness-0.2.2 → fundedness-0.2.3}/tests/test_optimize.py +0 -0
  96. {fundedness-0.2.2 → fundedness-0.2.3}/tests/test_simulate.py +0 -0
  97. {fundedness-0.2.2 → fundedness-0.2.3}/tests/test_withdrawals.py +0 -0
@@ -0,0 +1,11 @@
1
+ [theme]
2
+ primaryColor = "#3498db"
3
+ backgroundColor = "#ffffff"
4
+ secondaryBackgroundColor = "#f8f9fa"
5
+ textColor = "#2c3e50"
6
+ font = "sans serif"
7
+
8
+ [server]
9
+ headless = true
10
+ enableCORS = false
11
+ enableXsrfProtection = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fundedness
3
- Version: 0.2.2
3
+ Version: 0.2.3
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/
@@ -57,6 +57,7 @@ Description-Content-Type: text/markdown
57
57
  [![PyPI version](https://img.shields.io/pypi/v/fundedness.svg)](https://pypi.org/project/fundedness/)
58
58
  [![Python versions](https://img.shields.io/pypi/pyversions/fundedness.svg)](https://pypi.org/project/fundedness/)
59
59
  [![Documentation](https://img.shields.io/badge/docs-mkdocs-blue.svg)](https://engineerinvestor.github.io/financial-health-calculator/)
60
+ [![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://financial-health-calculator.streamlit.app/)
60
61
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
61
62
  [![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)
62
63
 
@@ -3,6 +3,7 @@
3
3
  [![PyPI version](https://img.shields.io/pypi/v/fundedness.svg)](https://pypi.org/project/fundedness/)
4
4
  [![Python versions](https://img.shields.io/pypi/pyversions/fundedness.svg)](https://pypi.org/project/fundedness/)
5
5
  [![Documentation](https://img.shields.io/badge/docs-mkdocs-blue.svg)](https://engineerinvestor.github.io/financial-health-calculator/)
6
+ [![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://financial-health-calculator.streamlit.app/)
6
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
8
  [![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)
8
9
 
@@ -18,34 +18,34 @@ Utility optimization provides a rigorous framework for finding the **optimal** s
18
18
 
19
19
  The Merton formula gives the optimal fraction to invest in risky assets:
20
20
 
21
- \[
21
+ $$
22
22
  k^* = \frac{\mu - r}{\gamma \cdot \sigma^2}
23
- \]
23
+ $$
24
24
 
25
25
  Where:
26
26
 
27
- - \(\mu\) = expected stock return
28
- - \(r\) = bond/risk-free return
29
- - \(\gamma\) = risk aversion coefficient
30
- - \(\sigma\) = stock volatility
27
+ - $\mu$ = expected stock return
28
+ - $r$ = bond/risk-free return
29
+ - $\gamma$ = risk aversion coefficient
30
+ - $\sigma$ = stock volatility
31
31
 
32
32
  ### Certainty Equivalent Return
33
33
 
34
34
  The guaranteed return that provides the same utility as the risky portfolio:
35
35
 
36
- \[
36
+ $$
37
37
  r_{CE} = r + k^*(\mu - r) - \frac{\gamma \cdot (k^*)^2 \cdot \sigma^2}{2}
38
- \]
38
+ $$
39
39
 
40
40
  ### Optimal Spending Rate
41
41
 
42
42
  For an infinite horizon:
43
43
 
44
- \[
44
+ $$
45
45
  c^* = r_{CE} - \frac{r_{CE} - \rho}{\gamma}
46
- \]
46
+ $$
47
47
 
48
- Where \(\rho\) is your time preference (discount rate).
48
+ Where $\rho$ is your time preference (discount rate).
49
49
 
50
50
  ## Quick Start
51
51
 
@@ -88,11 +88,11 @@ print(f"Year 1 spending: ${1_000_000 * result.optimal_spending_rate:,.0f}")
88
88
 
89
89
  Near the subsistence floor, you can't afford to take risk. The wealth-adjusted allocation accounts for this:
90
90
 
91
- \[
91
+ $$
92
92
  k_{adj} = k^* \cdot \frac{W - F}{W}
93
- \]
93
+ $$
94
94
 
95
- Where \(W\) is wealth and \(F\) is the subsistence floor.
95
+ Where $W$ is wealth and $F$ is the subsistence floor.
96
96
 
97
97
  As wealth approaches the floor, allocation approaches zero. As wealth rises far above the floor, allocation approaches the unconstrained optimal.
98
98
 
@@ -10,10 +10,3 @@ window.MathJax = {
10
10
  processHtmlClass: "arithmatex"
11
11
  }
12
12
  };
13
-
14
- document$.subscribe(() => {
15
- MathJax.startup.output.clearCache()
16
- MathJax.typesetClear()
17
- MathJax.texReset()
18
- MathJax.typesetPromise()
19
- })
@@ -139,22 +139,22 @@ class MarketModel(BaseModel):
139
139
 
140
140
  def expected_portfolio_return(
141
141
  self,
142
- stock_weight: float,
143
- bond_weight: Optional[float] = None,
144
- ) -> float:
142
+ stock_weight: float | np.ndarray,
143
+ bond_weight: float | np.ndarray | None = None,
144
+ ) -> float | np.ndarray:
145
145
  """Calculate expected return for a portfolio.
146
146
 
147
147
  Args:
148
- stock_weight: Weight in stocks (0-1)
148
+ stock_weight: Weight in stocks (0-1), scalar or array
149
149
  bond_weight: Weight in bonds (remainder is cash if not specified)
150
150
 
151
151
  Returns:
152
- Expected annual real return
152
+ Expected annual real return (scalar or array matching input)
153
153
  """
154
154
  if bond_weight is None:
155
155
  bond_weight = 1 - stock_weight
156
156
 
157
- cash_weight = max(0, 1 - stock_weight - bond_weight)
157
+ cash_weight = np.maximum(0, 1 - stock_weight - bond_weight)
158
158
 
159
159
  return (
160
160
  stock_weight * self.stock_return
@@ -164,25 +164,36 @@ class MarketModel(BaseModel):
164
164
 
165
165
  def portfolio_volatility(
166
166
  self,
167
- stock_weight: float,
168
- bond_weight: Optional[float] = None,
169
- ) -> float:
167
+ stock_weight: float | np.ndarray,
168
+ bond_weight: float | np.ndarray | None = None,
169
+ ) -> float | np.ndarray:
170
170
  """Calculate portfolio volatility.
171
171
 
172
172
  Args:
173
- stock_weight: Weight in stocks (0-1)
173
+ stock_weight: Weight in stocks (0-1), scalar or array
174
174
  bond_weight: Weight in bonds (remainder is cash if not specified)
175
175
 
176
176
  Returns:
177
- Annual portfolio volatility
177
+ Annual portfolio volatility (scalar or array matching input)
178
178
  """
179
179
  if bond_weight is None:
180
180
  bond_weight = 1 - stock_weight
181
181
 
182
- cash_weight = max(0, 1 - stock_weight - bond_weight)
183
- weights = np.array([stock_weight, bond_weight, cash_weight, 0])
182
+ cash_weight = np.maximum(0, 1 - stock_weight - bond_weight)
184
183
 
185
184
  cov = self.get_covariance_matrix()
186
- portfolio_variance = weights @ cov @ weights
187
185
 
188
- return np.sqrt(portfolio_variance)
186
+ # Handle both scalar and array inputs
187
+ if isinstance(stock_weight, np.ndarray):
188
+ # Vectorized computation for array inputs
189
+ # weights shape: (n_samples, 4)
190
+ weights = np.column_stack([stock_weight, bond_weight, cash_weight, np.zeros_like(stock_weight)])
191
+ # cov @ weights.T has shape (4, n_samples)
192
+ # We want sum of weights[i] * (cov @ weights[i]) for each i
193
+ # This is equivalent to diag(weights @ cov @ weights.T)
194
+ portfolio_variance = np.einsum('ij,jk,ik->i', weights, cov, weights)
195
+ return np.sqrt(portfolio_variance)
196
+ else:
197
+ weights = np.array([stock_weight, bond_weight, cash_weight, 0])
198
+ portfolio_variance = weights @ cov @ weights
199
+ return np.sqrt(portfolio_variance)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fundedness"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "A Python financial planning toolkit with CEFR calculations, Monte Carlo simulations, and beautiful visualizations"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -10,6 +10,12 @@ st.set_page_config(
10
10
  initial_sidebar_state="expanded",
11
11
  )
12
12
 
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ # Add parent directory to path for imports when running on Streamlit Cloud
17
+ sys.path.insert(0, str(Path(__file__).parent.parent))
18
+
13
19
  from streamlit_app.utils.session_state import initialize_session_state
14
20
 
15
21
  # Initialize session state
@@ -1,5 +1,9 @@
1
1
  """Inputs page for asset, liability, and assumption entry."""
2
2
 
3
+ import sys
4
+ from pathlib import Path
5
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
6
+
3
7
  import streamlit as st
4
8
 
5
9
  from fundedness.models.household import Household, Person
@@ -1,5 +1,9 @@
1
1
  """CEFR Dashboard page."""
2
2
 
3
+ import sys
4
+ from pathlib import Path
5
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
6
+
3
7
  import streamlit as st
4
8
 
5
9
  from fundedness.cefr import compute_cefr
@@ -1,5 +1,9 @@
1
1
  """Time Runway page with Monte Carlo projections."""
2
2
 
3
+ import sys
4
+ from pathlib import Path
5
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
6
+
3
7
  import numpy as np
4
8
  import streamlit as st
5
9
 
@@ -1,5 +1,9 @@
1
1
  """Withdrawal Strategy Lab page."""
2
2
 
3
+ import sys
4
+ from pathlib import Path
5
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
6
+
3
7
  import numpy as np
4
8
  import streamlit as st
5
9
 
@@ -1,5 +1,9 @@
1
1
  """Sensitivity Analysis page."""
2
2
 
3
+ import sys
4
+ from pathlib import Path
5
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
6
+
3
7
  import streamlit as st
4
8
 
5
9
  from fundedness.cefr import compute_cefr
@@ -1,5 +1,9 @@
1
1
  """Utility Optimization page - Merton optimal spending and allocation."""
2
2
 
3
+ import sys
4
+ from pathlib import Path
5
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
6
+
3
7
  import numpy as np
4
8
  import streamlit as st
5
9
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes