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.
- goal_based_allocation-0.3.1/MANIFEST.in +1 -0
- {goal_based_allocation-0.2.0/goal_based_allocation.egg-info → goal_based_allocation-0.3.1}/PKG-INFO +87 -64
- goal_based_allocation-0.2.0/PKG-INFO → goal_based_allocation-0.3.1/README.md +76 -91
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/pyproject.toml +69 -58
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/__init__.py +56 -42
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/client_solver.py +0 -65
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/laplace_inversion.py +0 -102
- goal_based_allocation-0.3.1/src/goal_based_allocation/mandate_utils.py +101 -0
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/opportunity_set.py +0 -77
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/regime_switch_paper.py +0 -75
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/riccati_solver.py +0 -46
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation/vanilla_option_pricer.py +10 -3
- goal_based_allocation-0.3.1/src/goal_based_allocation/variance_swap.py +449 -0
- goal_based_allocation-0.2.0/README.md → goal_based_allocation-0.3.1/src/goal_based_allocation.egg-info/PKG-INFO +512 -457
- goal_based_allocation-0.3.1/src/goal_based_allocation.egg-info/SOURCES.txt +27 -0
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation.egg-info/requires.txt +5 -0
- goal_based_allocation-0.3.1/tests/test_docs.py +37 -0
- goal_based_allocation-0.3.1/tests/test_metadata.py +32 -0
- goal_based_allocation-0.3.1/tests/test_module_layout.py +191 -0
- goal_based_allocation-0.3.1/tests/test_quickstart.py +20 -0
- goal_based_allocation-0.3.1/tests/test_repository_layout.py +54 -0
- goal_based_allocation-0.3.1/tests/test_variance_swap.py +229 -0
- goal_based_allocation-0.2.0/goal_based_allocation/mandate_utils.py +0 -314
- goal_based_allocation-0.2.0/goal_based_allocation.egg-info/SOURCES.txt +0 -19
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/LICENSE +0 -0
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/setup.cfg +0 -0
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation.egg-info/dependency_links.txt +0 -0
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1/src}/goal_based_allocation.egg-info/top_level.txt +0 -0
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/tests/test_framework.py +0 -0
- {goal_based_allocation-0.2.0 → goal_based_allocation-0.3.1}/tests/test_reference_crosschecks.py +0 -0
- {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
|
{goal_based_allocation-0.2.0/goal_based_allocation.egg-info → goal_based_allocation-0.3.1}/PKG-INFO
RENAMED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: goal-based-allocation
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
[](https://pypi.org/project/goal-based-allocation/)
|
|
46
|
+
[](https://pypi.org/project/goal-based-allocation/)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
[](https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml)
|
|
49
|
+
[](https://pepy.tech/project/goal-based-allocation)
|
|
50
|
+
[](https://pepy.tech/project/goal-based-allocation)
|
|
51
|
+
|
|
52
|
+
**Documentation:** [artursepp.github.io/GoalBasedAllocation](https://artursepp.github.io/GoalBasedAllocation/)
|
|
56
53
|
|
|
57
|
-
|
|
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
|
-
|
|
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
|
|
72
|
+
$$\omega^{\ast}(t) = |\omega^{\ast}_a| \cdot \left(\frac{\Pi^{\ast}(t)}{\Pi_t} - 1\right)$$
|
|
72
73
|
|
|
73
|
-
where $\Pi
|
|
74
|
-
and $|\omega
|
|
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
|
|
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
|
|
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
|
|
158
|
+
### MV-optimal results ($c=0\%$, $\omega^{\ast}(0)=1$, $q_{dd}=2$)
|
|
152
159
|
|
|
153
|
-
| Mandate | $r_{\text{impl}}$ | $\Pi
|
|
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
|
-
├──
|
|
185
|
-
│
|
|
186
|
-
│
|
|
187
|
-
│
|
|
188
|
-
│
|
|
189
|
-
│
|
|
190
|
-
│
|
|
191
|
-
│
|
|
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
|
-
├──
|
|
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
|
-
|
|
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
|
|
356
|
+
cd papers/goal_based_allocation_2026
|
|
340
357
|
|
|
341
|
-
# Run integration
|
|
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="
|
|
387
|
-
<img src="
|
|
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="
|
|
397
|
-
<img src="
|
|
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
|
|
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
|
-
##
|
|
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 |
|
|
450
|
+
| Package | Purpose |
|
|
432
451
|
|---|---|
|
|
433
|
-
| [
|
|
434
|
-
| [
|
|
435
|
-
| [
|
|
436
|
-
| [
|
|
437
|
-
| [
|
|
438
|
-
| [
|
|
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.
|
|
487
|
+
note = {Version 0.3.1},
|
|
465
488
|
howpublished = {\url{https://github.com/ArturSepp/GoalBasedAllocation}}
|
|
466
489
|
}
|
|
467
490
|
```
|
|
@@ -1,64 +1,21 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
[](https://pypi.org/project/goal-based-allocation/)
|
|
8
|
+
[](https://pypi.org/project/goal-based-allocation/)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://github.com/ArturSepp/GoalBasedAllocation/actions/workflows/tests.yml)
|
|
11
|
+
[](https://pepy.tech/project/goal-based-allocation)
|
|
12
|
+
[](https://pepy.tech/project/goal-based-allocation)
|
|
13
|
+
|
|
14
|
+
**Documentation:** [artursepp.github.io/GoalBasedAllocation](https://artursepp.github.io/GoalBasedAllocation/)
|
|
56
15
|
|
|
57
|
-
|
|
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
|
-
|
|
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
|
|
34
|
+
$$\omega^{\ast}(t) = |\omega^{\ast}_a| \cdot \left(\frac{\Pi^{\ast}(t)}{\Pi_t} - 1\right)$$
|
|
72
35
|
|
|
73
|
-
where $\Pi
|
|
74
|
-
and $|\omega
|
|
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
|
|
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
|
|
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
|
|
120
|
+
### MV-optimal results ($c=0\%$, $\omega^{\ast}(0)=1$, $q_{dd}=2$)
|
|
152
121
|
|
|
153
|
-
| Mandate | $r_{\text{impl}}$ | $\Pi
|
|
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
|
-
├──
|
|
185
|
-
│
|
|
186
|
-
│
|
|
187
|
-
│
|
|
188
|
-
│
|
|
189
|
-
│
|
|
190
|
-
│
|
|
191
|
-
│
|
|
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
|
-
├──
|
|
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
|
-
|
|
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
|
|
318
|
+
cd papers/goal_based_allocation_2026
|
|
340
319
|
|
|
341
|
-
# Run integration
|
|
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="
|
|
387
|
-
<img src="
|
|
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="
|
|
397
|
-
<img src="
|
|
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
|
|
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
|
-
##
|
|
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 |
|
|
412
|
+
| Package | Purpose |
|
|
432
413
|
|---|---|
|
|
433
|
-
| [
|
|
434
|
-
| [
|
|
435
|
-
| [
|
|
436
|
-
| [
|
|
437
|
-
| [
|
|
438
|
-
| [
|
|
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.
|
|
449
|
+
note = {Version 0.3.1},
|
|
465
450
|
howpublished = {\url{https://github.com/ArturSepp/GoalBasedAllocation}}
|
|
466
451
|
}
|
|
467
452
|
```
|