goal-based-allocation 0.2.0__tar.gz → 0.3.1__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.
Files changed (31) hide show
  1. goal_based_allocation-0.3.1/MANIFEST.in +1 -0
  2. {goal_based_allocation-0.2.0/goal_based_allocation.egg-info → goal_based_allocation-0.3.1}/PKG-INFO +87 -64
  3. goal_based_allocation-0.2.0/PKG-INFO → goal_based_allocation-0.3.1/README.md +76 -91
  4. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/pyproject.toml +69 -58
  5. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/__init__.py +56 -42
  6. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/client_solver.py +0 -65
  7. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/laplace_inversion.py +0 -102
  8. goal_based_allocation-0.3.1/src/goal_based_allocation/mandate_utils.py +101 -0
  9. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/opportunity_set.py +0 -77
  10. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/regime_switch_paper.py +0 -75
  11. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/riccati_solver.py +0 -46
  12. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/vanilla_option_pricer.py +10 -3
  13. goal_based_allocation-0.3.1/src/goal_based_allocation/variance_swap.py +449 -0
  14. goal_based_allocation-0.2.0/README.md → goal_based_allocation-0.3.1/src/goal_based_allocation.egg-info/PKG-INFO +512 -457
  15. goal_based_allocation-0.3.1/src/goal_based_allocation.egg-info/SOURCES.txt +27 -0
  16. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation.egg-info/requires.txt +5 -0
  17. goal_based_allocation-0.3.1/tests/test_docs.py +37 -0
  18. goal_based_allocation-0.3.1/tests/test_metadata.py +32 -0
  19. goal_based_allocation-0.3.1/tests/test_module_layout.py +191 -0
  20. goal_based_allocation-0.3.1/tests/test_quickstart.py +20 -0
  21. goal_based_allocation-0.3.1/tests/test_repository_layout.py +54 -0
  22. goal_based_allocation-0.3.1/tests/test_variance_swap.py +229 -0
  23. goal_based_allocation-0.2.0/goal_based_allocation/mandate_utils.py +0 -314
  24. goal_based_allocation-0.2.0/goal_based_allocation.egg-info/SOURCES.txt +0 -19
  25. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/LICENSE +0 -0
  26. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/setup.cfg +0 -0
  27. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation.egg-info/dependency_links.txt +0 -0
  28. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation.egg-info/top_level.txt +0 -0
  29. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/tests/test_framework.py +0 -0
  30. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/tests/test_reference_crosschecks.py +0 -0
  31. {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/tests/test_vanilla_option_pricer.py +0 -0
@@ -0,0 +1 @@
1
+ prune src/goal_based_allocation/run
@@ -1,17 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: goal-based-allocation
3
- Version: 0.2.0
4
- Summary: Dynamic Mean-Variance Portfolio Allocation under Regime-Switching Jump-Diffusions with Absorbing Barriers and Distribution Matching
3
+ Version: 0.3.1
4
+ Summary: Analytical dynamic mean-variance allocation and terminal-wealth risk under regime-switching jump-diffusions
5
5
  Author: Artur Sepp
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/ArturSepp/GoalBasedAllocation
8
8
  Project-URL: Repository, https://github.com/ArturSepp/GoalBasedAllocation
9
9
  Project-URL: Issues, https://github.com/ArturSepp/GoalBasedAllocation/issues
10
- Keywords: portfolio-optimization,mean-variance,regime-switching,jump-diffusion,goal-based-investing,laplace-transform,option-pricing,quantitative-finance
10
+ Project-URL: Documentation, https://goalbasedallocation.readthedocs.io
11
+ Project-URL: Changelog, https://github.com/ArturSepp/GoalBasedAllocation/blob/main/CHANGELOG.md
12
+ Project-URL: Paper, https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6534579
13
+ Keywords: portfolio-optimization,mean-variance,regime-switching,jump-diffusion,goal-based-investing,laplace-transform,option-pricing,quantitative-finance,asset-allocation,wealth-management,absorbing-barrier
11
14
  Classifier: Development Status :: 4 - Beta
12
15
  Classifier: Intended Audience :: Financial and Insurance Industry
13
16
  Classifier: Intended Audience :: Science/Research
14
- Classifier: License :: OSI Approved :: MIT License
15
17
  Classifier: Programming Language :: Python :: 3
16
18
  Classifier: Programming Language :: Python :: 3.10
17
19
  Classifier: Programming Language :: Python :: 3.11
@@ -28,37 +30,30 @@ Provides-Extra: dev
28
30
  Requires-Dist: pytest; extra == "dev"
29
31
  Requires-Dist: black; extra == "dev"
30
32
  Requires-Dist: ruff; extra == "dev"
33
+ Provides-Extra: docs
34
+ Requires-Dist: sphinx>=7; extra == "docs"
35
+ Requires-Dist: myst-parser>=2; extra == "docs"
36
+ Requires-Dist: sphinx-sitemap>=2; extra == "docs"
31
37
  Dynamic: license-file
32
38
 
33
- # GoalBasedAllocation
39
+ # GoalBasedAllocation (`goal-based-allocation`)
34
40
 
35
- <p align="center">
36
- <em>Dynamic Mean-Variance Portfolio Allocation under Regime-Switching Jump-Diffusions<br>with Absorbing Barriers and Distribution Matching</em>
37
- </p>
41
+ Analytical dynamic mean-variance allocation and terminal-wealth risk under
42
+ regime-switching jump-diffusions in Python for quantitative researchers and
43
+ wealth-management model developers.
38
44
 
39
- <p align="center">
40
- <a href="https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml">
41
- <img src="https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml/badge.svg" alt="Tests">
42
- </a>
43
- <a href="https://github.com/ArturSepp/GoalBasedAllocation/blob/main/LICENSE">
44
- <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT">
45
- </a>
46
- <a href="https://pypi.org/project/goal-based-allocation/">
47
- <img src="https://img.shields.io/pypi/v/goal-based-allocation.svg" alt="PyPI">
48
- </a>
49
- <a href="https://pypi.org/project/goal-based-allocation/">
50
- <img src="https://img.shields.io/pypi/pyversions/goal-based-allocation.svg" alt="Python">
51
- </a>
52
- <a href="https://pepy.tech/project/goal-based-allocation">
53
- <img src="https://pepy.tech/badge/goal-based-allocation" alt="Downloads">
54
- </a>
55
- </p>
45
+ [![PyPI](https://img.shields.io/pypi/v/goal-based-allocation?style=flat-square)](https://pypi.org/project/goal-based-allocation/)
46
+ [![Python](https://img.shields.io/pypi/pyversions/goal-based-allocation?style=flat-square)](https://pypi.org/project/goal-based-allocation/)
47
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
48
+ [![Tests](https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml/badge.svg)](https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml)
49
+ [![Downloads](https://static.pepy.tech/badge/goal-based-allocation)](https://pepy.tech/project/goal-based-allocation)
50
+ [![Monthly](https://static.pepy.tech/badge/goal-based-allocation/month)](https://pepy.tech/project/goal-based-allocation)
51
+
52
+ **Documentation:** [artursepp.github.io/GoalBasedAllocation](https://artursepp.github.io/GoalBasedAllocation/)
56
53
 
57
- Companion code to:
54
+ **Paper:** companion code to Sepp, A. (2026), *Dynamic Mean-Variance Portfolio Allocation under Regime-Switching Jump-Diffusions with Absorbing Barriers and Distribution Matching* — [SSRN 6534579](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6534579). See [Citation](#citation) for BibTeX.
58
55
 
59
- > **Sepp, A. (2026). Dynamic Mean-Variance Portfolio Allocation under Regime-Switching
60
- > Jump-Diffusions with Absorbing Barriers and Distribution Matching.**
61
- > [SSRN: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6534579](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6534579)
56
+ ---
62
57
 
63
58
  ## Overview
64
59
 
@@ -66,12 +61,18 @@ This package provides a **fully analytical Laplace-transform framework** for dyn
66
61
  mean-variance (MV) portfolio allocation under a two-state regime-switching model
67
62
  with exponential jumps at regime transitions and an absorbing wealth floor.
68
63
 
64
+ It models two regimes and multi-asset mandates aggregated to one effective risky
65
+ asset. It is not a discrete constrained multi-asset optimiser, trading engine, or
66
+ production portfolio-management system; use
67
+ [`optimalportfolios`](https://github.com/ArturSepp/OptimalPortfolios) for rolling
68
+ multi-asset construction, constraints, transaction costs, and backtesting.
69
+
69
70
  The MV-optimal strategy takes the form
70
71
 
71
- $$\omega^*(t) = |\omega^*_a| \cdot \left(\frac{\Pi^*(t)}{\Pi_t} - 1\right)$$
72
+ $$\omega^{\ast}(t) = |\omega^{\ast}_a| \cdot \left(\frac{\Pi^{\ast}(t)}{\Pi_t} - 1\right)$$
72
73
 
73
- where $\Pi^*(t)$ is the target wealth trajectory derived from the Riccati ODE system,
74
- and $|\omega^*_a|$ is the regime-dependent allocation intensity. This produces an
74
+ where $\Pi^{\ast}(t)$ is the target wealth trajectory derived from the Riccati ODE system,
75
+ and $|\omega^{\ast}_a|$ is the regime-dependent allocation intensity. This produces an
75
76
  **endogenous de-risking glide path**: early in the horizon the funding gap is large
76
77
  and allocation is aggressive; as the portfolio approaches the target, allocation
77
78
  moderates automatically.
@@ -101,6 +102,12 @@ simulation is needed for pricing; MC is used only for validation.
101
102
  - Monte Carlo simulator for validation of all analytical results
102
103
  - Integration tests and all paper figures reproducible from a single command
103
104
 
105
+ ## When to use it — and when not
106
+
107
+ Use `goal-based-allocation` to design goal-based mandates under regime switching: survival and shortfall probabilities against a wealth floor, MV-optimal allocation intensities and glide paths, terminal wealth distributions, investment opportunity sets for client advice, and regime-switching vanilla option pricing on the same Laplace engine — all analytically, with Monte Carlo reserved for validation.
108
+
109
+ The model is deliberately parsimonious: two regimes, exponential jumps at transitions, and multi-asset mandates reduced to a single effective asset via portfolio aggregation. For discrete rolling multi-asset optimisation with weight constraints and transaction costs, use [`optimalportfolios`](https://github.com/ArturSepp/OptimalPortfolios).
110
+
104
111
  ## Model
105
112
 
106
113
  The portfolio wealth $\Pi_t$ follows a regime-switching jump-diffusion
@@ -114,7 +121,7 @@ with two states: **growth** ($i=1$) and **stress** ($i=2$).
114
121
  | $\mu^{[i]} = \bar{\mu}^{[i]} - \lambda \alpha^{[i]}$ | Diffusion drift | `mu_bar` |
115
122
  | $r_h = \max(r, c)$ | Hurdle rate | `r_h` |
116
123
  | $r_c = r_h - c = \max(0, r-c)$ | Net floor growth rate | `r_c` |
117
- | $\omega^*_a = -\tilde{a}^{[i]}/\Sigma^{[i]}$ | Risky allocation coefficient | `w_a` |
124
+ | $\omega^{\ast}_a = -\tilde{a}^{[i]}/\Sigma^{[i]}$ | Risky allocation coefficient | `w_a` |
118
125
 
119
126
  ### Process specification
120
127
 
@@ -123,7 +130,7 @@ with two states: **growth** ($i=1$) and **stress** ($i=2$).
123
130
  | Diffusion | Regime-dependent drift $\mu^{[i]}$ and volatility $\sigma^{[i]}$ |
124
131
  | Jumps | Exponential crash at growth→stress ($\eta^{[1]}$), exponential recovery at stress→growth ($\eta^{[2]}$) |
125
132
  | Regime switching | Poisson rates $\lambda^{[12]}$ (crash) and $\lambda^{[21]}$ (recovery) |
126
- | MV-optimal control | $\omega^*(t) = \|\omega_a^*\| \cdot (\Pi^*(t)/\Pi_t - 1)$, from Riccati system |
133
+ | MV-optimal control | $\omega^{\ast}(t) = \|\omega_a^{\ast}\| \cdot (\Pi^{\ast}(t)/\Pi_t - 1)$, from Riccati system |
127
134
  | Absorbing floor | Wealth stopped at $L_t = L_0 \cdot e^{r_c t}$, converting to cash |
128
135
 
129
136
  ### Asset class parameters
@@ -148,9 +155,9 @@ $w_{\text{eq}} = g(1-w)$, $w_{\text{pe}} = (1-g)(1-w)$.
148
155
  | Balanced | 35/43/22 | 11.1 / 16.7% | 4.34 / 0.70% | 0.233 |
149
156
  | Growth | 0/67/33 | 15.8 / 23.8% | 5.33 / 0.00% | 0.334 |
150
157
 
151
- ### MV-optimal results ($c=0\%$, $\omega^*(0)=1$, $q_{dd}=2$)
158
+ ### MV-optimal results ($c=0\%$, $\omega^{\ast}(0)=1$, $q_{dd}=2$)
152
159
 
153
- | Mandate | $r_{\text{impl}}$ | $\Pi^*_T$ | $\mathbb{E}[\Pi_T]$ | Std | Survival | $r^{\text{BH}}_{\text{impl}}$ | $\text{Std}^{\text{BH}}$ |
160
+ | Mandate | $r_{\text{impl}}$ | $\Pi^{\ast}_T$ | $\mathbb{E}[\Pi_T]$ | Std | Survival | $r^{\text{BH}}_{\text{impl}}$ | $\text{Std}^{\text{BH}}$ |
154
161
  |---|---|---|---|---|---|---|---|
155
162
  | Income | 2.39% | 240 | 127 | 24.7 | 85.5% | 2.46% | 30.4 |
156
163
  | Conservative | 2.92% | 200 | 134 | 33.7 | 83.2% | 3.33% | 48.3 |
@@ -181,20 +188,22 @@ Requires Python >= 3.10 with NumPy >= 1.24, SciPy >= 1.10, and Matplotlib >= 3.7
181
188
 
182
189
  ```
183
190
  GoalBasedAllocation/
184
- ├── goal_based_allocation/ # Core library
185
- │ ├── regime_switch_paper.py # Laplace framework: density, survival, overshoot, BH moments
186
- │ ├── riccati_solver.py # Riccati ODE system + MC simulator
187
- │ ├── laplace_inversion.py # Abate-Whitt & Stehfest numerical inversion
188
- │ ├── client_solver.py # Effective asset construction, portfolio eta quadrature
189
- │ ├── mandate_utils.py # Portfolio mandate construction from assets
190
- │ ├── opportunity_set.py # Investment opportunity set & advisor framework
191
- │ └── vanilla_option_pricer.py # Laplace-transform vanilla option pricing (regime switching)
191
+ ├── src/
192
+ │ └── goal_based_allocation/ # Core library
193
+ │ ├── regime_switch_paper.py # Density, survival, overshoot, BH moments
194
+ │ ├── riccati_solver.py # Riccati ODE system + MC simulator
195
+ │ ├── laplace_inversion.py # Abate-Whitt & Stehfest numerical inversion
196
+ │ ├── client_solver.py # Effective asset construction, portfolio eta quadrature
197
+ │ ├── mandate_utils.py # Portfolio mandate construction from assets
198
+ │ ├── opportunity_set.py # Investment opportunity set & advisor framework
199
+ │ ├── vanilla_option_pricer.py # Regime-switching vanilla option pricing
200
+ │ └── run/ # Source-only development runners (*_local.py)
192
201
  ├── examples/ # Minimal, self-contained illustrations
193
202
  │ ├── wealth_process_simulation.py
194
203
  │ ├── terminal_wealth_distribution.py
195
204
  │ ├── investment_opportunity_set.py
196
205
  │ └── regime_switch_smile.py # Vol smile + Fourier/MC reference pricers
197
- ├── paper_code/
206
+ ├── papers/
198
207
  │ └── goal_based_allocation_2026/ # Self-contained paper: LaTeX, PDF, figures
199
208
  │ ├── goal_based_allocation_2026.tex
200
209
  │ ├── generate_paper_figures.py # All 10 figures + integration tests
@@ -216,6 +225,10 @@ GoalBasedAllocation/
216
225
  | `opportunity_set` | Two-step advisor framework: opportunity set + client profile | `AdvisorSpec`, `compute_opportunity_point`, `build_opportunity_set` |
217
226
  | `vanilla_option_pricer` | Laplace-transform vanilla option pricing under regime switching | `RiskNeutralParams`, `price_vanilla`, `implied_vol` |
218
227
 
228
+ Component development checks live under `src/goal_based_allocation/run/` as
229
+ `<subject>_local.py`. Each runner exposes `Locals` and `run_local(local=...)`; these files are
230
+ excluded from built distributions. Automated checks remain in the top-level `tests/` directory.
231
+
219
232
  ## Quick Start
220
233
 
221
234
  ### 1. Compute survival probability for a single asset
@@ -333,13 +346,17 @@ Fourier and Monte Carlo cross-checks.
333
346
 
334
347
  ## Reproducing Paper Results
335
348
 
336
- All figures and integration tests can be reproduced with a single command:
349
+ The `papers/` projects are available from a GitHub clone and are intentionally not
350
+ included in PyPI distributions. From a clone, the main paper's integration tests and
351
+ figures can be reproduced with the commands below. The current `--test` mode runs the
352
+ tests and then generates all figures; use `--outdir` to keep verification output away
353
+ from tracked figures.
337
354
 
338
355
  ```bash
339
- cd paper_code/goal_based_allocation_2026
356
+ cd papers/goal_based_allocation_2026
340
357
 
341
- # Run integration tests (9 assertions)
342
- python generate_paper_figures.py --test
358
+ # Run 9 integration assertions, then generate figures into a temporary directory
359
+ python generate_paper_figures.py --test --outdir my_verification_figures/
343
360
 
344
361
  # Generate all figures only (writes to ./figures/)
345
362
  python generate_paper_figures.py
@@ -383,8 +400,8 @@ The `--test` flag runs 7 tests with 9 assertions covering:
383
400
  ### Selected figures
384
401
 
385
402
  <p align="center">
386
- <img src="paper_code/goal_based_allocation_2026/figures/mandate_density_overlay_c0.png" width="48%" />
387
- <img src="paper_code/goal_based_allocation_2026/figures/mandate_comparison.png" width="48%" />
403
+ <img src="papers/goal_based_allocation_2026/figures/mandate_density_overlay_c0.png" width="48%" />
404
+ <img src="papers/goal_based_allocation_2026/figures/mandate_comparison.png" width="48%" />
388
405
  </p>
389
406
 
390
407
  <p align="center">
@@ -393,8 +410,8 @@ The `--test` flag runs 7 tests with 9 assertions covering:
393
410
  </p>
394
411
 
395
412
  <p align="center">
396
- <img src="paper_code/goal_based_allocation_2026/figures/path_dynamics_balanced.png" width="48%" />
397
- <img src="paper_code/goal_based_allocation_2026/figures/opportunity_set_c0.png" width="48%" />
413
+ <img src="papers/goal_based_allocation_2026/figures/path_dynamics_balanced.png" width="48%" />
414
+ <img src="papers/goal_based_allocation_2026/figures/opportunity_set_c0.png" width="48%" />
398
415
  </p>
399
416
 
400
417
  <p align="center">
@@ -409,7 +426,7 @@ The analytical framework proceeds in three steps:
409
426
  **Step 1: Riccati ODE system.** The pre-commitment MV problem reduces to a
410
427
  system of coupled Riccati ODEs for the value function coefficients
411
428
  in each regime. The solution yields the optimal allocation
412
- intensity $|\omega^*_a|$ and the target wealth trajectory $\Pi^*(t)$.
429
+ intensity $|\omega^{\ast}_a|$ and the target wealth trajectory $\Pi^{\ast}(t)$.
413
430
 
414
431
  **Step 2: Gap process.** Under the optimal policy, the log-cushion ratio
415
432
  $X_t = \ln(B_t/Z_t)$ is a regime-switching jump-diffusion with an absorbing
@@ -426,16 +443,22 @@ jump sizes via deterministic numerical integration (`portfolio_eta_quadrature`).
426
443
  Buy-and-hold benchmark moments are computed exactly via the 2×2 matrix exponential
427
444
  of Proposition B.7.
428
445
 
429
- ## Related Packages
446
+ ## Ecosystem
447
+
448
+ This package is part of an open-source Python stack for quantitative finance — full catalogue at [github.com/ArturSepp](https://github.com/ArturSepp):
430
449
 
431
- | Package | Description |
450
+ | Package | Purpose |
432
451
  |---|---|
433
- | [OptimalPortfolios](https://github.com/ArturSepp/OptimalPortfolios) | Optimal portfolio construction and backtesting |
434
- | [StochVolModels](https://github.com/ArturSepp/StochVolModels) | Stochastic volatility models for options pricing |
435
- | [QuantInvestStrats](https://github.com/ArturSepp/QuantInvestStrats) | Quantitative investment strategies and analytics |
436
- | [BloombergFetch](https://github.com/ArturSepp/BloombergFetch) | Bloomberg data API wrapper |
437
- | [VanillaOptionPricers](https://github.com/ArturSepp/VanillaOptionPricers) | Vanilla option pricing models |
438
- | [factorlasso](https://github.com/ArturSepp/factorlasso) | Factor model estimation with LASSO |
452
+ | [`qis`](https://github.com/ArturSepp/QuantInvestStrats) | Performance analytics, factsheets, and visualisation |
453
+ | [`optimalportfolios`](https://github.com/ArturSepp/OptimalPortfolios) | Portfolio construction and backtesting |
454
+ | [`factorlasso`](https://github.com/ArturSepp/factorlasso) | Sparse factor models and factor covariance estimation |
455
+ | [`bbg-fetch`](https://github.com/ArturSepp/BloombergFetch) | Bloomberg data fetching |
456
+ | [`trendfollowing`](https://github.com/ArturSepp/TrendFollowingSystems) | Trend-following systems: closed-form theory and replication |
457
+ | [`goal-based-allocation`](https://github.com/ArturSepp/GoalBasedAllocation) *(this package)* | Dynamic MV allocation under regime-switching jump-diffusions |
458
+ | [`stochvolmodels`](https://github.com/ArturSepp/StochVolModels) | Stochastic volatility pricing analytics |
459
+ | [`vanilla-option-pricers`](https://github.com/ArturSepp/VanillaOptionPricers) | Vectorised vanilla option pricers and implied volatility fitters |
460
+
461
+ Dependency links within the stack: `optimalportfolios` builds on `qis` and `factorlasso`; `trendfollowing` builds on `qis`.
439
462
 
440
463
  ## Citation
441
464
 
@@ -461,7 +484,7 @@ If you use this work in your research, please cite both the paper and the softwa
461
484
  title = {{GoalBasedAllocation}: A {Python} package for dynamic mean-variance
462
485
  portfolio allocation under regime-switching jump-diffusions},
463
486
  year = {2026},
464
- note = {Version 0.2.0},
487
+ note = {Version 0.3.1},
465
488
  howpublished = {\url{https://github.com/ArturSepp/GoalBasedAllocation}}
466
489
  }
467
490
  ```
@@ -1,64 +1,21 @@
1
- Metadata-Version: 2.4
2
- Name: goal-based-allocation
3
- Version: 0.2.0
4
- Summary: Dynamic Mean-Variance Portfolio Allocation under Regime-Switching Jump-Diffusions with Absorbing Barriers and Distribution Matching
5
- Author: Artur Sepp
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/ArturSepp/GoalBasedAllocation
8
- Project-URL: Repository, https://github.com/ArturSepp/GoalBasedAllocation
9
- Project-URL: Issues, https://github.com/ArturSepp/GoalBasedAllocation/issues
10
- Keywords: portfolio-optimization,mean-variance,regime-switching,jump-diffusion,goal-based-investing,laplace-transform,option-pricing,quantitative-finance
11
- Classifier: Development Status :: 4 - Beta
12
- Classifier: Intended Audience :: Financial and Insurance Industry
13
- Classifier: Intended Audience :: Science/Research
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
- Classifier: Topic :: Scientific/Engineering :: Mathematics
21
- Requires-Python: >=3.10
22
- Description-Content-Type: text/markdown
23
- License-File: LICENSE
24
- Requires-Dist: numpy>=1.24
25
- Requires-Dist: scipy>=1.10
26
- Requires-Dist: matplotlib>=3.7
27
- Provides-Extra: dev
28
- Requires-Dist: pytest; extra == "dev"
29
- Requires-Dist: black; extra == "dev"
30
- Requires-Dist: ruff; extra == "dev"
31
- Dynamic: license-file
32
-
33
- # GoalBasedAllocation
1
+ # GoalBasedAllocation (`goal-based-allocation`)
34
2
 
35
- <p align="center">
36
- <em>Dynamic Mean-Variance Portfolio Allocation under Regime-Switching Jump-Diffusions<br>with Absorbing Barriers and Distribution Matching</em>
37
- </p>
3
+ Analytical dynamic mean-variance allocation and terminal-wealth risk under
4
+ regime-switching jump-diffusions in Python for quantitative researchers and
5
+ wealth-management model developers.
38
6
 
39
- <p align="center">
40
- <a href="https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml">
41
- <img src="https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml/badge.svg" alt="Tests">
42
- </a>
43
- <a href="https://github.com/ArturSepp/GoalBasedAllocation/blob/main/LICENSE">
44
- <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT">
45
- </a>
46
- <a href="https://pypi.org/project/goal-based-allocation/">
47
- <img src="https://img.shields.io/pypi/v/goal-based-allocation.svg" alt="PyPI">
48
- </a>
49
- <a href="https://pypi.org/project/goal-based-allocation/">
50
- <img src="https://img.shields.io/pypi/pyversions/goal-based-allocation.svg" alt="Python">
51
- </a>
52
- <a href="https://pepy.tech/project/goal-based-allocation">
53
- <img src="https://pepy.tech/badge/goal-based-allocation" alt="Downloads">
54
- </a>
55
- </p>
7
+ [![PyPI](https://img.shields.io/pypi/v/goal-based-allocation?style=flat-square)](https://pypi.org/project/goal-based-allocation/)
8
+ [![Python](https://img.shields.io/pypi/pyversions/goal-based-allocation?style=flat-square)](https://pypi.org/project/goal-based-allocation/)
9
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
+ [![Tests](https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml/badge.svg)](https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml)
11
+ [![Downloads](https://static.pepy.tech/badge/goal-based-allocation)](https://pepy.tech/project/goal-based-allocation)
12
+ [![Monthly](https://static.pepy.tech/badge/goal-based-allocation/month)](https://pepy.tech/project/goal-based-allocation)
13
+
14
+ **Documentation:** [artursepp.github.io/GoalBasedAllocation](https://artursepp.github.io/GoalBasedAllocation/)
56
15
 
57
- Companion code to:
16
+ **Paper:** companion code to Sepp, A. (2026), *Dynamic Mean-Variance Portfolio Allocation under Regime-Switching Jump-Diffusions with Absorbing Barriers and Distribution Matching* — [SSRN 6534579](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6534579). See [Citation](#citation) for BibTeX.
58
17
 
59
- > **Sepp, A. (2026). Dynamic Mean-Variance Portfolio Allocation under Regime-Switching
60
- > Jump-Diffusions with Absorbing Barriers and Distribution Matching.**
61
- > [SSRN: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6534579](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6534579)
18
+ ---
62
19
 
63
20
  ## Overview
64
21
 
@@ -66,12 +23,18 @@ This package provides a **fully analytical Laplace-transform framework** for dyn
66
23
  mean-variance (MV) portfolio allocation under a two-state regime-switching model
67
24
  with exponential jumps at regime transitions and an absorbing wealth floor.
68
25
 
26
+ It models two regimes and multi-asset mandates aggregated to one effective risky
27
+ asset. It is not a discrete constrained multi-asset optimiser, trading engine, or
28
+ production portfolio-management system; use
29
+ [`optimalportfolios`](https://github.com/ArturSepp/OptimalPortfolios) for rolling
30
+ multi-asset construction, constraints, transaction costs, and backtesting.
31
+
69
32
  The MV-optimal strategy takes the form
70
33
 
71
- $$\omega^*(t) = |\omega^*_a| \cdot \left(\frac{\Pi^*(t)}{\Pi_t} - 1\right)$$
34
+ $$\omega^{\ast}(t) = |\omega^{\ast}_a| \cdot \left(\frac{\Pi^{\ast}(t)}{\Pi_t} - 1\right)$$
72
35
 
73
- where $\Pi^*(t)$ is the target wealth trajectory derived from the Riccati ODE system,
74
- and $|\omega^*_a|$ is the regime-dependent allocation intensity. This produces an
36
+ where $\Pi^{\ast}(t)$ is the target wealth trajectory derived from the Riccati ODE system,
37
+ and $|\omega^{\ast}_a|$ is the regime-dependent allocation intensity. This produces an
75
38
  **endogenous de-risking glide path**: early in the horizon the funding gap is large
76
39
  and allocation is aggressive; as the portfolio approaches the target, allocation
77
40
  moderates automatically.
@@ -101,6 +64,12 @@ simulation is needed for pricing; MC is used only for validation.
101
64
  - Monte Carlo simulator for validation of all analytical results
102
65
  - Integration tests and all paper figures reproducible from a single command
103
66
 
67
+ ## When to use it — and when not
68
+
69
+ Use `goal-based-allocation` to design goal-based mandates under regime switching: survival and shortfall probabilities against a wealth floor, MV-optimal allocation intensities and glide paths, terminal wealth distributions, investment opportunity sets for client advice, and regime-switching vanilla option pricing on the same Laplace engine — all analytically, with Monte Carlo reserved for validation.
70
+
71
+ The model is deliberately parsimonious: two regimes, exponential jumps at transitions, and multi-asset mandates reduced to a single effective asset via portfolio aggregation. For discrete rolling multi-asset optimisation with weight constraints and transaction costs, use [`optimalportfolios`](https://github.com/ArturSepp/OptimalPortfolios).
72
+
104
73
  ## Model
105
74
 
106
75
  The portfolio wealth $\Pi_t$ follows a regime-switching jump-diffusion
@@ -114,7 +83,7 @@ with two states: **growth** ($i=1$) and **stress** ($i=2$).
114
83
  | $\mu^{[i]} = \bar{\mu}^{[i]} - \lambda \alpha^{[i]}$ | Diffusion drift | `mu_bar` |
115
84
  | $r_h = \max(r, c)$ | Hurdle rate | `r_h` |
116
85
  | $r_c = r_h - c = \max(0, r-c)$ | Net floor growth rate | `r_c` |
117
- | $\omega^*_a = -\tilde{a}^{[i]}/\Sigma^{[i]}$ | Risky allocation coefficient | `w_a` |
86
+ | $\omega^{\ast}_a = -\tilde{a}^{[i]}/\Sigma^{[i]}$ | Risky allocation coefficient | `w_a` |
118
87
 
119
88
  ### Process specification
120
89
 
@@ -123,7 +92,7 @@ with two states: **growth** ($i=1$) and **stress** ($i=2$).
123
92
  | Diffusion | Regime-dependent drift $\mu^{[i]}$ and volatility $\sigma^{[i]}$ |
124
93
  | Jumps | Exponential crash at growth→stress ($\eta^{[1]}$), exponential recovery at stress→growth ($\eta^{[2]}$) |
125
94
  | Regime switching | Poisson rates $\lambda^{[12]}$ (crash) and $\lambda^{[21]}$ (recovery) |
126
- | MV-optimal control | $\omega^*(t) = \|\omega_a^*\| \cdot (\Pi^*(t)/\Pi_t - 1)$, from Riccati system |
95
+ | MV-optimal control | $\omega^{\ast}(t) = \|\omega_a^{\ast}\| \cdot (\Pi^{\ast}(t)/\Pi_t - 1)$, from Riccati system |
127
96
  | Absorbing floor | Wealth stopped at $L_t = L_0 \cdot e^{r_c t}$, converting to cash |
128
97
 
129
98
  ### Asset class parameters
@@ -148,9 +117,9 @@ $w_{\text{eq}} = g(1-w)$, $w_{\text{pe}} = (1-g)(1-w)$.
148
117
  | Balanced | 35/43/22 | 11.1 / 16.7% | 4.34 / 0.70% | 0.233 |
149
118
  | Growth | 0/67/33 | 15.8 / 23.8% | 5.33 / 0.00% | 0.334 |
150
119
 
151
- ### MV-optimal results ($c=0\%$, $\omega^*(0)=1$, $q_{dd}=2$)
120
+ ### MV-optimal results ($c=0\%$, $\omega^{\ast}(0)=1$, $q_{dd}=2$)
152
121
 
153
- | Mandate | $r_{\text{impl}}$ | $\Pi^*_T$ | $\mathbb{E}[\Pi_T]$ | Std | Survival | $r^{\text{BH}}_{\text{impl}}$ | $\text{Std}^{\text{BH}}$ |
122
+ | Mandate | $r_{\text{impl}}$ | $\Pi^{\ast}_T$ | $\mathbb{E}[\Pi_T]$ | Std | Survival | $r^{\text{BH}}_{\text{impl}}$ | $\text{Std}^{\text{BH}}$ |
154
123
  |---|---|---|---|---|---|---|---|
155
124
  | Income | 2.39% | 240 | 127 | 24.7 | 85.5% | 2.46% | 30.4 |
156
125
  | Conservative | 2.92% | 200 | 134 | 33.7 | 83.2% | 3.33% | 48.3 |
@@ -181,20 +150,22 @@ Requires Python >= 3.10 with NumPy >= 1.24, SciPy >= 1.10, and Matplotlib >= 3.7
181
150
 
182
151
  ```
183
152
  GoalBasedAllocation/
184
- ├── goal_based_allocation/ # Core library
185
- │ ├── regime_switch_paper.py # Laplace framework: density, survival, overshoot, BH moments
186
- │ ├── riccati_solver.py # Riccati ODE system + MC simulator
187
- │ ├── laplace_inversion.py # Abate-Whitt & Stehfest numerical inversion
188
- │ ├── client_solver.py # Effective asset construction, portfolio eta quadrature
189
- │ ├── mandate_utils.py # Portfolio mandate construction from assets
190
- │ ├── opportunity_set.py # Investment opportunity set & advisor framework
191
- │ └── vanilla_option_pricer.py # Laplace-transform vanilla option pricing (regime switching)
153
+ ├── src/
154
+ │ └── goal_based_allocation/ # Core library
155
+ │ ├── regime_switch_paper.py # Density, survival, overshoot, BH moments
156
+ │ ├── riccati_solver.py # Riccati ODE system + MC simulator
157
+ │ ├── laplace_inversion.py # Abate-Whitt & Stehfest numerical inversion
158
+ │ ├── client_solver.py # Effective asset construction, portfolio eta quadrature
159
+ │ ├── mandate_utils.py # Portfolio mandate construction from assets
160
+ │ ├── opportunity_set.py # Investment opportunity set & advisor framework
161
+ │ ├── vanilla_option_pricer.py # Regime-switching vanilla option pricing
162
+ │ └── run/ # Source-only development runners (*_local.py)
192
163
  ├── examples/ # Minimal, self-contained illustrations
193
164
  │ ├── wealth_process_simulation.py
194
165
  │ ├── terminal_wealth_distribution.py
195
166
  │ ├── investment_opportunity_set.py
196
167
  │ └── regime_switch_smile.py # Vol smile + Fourier/MC reference pricers
197
- ├── paper_code/
168
+ ├── papers/
198
169
  │ └── goal_based_allocation_2026/ # Self-contained paper: LaTeX, PDF, figures
199
170
  │ ├── goal_based_allocation_2026.tex
200
171
  │ ├── generate_paper_figures.py # All 10 figures + integration tests
@@ -216,6 +187,10 @@ GoalBasedAllocation/
216
187
  | `opportunity_set` | Two-step advisor framework: opportunity set + client profile | `AdvisorSpec`, `compute_opportunity_point`, `build_opportunity_set` |
217
188
  | `vanilla_option_pricer` | Laplace-transform vanilla option pricing under regime switching | `RiskNeutralParams`, `price_vanilla`, `implied_vol` |
218
189
 
190
+ Component development checks live under `src/goal_based_allocation/run/` as
191
+ `<subject>_local.py`. Each runner exposes `Locals` and `run_local(local=...)`; these files are
192
+ excluded from built distributions. Automated checks remain in the top-level `tests/` directory.
193
+
219
194
  ## Quick Start
220
195
 
221
196
  ### 1. Compute survival probability for a single asset
@@ -333,13 +308,17 @@ Fourier and Monte Carlo cross-checks.
333
308
 
334
309
  ## Reproducing Paper Results
335
310
 
336
- All figures and integration tests can be reproduced with a single command:
311
+ The `papers/` projects are available from a GitHub clone and are intentionally not
312
+ included in PyPI distributions. From a clone, the main paper's integration tests and
313
+ figures can be reproduced with the commands below. The current `--test` mode runs the
314
+ tests and then generates all figures; use `--outdir` to keep verification output away
315
+ from tracked figures.
337
316
 
338
317
  ```bash
339
- cd paper_code/goal_based_allocation_2026
318
+ cd papers/goal_based_allocation_2026
340
319
 
341
- # Run integration tests (9 assertions)
342
- python generate_paper_figures.py --test
320
+ # Run 9 integration assertions, then generate figures into a temporary directory
321
+ python generate_paper_figures.py --test --outdir my_verification_figures/
343
322
 
344
323
  # Generate all figures only (writes to ./figures/)
345
324
  python generate_paper_figures.py
@@ -383,8 +362,8 @@ The `--test` flag runs 7 tests with 9 assertions covering:
383
362
  ### Selected figures
384
363
 
385
364
  <p align="center">
386
- <img src="paper_code/goal_based_allocation_2026/figures/mandate_density_overlay_c0.png" width="48%" />
387
- <img src="paper_code/goal_based_allocation_2026/figures/mandate_comparison.png" width="48%" />
365
+ <img src="papers/goal_based_allocation_2026/figures/mandate_density_overlay_c0.png" width="48%" />
366
+ <img src="papers/goal_based_allocation_2026/figures/mandate_comparison.png" width="48%" />
388
367
  </p>
389
368
 
390
369
  <p align="center">
@@ -393,8 +372,8 @@ The `--test` flag runs 7 tests with 9 assertions covering:
393
372
  </p>
394
373
 
395
374
  <p align="center">
396
- <img src="paper_code/goal_based_allocation_2026/figures/path_dynamics_balanced.png" width="48%" />
397
- <img src="paper_code/goal_based_allocation_2026/figures/opportunity_set_c0.png" width="48%" />
375
+ <img src="papers/goal_based_allocation_2026/figures/path_dynamics_balanced.png" width="48%" />
376
+ <img src="papers/goal_based_allocation_2026/figures/opportunity_set_c0.png" width="48%" />
398
377
  </p>
399
378
 
400
379
  <p align="center">
@@ -409,7 +388,7 @@ The analytical framework proceeds in three steps:
409
388
  **Step 1: Riccati ODE system.** The pre-commitment MV problem reduces to a
410
389
  system of coupled Riccati ODEs for the value function coefficients
411
390
  in each regime. The solution yields the optimal allocation
412
- intensity $|\omega^*_a|$ and the target wealth trajectory $\Pi^*(t)$.
391
+ intensity $|\omega^{\ast}_a|$ and the target wealth trajectory $\Pi^{\ast}(t)$.
413
392
 
414
393
  **Step 2: Gap process.** Under the optimal policy, the log-cushion ratio
415
394
  $X_t = \ln(B_t/Z_t)$ is a regime-switching jump-diffusion with an absorbing
@@ -426,16 +405,22 @@ jump sizes via deterministic numerical integration (`portfolio_eta_quadrature`).
426
405
  Buy-and-hold benchmark moments are computed exactly via the 2×2 matrix exponential
427
406
  of Proposition B.7.
428
407
 
429
- ## Related Packages
408
+ ## Ecosystem
409
+
410
+ This package is part of an open-source Python stack for quantitative finance — full catalogue at [github.com/ArturSepp](https://github.com/ArturSepp):
430
411
 
431
- | Package | Description |
412
+ | Package | Purpose |
432
413
  |---|---|
433
- | [OptimalPortfolios](https://github.com/ArturSepp/OptimalPortfolios) | Optimal portfolio construction and backtesting |
434
- | [StochVolModels](https://github.com/ArturSepp/StochVolModels) | Stochastic volatility models for options pricing |
435
- | [QuantInvestStrats](https://github.com/ArturSepp/QuantInvestStrats) | Quantitative investment strategies and analytics |
436
- | [BloombergFetch](https://github.com/ArturSepp/BloombergFetch) | Bloomberg data API wrapper |
437
- | [VanillaOptionPricers](https://github.com/ArturSepp/VanillaOptionPricers) | Vanilla option pricing models |
438
- | [factorlasso](https://github.com/ArturSepp/factorlasso) | Factor model estimation with LASSO |
414
+ | [`qis`](https://github.com/ArturSepp/QuantInvestStrats) | Performance analytics, factsheets, and visualisation |
415
+ | [`optimalportfolios`](https://github.com/ArturSepp/OptimalPortfolios) | Portfolio construction and backtesting |
416
+ | [`factorlasso`](https://github.com/ArturSepp/factorlasso) | Sparse factor models and factor covariance estimation |
417
+ | [`bbg-fetch`](https://github.com/ArturSepp/BloombergFetch) | Bloomberg data fetching |
418
+ | [`trendfollowing`](https://github.com/ArturSepp/TrendFollowingSystems) | Trend-following systems: closed-form theory and replication |
419
+ | [`goal-based-allocation`](https://github.com/ArturSepp/GoalBasedAllocation) *(this package)* | Dynamic MV allocation under regime-switching jump-diffusions |
420
+ | [`stochvolmodels`](https://github.com/ArturSepp/StochVolModels) | Stochastic volatility pricing analytics |
421
+ | [`vanilla-option-pricers`](https://github.com/ArturSepp/VanillaOptionPricers) | Vectorised vanilla option pricers and implied volatility fitters |
422
+
423
+ Dependency links within the stack: `optimalportfolios` builds on `qis` and `factorlasso`; `trendfollowing` builds on `qis`.
439
424
 
440
425
  ## Citation
441
426
 
@@ -461,7 +446,7 @@ If you use this work in your research, please cite both the paper and the softwa
461
446
  title = {{GoalBasedAllocation}: A {Python} package for dynamic mean-variance
462
447
  portfolio allocation under regime-switching jump-diffusions},
463
448
  year = {2026},
464
- note = {Version 0.2.0},
449
+ note = {Version 0.3.1},
465
450
  howpublished = {\url{https://github.com/ArturSepp/GoalBasedAllocation}}
466
451
  }
467
452
  ```