quantlite 0.2.0__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.
- quantlite-0.2.0/LICENSE +22 -0
- quantlite-0.2.0/PKG-INFO +584 -0
- quantlite-0.2.0/README.md +525 -0
- quantlite-0.2.0/pyproject.toml +68 -0
- quantlite-0.2.0/setup.cfg +4 -0
- quantlite-0.2.0/src/quantlite/__init__.py +60 -0
- quantlite-0.2.0/src/quantlite/backtesting/__init__.py +43 -0
- quantlite-0.2.0/src/quantlite/backtesting/analysis.py +226 -0
- quantlite-0.2.0/src/quantlite/backtesting/engine.py +407 -0
- quantlite-0.2.0/src/quantlite/backtesting/legacy.py +101 -0
- quantlite-0.2.0/src/quantlite/backtesting/signals.py +182 -0
- quantlite-0.2.0/src/quantlite/core/__init__.py +1 -0
- quantlite-0.2.0/src/quantlite/core/types.py +152 -0
- quantlite-0.2.0/src/quantlite/data_generation.py +228 -0
- quantlite-0.2.0/src/quantlite/dependency/__init__.py +1 -0
- quantlite-0.2.0/src/quantlite/dependency/clustering.py +177 -0
- quantlite-0.2.0/src/quantlite/dependency/copulas.py +663 -0
- quantlite-0.2.0/src/quantlite/dependency/correlation.py +192 -0
- quantlite-0.2.0/src/quantlite/distributions/__init__.py +1 -0
- quantlite-0.2.0/src/quantlite/distributions/fat_tails.py +239 -0
- quantlite-0.2.0/src/quantlite/instruments/__init__.py +1 -0
- quantlite-0.2.0/src/quantlite/instruments/bond_pricing.py +108 -0
- quantlite-0.2.0/src/quantlite/instruments/exotic_options.py +121 -0
- quantlite-0.2.0/src/quantlite/instruments/option_pricing.py +104 -0
- quantlite-0.2.0/src/quantlite/metrics.py +86 -0
- quantlite-0.2.0/src/quantlite/monte_carlo.py +87 -0
- quantlite-0.2.0/src/quantlite/portfolio/__init__.py +35 -0
- quantlite-0.2.0/src/quantlite/portfolio/optimisation.py +506 -0
- quantlite-0.2.0/src/quantlite/portfolio/rebalancing.py +249 -0
- quantlite-0.2.0/src/quantlite/regimes/__init__.py +1 -0
- quantlite-0.2.0/src/quantlite/regimes/changepoint.py +249 -0
- quantlite-0.2.0/src/quantlite/regimes/conditional.py +171 -0
- quantlite-0.2.0/src/quantlite/regimes/hmm.py +224 -0
- quantlite-0.2.0/src/quantlite/risk/__init__.py +1 -0
- quantlite-0.2.0/src/quantlite/risk/evt.py +261 -0
- quantlite-0.2.0/src/quantlite/risk/metrics.py +262 -0
- quantlite-0.2.0/src/quantlite/visualisation.py +202 -0
- quantlite-0.2.0/src/quantlite/viz/__init__.py +1 -0
- quantlite-0.2.0/src/quantlite/viz/dependency.py +300 -0
- quantlite-0.2.0/src/quantlite/viz/portfolio.py +413 -0
- quantlite-0.2.0/src/quantlite/viz/regimes.py +304 -0
- quantlite-0.2.0/src/quantlite/viz/risk.py +284 -0
- quantlite-0.2.0/src/quantlite/viz/theme.py +222 -0
- quantlite-0.2.0/src/quantlite.egg-info/PKG-INFO +584 -0
- quantlite-0.2.0/src/quantlite.egg-info/SOURCES.txt +70 -0
- quantlite-0.2.0/src/quantlite.egg-info/dependency_links.txt +1 -0
- quantlite-0.2.0/src/quantlite.egg-info/requires.txt +11 -0
- quantlite-0.2.0/src/quantlite.egg-info/top_level.txt +1 -0
- quantlite-0.2.0/tests/test_analysis.py +113 -0
- quantlite-0.2.0/tests/test_backtesting.py +33 -0
- quantlite-0.2.0/tests/test_changepoint.py +54 -0
- quantlite-0.2.0/tests/test_clustering.py +67 -0
- quantlite-0.2.0/tests/test_conditional.py +74 -0
- quantlite-0.2.0/tests/test_copulas.py +135 -0
- quantlite-0.2.0/tests/test_correlation.py +103 -0
- quantlite-0.2.0/tests/test_data_generation.py +43 -0
- quantlite-0.2.0/tests/test_engine.py +113 -0
- quantlite-0.2.0/tests/test_evt.py +116 -0
- quantlite-0.2.0/tests/test_fat_tails.py +118 -0
- quantlite-0.2.0/tests/test_hmm.py +86 -0
- quantlite-0.2.0/tests/test_instruments.py +56 -0
- quantlite-0.2.0/tests/test_metrics.py +32 -0
- quantlite-0.2.0/tests/test_monte_carlo.py +20 -0
- quantlite-0.2.0/tests/test_optimisation.py +141 -0
- quantlite-0.2.0/tests/test_rebalancing.py +78 -0
- quantlite-0.2.0/tests/test_risk_metrics.py +127 -0
- quantlite-0.2.0/tests/test_signals.py +93 -0
- quantlite-0.2.0/tests/test_visualisation.py +44 -0
- quantlite-0.2.0/tests/test_viz.py +105 -0
- quantlite-0.2.0/tests/test_viz_dependency.py +67 -0
- quantlite-0.2.0/tests/test_viz_portfolio.py +83 -0
- quantlite-0.2.0/tests/test_viz_regimes.py +53 -0
quantlite-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Prasant Sudhakaran
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
quantlite-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantlite
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A fat-tail-native quantitative finance toolkit: EVT, risk metrics, and honest modelling for markets that bite.
|
|
5
|
+
Author-email: Prasant Sudhakaran <code@prasant.net>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Prasant Sudhakaran
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in
|
|
18
|
+
all copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
26
|
+
THE SOFTWARE.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Project-URL: Homepage, https://github.com/prasants/QuantLite
|
|
30
|
+
Project-URL: Repository, https://github.com/prasants/QuantLite
|
|
31
|
+
Project-URL: Issues, https://github.com/prasants/QuantLite/issues
|
|
32
|
+
Keywords: quant,finance,risk,extreme value theory,monte carlo,fat tails,VaR,CVaR,options
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
35
|
+
Classifier: Intended Audience :: Science/Research
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
42
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
43
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
44
|
+
Classifier: Typing :: Typed
|
|
45
|
+
Requires-Python: >=3.10
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
License-File: LICENSE
|
|
48
|
+
Requires-Dist: numpy>=1.24
|
|
49
|
+
Requires-Dist: pandas>=2.0
|
|
50
|
+
Requires-Dist: scipy>=1.10
|
|
51
|
+
Requires-Dist: matplotlib>=3.7
|
|
52
|
+
Requires-Dist: mplfinance
|
|
53
|
+
Provides-Extra: dev
|
|
54
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
55
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
56
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
57
|
+
Requires-Dist: pandas-stubs; extra == "dev"
|
|
58
|
+
Dynamic: license-file
|
|
59
|
+
|
|
60
|
+
# QuantLite
|
|
61
|
+
|
|
62
|
+
**A fat-tail-native quantitative finance toolkit for Python.**
|
|
63
|
+
|
|
64
|
+
[](https://pypi.org/project/quantlite/)
|
|
65
|
+
[](https://www.python.org/downloads/)
|
|
66
|
+
[](LICENSE)
|
|
67
|
+
|
|
68
|
+
QuantLite is what pandas is to data manipulation, but for fat-tail quant finance. It provides honest modelling tools for markets that bite: extreme value theory, copula dependence structures, regime detection, fat-tailed distributions, and risk metrics that do not assume returns are Gaussian.
|
|
69
|
+
|
|
70
|
+
Most quantitative finance libraries treat fat tails as an afterthought. QuantLite treats them as the starting point.
|
|
71
|
+
|
|
72
|
+
## Key Differentiators
|
|
73
|
+
|
|
74
|
+
- **Fat-tail-native**: Every risk metric, simulation, and optimisation accounts for non-Gaussian behaviour
|
|
75
|
+
- **Extreme Value Theory**: GPD, GEV, Hill estimator, and Peaks Over Threshold for rigorous tail modelling
|
|
76
|
+
- **Copula dependence**: Five copula families with tail dependence analysis, not just Pearson correlation
|
|
77
|
+
- **Regime detection**: Hidden Markov Models and Bayesian changepoint detection for structural breaks
|
|
78
|
+
- **Stephen Few visualisations**: Publication-quality charts following Few's principles of maximum data-ink ratio
|
|
79
|
+
- **Production backtesting**: Multi-asset engine with circuit breakers, slippage models, and regime-aware allocation
|
|
80
|
+
|
|
81
|
+
## Installation
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install quantlite
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For development:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install quantlite[dev]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Optional dependency for HMM regime detection:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install hmmlearn
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Quickstart
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import numpy as np
|
|
103
|
+
import quantlite as ql
|
|
104
|
+
from quantlite.distributions.fat_tails import student_t_process
|
|
105
|
+
from quantlite.risk.metrics import value_at_risk, cvar, return_moments
|
|
106
|
+
from quantlite.risk.evt import tail_risk_summary
|
|
107
|
+
|
|
108
|
+
# Generate fat-tailed returns (nu=4 gives realistic equity tail behaviour)
|
|
109
|
+
returns = student_t_process(nu=4.0, mu=0.0003, sigma=0.012, n_steps=2520, rng_seed=42)
|
|
110
|
+
|
|
111
|
+
# Standard risk metrics
|
|
112
|
+
var_95 = value_at_risk(returns, alpha=0.05)
|
|
113
|
+
cvar_95 = cvar(returns, alpha=0.05)
|
|
114
|
+
moments = return_moments(returns)
|
|
115
|
+
|
|
116
|
+
print(f"VaR (95%): {var_95:.4f}")
|
|
117
|
+
print(f"CVaR (95%): {cvar_95:.4f}")
|
|
118
|
+
print(f"Excess kurtosis: {moments.kurtosis:.2f}")
|
|
119
|
+
|
|
120
|
+
# Full tail risk analysis with EVT
|
|
121
|
+
summary = tail_risk_summary(returns)
|
|
122
|
+
print(f"Hill tail index: {summary.hill_estimate.tail_index:.2f}")
|
|
123
|
+
print(f"GPD shape (xi): {summary.gpd_fit.shape:.4f}")
|
|
124
|
+
print(f"1-in-100 loss: {summary.return_level_100:.4f}")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Modules
|
|
128
|
+
|
|
129
|
+
### Risk Metrics
|
|
130
|
+
|
|
131
|
+
Classical and tail-aware risk measures: VaR (historical, parametric, Cornish-Fisher), CVaR, Sortino ratio, Calmar ratio, Omega ratio, tail ratio, and maximum drawdown with duration tracking.
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from quantlite.risk.metrics import (
|
|
135
|
+
value_at_risk, cvar, sortino_ratio, calmar_ratio,
|
|
136
|
+
omega_ratio, tail_ratio, max_drawdown_duration, return_moments,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Cornish-Fisher VaR accounts for skewness and kurtosis
|
|
140
|
+
cf_var = value_at_risk(returns, alpha=0.01, method="cornish-fisher")
|
|
141
|
+
hist_var = value_at_risk(returns, alpha=0.01, method="historical")
|
|
142
|
+
print(f"CF VaR (99%): {cf_var:.4f}")
|
|
143
|
+
print(f"Hist VaR (99%): {hist_var:.4f}")
|
|
144
|
+
|
|
145
|
+
# Drawdown analysis
|
|
146
|
+
dd = max_drawdown_duration(returns)
|
|
147
|
+
print(f"Max drawdown: {dd.max_drawdown:.2%}, duration: {dd.duration} periods")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
[Detailed documentation: docs/risk.md](docs/risk.md)
|
|
151
|
+
|
|
152
|
+
### Extreme Value Theory
|
|
153
|
+
|
|
154
|
+
Rigorous tail modelling via the Generalised Pareto Distribution, Generalised Extreme Value distribution, Hill estimator, and Peaks Over Threshold method.
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from quantlite.risk.evt import fit_gpd, hill_estimator, return_level, tail_risk_summary
|
|
158
|
+
|
|
159
|
+
# Fit GPD to the tail exceedances
|
|
160
|
+
gpd = fit_gpd(returns)
|
|
161
|
+
print(f"GPD shape (xi): {gpd.shape:.4f}")
|
|
162
|
+
print(f"GPD scale: {gpd.scale:.4f}")
|
|
163
|
+
print(f"Exceedances: {gpd.n_exceedances} / {gpd.n_total}")
|
|
164
|
+
|
|
165
|
+
# Estimate the 1-in-1000-day loss
|
|
166
|
+
rl = return_level(gpd, return_period=1000)
|
|
167
|
+
print(f"1-in-1000-day loss: {rl:.4f}")
|
|
168
|
+
|
|
169
|
+
# Hill estimator for tail index
|
|
170
|
+
hill = hill_estimator(returns)
|
|
171
|
+
print(f"Tail index (alpha): {hill.tail_index:.2f}")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
[Detailed documentation: docs/evt.md](docs/evt.md)
|
|
175
|
+
|
|
176
|
+
### Fat-Tailed Distributions
|
|
177
|
+
|
|
178
|
+
Generate realistic return series from Student-t, Levy stable, regime-switching GBM, and Kou's double-exponential jump-diffusion models.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from quantlite.distributions.fat_tails import (
|
|
182
|
+
student_t_process, levy_stable_process,
|
|
183
|
+
regime_switching_gbm, kou_double_exponential_jump, RegimeParams,
|
|
184
|
+
)
|
|
185
|
+
import numpy as np
|
|
186
|
+
|
|
187
|
+
# Student-t returns (nu=4 matches typical equity behaviour)
|
|
188
|
+
t_returns = student_t_process(nu=4.0, mu=0.0003, sigma=0.012, n_steps=1260, rng_seed=42)
|
|
189
|
+
|
|
190
|
+
# Levy stable (alpha < 2 gives infinite variance)
|
|
191
|
+
stable_returns = levy_stable_process(alpha=1.7, beta=-0.1, sigma=0.008, n_steps=1260, rng_seed=42)
|
|
192
|
+
|
|
193
|
+
# Regime-switching GBM (calm and crisis regimes)
|
|
194
|
+
calm = RegimeParams(mu=0.08, sigma=0.15)
|
|
195
|
+
crisis = RegimeParams(mu=-0.20, sigma=0.40)
|
|
196
|
+
transition = np.array([[0.98, 0.02], [0.05, 0.95]])
|
|
197
|
+
prices, regimes = regime_switching_gbm(
|
|
198
|
+
[calm, crisis], transition, n_steps=2520, rng_seed=42
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Kou's double-exponential jump-diffusion
|
|
202
|
+
prices_kou = kou_double_exponential_jump(
|
|
203
|
+
S0=100, mu=0.05, sigma=0.2, lam=1.0, p=0.4,
|
|
204
|
+
eta1=10, eta2=5, n_steps=252, rng_seed=42,
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
[Detailed documentation: docs/risk.md](docs/risk.md)
|
|
209
|
+
|
|
210
|
+
### Copulas
|
|
211
|
+
|
|
212
|
+
Five copula families for modelling dependence beyond linear correlation: Gaussian, Student-t, Clayton, Gumbel, and Frank. Each provides fitting, simulation, log-likelihood, and analytical tail dependence coefficients.
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from quantlite.dependency.copulas import (
|
|
216
|
+
StudentTCopula, ClaytonCopula, select_best_copula,
|
|
217
|
+
)
|
|
218
|
+
import numpy as np
|
|
219
|
+
|
|
220
|
+
# Simulate correlated fat-tailed returns
|
|
221
|
+
rng = np.random.default_rng(42)
|
|
222
|
+
z = rng.multivariate_normal([0, 0], [[1, 0.6], [0.6, 1]], size=1000)
|
|
223
|
+
data = np.column_stack([z[:, 0] * 0.02, z[:, 1] * 0.015])
|
|
224
|
+
|
|
225
|
+
# Fit Student-t copula (captures tail dependence)
|
|
226
|
+
cop = StudentTCopula()
|
|
227
|
+
cop.fit(data)
|
|
228
|
+
td = cop.tail_dependence()
|
|
229
|
+
print(f"Student-t copula: rho={cop.rho:.3f}, nu={cop.nu:.1f}")
|
|
230
|
+
print(f"Lower tail dependence: {td['lower']:.3f}")
|
|
231
|
+
print(f"Upper tail dependence: {td['upper']:.3f}")
|
|
232
|
+
|
|
233
|
+
# Automatic model selection by AIC
|
|
234
|
+
best = select_best_copula(data)
|
|
235
|
+
print(f"Best copula: {best.name} (AIC={best.aic:.1f})")
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
[Detailed documentation: docs/copulas.md](docs/copulas.md)
|
|
239
|
+
|
|
240
|
+
### Correlation Analysis
|
|
241
|
+
|
|
242
|
+
Rolling, exponentially-weighted, stress-conditional, and rank-based correlation measures.
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
from quantlite.dependency.correlation import (
|
|
246
|
+
rolling_correlation, exponential_weighted_correlation,
|
|
247
|
+
stress_correlation, correlation_breakdown_test,
|
|
248
|
+
)
|
|
249
|
+
import pandas as pd
|
|
250
|
+
import numpy as np
|
|
251
|
+
|
|
252
|
+
# Simulate two equity return series
|
|
253
|
+
rng = np.random.default_rng(42)
|
|
254
|
+
n = 504
|
|
255
|
+
rets = pd.DataFrame({
|
|
256
|
+
"Equities": rng.normal(0.0003, 0.012, n),
|
|
257
|
+
"Bonds": rng.normal(0.0001, 0.005, n),
|
|
258
|
+
"Commodities": rng.normal(0.0002, 0.015, n),
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
# EWMA correlation (more responsive to regime changes)
|
|
262
|
+
ewma_corr = exponential_weighted_correlation(rets["Equities"], rets["Bonds"], halflife=30)
|
|
263
|
+
|
|
264
|
+
# Stress correlation (correlation during drawdowns)
|
|
265
|
+
stress_corr = stress_correlation(rets, threshold_percentile=10)
|
|
266
|
+
print("Stress-period correlation matrix:")
|
|
267
|
+
print(stress_corr)
|
|
268
|
+
|
|
269
|
+
# Test for correlation breakdown
|
|
270
|
+
test = correlation_breakdown_test(rets)
|
|
271
|
+
print(f"Calm corr: {test['calm_corr']:.3f}, Stress corr: {test['stress_corr']:.3f}")
|
|
272
|
+
print(f"p-value: {test['p_value']:.4f}")
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Hierarchical Risk Parity
|
|
276
|
+
|
|
277
|
+
Lopez de Prado's HRP method produces diversified, stable portfolio weights without covariance matrix inversion.
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from quantlite.dependency.clustering import hrp_weights
|
|
281
|
+
import pandas as pd
|
|
282
|
+
import numpy as np
|
|
283
|
+
|
|
284
|
+
rng = np.random.default_rng(42)
|
|
285
|
+
returns_df = pd.DataFrame({
|
|
286
|
+
"US_Equity": rng.normal(0.0003, 0.012, 504),
|
|
287
|
+
"EU_Equity": rng.normal(0.0002, 0.014, 504),
|
|
288
|
+
"Govt_Bonds": rng.normal(0.0001, 0.004, 504),
|
|
289
|
+
"Gold": rng.normal(0.0001, 0.010, 504),
|
|
290
|
+
"REITs": rng.normal(0.0002, 0.013, 504),
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
weights = hrp_weights(returns_df)
|
|
294
|
+
for asset, w in weights.items():
|
|
295
|
+
print(f" {asset}: {w:.2%}")
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
[Detailed documentation: docs/copulas.md](docs/copulas.md)
|
|
299
|
+
|
|
300
|
+
### Regime Detection
|
|
301
|
+
|
|
302
|
+
Hidden Markov Models and Bayesian changepoint detection for identifying structural breaks in market behaviour.
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
from quantlite.regimes.hmm import fit_regime_model, select_n_regimes
|
|
306
|
+
from quantlite.regimes.changepoint import detect_changepoints
|
|
307
|
+
from quantlite.regimes.conditional import conditional_metrics, regime_aware_var
|
|
308
|
+
from quantlite.distributions.fat_tails import student_t_process
|
|
309
|
+
|
|
310
|
+
# Generate a return series with embedded regime structure
|
|
311
|
+
returns = student_t_process(nu=4, mu=0.0003, sigma=0.015, n_steps=1260, rng_seed=42)
|
|
312
|
+
|
|
313
|
+
# Fit a 2-regime HMM (requires hmmlearn)
|
|
314
|
+
model = fit_regime_model(returns, n_regimes=2, rng_seed=42)
|
|
315
|
+
print(f"Regime means: {model.means}")
|
|
316
|
+
print(f"Regime variances: {model.variances}")
|
|
317
|
+
print(f"Stationary distribution: {model.stationary_distribution}")
|
|
318
|
+
|
|
319
|
+
# Conditional risk metrics per regime
|
|
320
|
+
cond = conditional_metrics(returns, model.regime_labels)
|
|
321
|
+
for regime, metrics in cond.items():
|
|
322
|
+
print(f"Regime {regime}: mean={metrics['mean']:.5f}, vol={metrics['volatility']:.5f}")
|
|
323
|
+
|
|
324
|
+
# Regime-aware VaR
|
|
325
|
+
rvar = regime_aware_var(returns, model.regime_labels, alpha=0.05)
|
|
326
|
+
print(f"Regime-aware VaR (95%): {rvar:.4f}")
|
|
327
|
+
|
|
328
|
+
# Bayesian changepoint detection (no hmmlearn required)
|
|
329
|
+
cps = detect_changepoints(returns, method="bayesian", penalty=50)
|
|
330
|
+
for cp in cps:
|
|
331
|
+
print(f" Changepoint at index {cp.index}, confidence={cp.confidence:.2f}, {cp.direction}")
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
[Detailed documentation: docs/regimes.md](docs/regimes.md)
|
|
335
|
+
|
|
336
|
+
### Portfolio Optimisation
|
|
337
|
+
|
|
338
|
+
Six optimisation methods: Markowitz mean-variance, minimum variance, CVaR optimisation, risk parity, HRP, and maximum Sharpe. Plus Black-Litterman and Kelly criterion.
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
from quantlite.portfolio.optimisation import (
|
|
342
|
+
mean_variance_weights, mean_cvar_weights, risk_parity_weights,
|
|
343
|
+
hrp_weights, max_sharpe_weights, black_litterman, kelly_criterion,
|
|
344
|
+
)
|
|
345
|
+
import pandas as pd
|
|
346
|
+
import numpy as np
|
|
347
|
+
|
|
348
|
+
rng = np.random.default_rng(42)
|
|
349
|
+
returns_df = pd.DataFrame({
|
|
350
|
+
"US_Equity": rng.normal(0.0004, 0.012, 504),
|
|
351
|
+
"Intl_Equity": rng.normal(0.0003, 0.014, 504),
|
|
352
|
+
"Govt_Bonds": rng.normal(0.00015, 0.004, 504),
|
|
353
|
+
"Corp_Bonds": rng.normal(0.0002, 0.006, 504),
|
|
354
|
+
"Gold": rng.normal(0.0001, 0.010, 504),
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
# CVaR-optimised portfolio (minimises expected shortfall)
|
|
358
|
+
cvar_port = mean_cvar_weights(returns_df, alpha=0.05)
|
|
359
|
+
print(f"CVaR portfolio: return={cvar_port.expected_return:.2%}, risk={cvar_port.expected_risk:.4f}")
|
|
360
|
+
for asset, w in cvar_port.weights.items():
|
|
361
|
+
print(f" {asset}: {w:.2%}")
|
|
362
|
+
|
|
363
|
+
# Risk parity
|
|
364
|
+
rp = risk_parity_weights(returns_df)
|
|
365
|
+
print(f"\nRisk parity Sharpe: {rp.sharpe:.2f}")
|
|
366
|
+
|
|
367
|
+
# Kelly criterion for position sizing
|
|
368
|
+
from quantlite.distributions.fat_tails import student_t_process
|
|
369
|
+
strat_returns = student_t_process(nu=5, mu=0.001, sigma=0.02, n_steps=252, rng_seed=42)
|
|
370
|
+
kelly_f = kelly_criterion(strat_returns)
|
|
371
|
+
print(f"Full Kelly fraction: {kelly_f:.2f}")
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
[Detailed documentation: docs/portfolio.md](docs/portfolio.md)
|
|
375
|
+
|
|
376
|
+
### Rebalancing Strategies
|
|
377
|
+
|
|
378
|
+
Calendar-based, threshold-triggered, and regime-tactical rebalancing with full turnover tracking.
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
from quantlite.portfolio.rebalancing import (
|
|
382
|
+
rebalance_calendar, rebalance_threshold, rebalance_tactical,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Monthly calendar rebalance with equal weights
|
|
386
|
+
def equal_weight(df):
|
|
387
|
+
n = df.shape[1]
|
|
388
|
+
return {col: 1.0 / n for col in df.columns}
|
|
389
|
+
|
|
390
|
+
result = rebalance_calendar(returns_df, equal_weight, freq="monthly")
|
|
391
|
+
print(f"Rebalances: {result.n_rebalances}, turnover: {result.turnover:.2f}")
|
|
392
|
+
|
|
393
|
+
# Threshold rebalance (rebalance when any weight drifts >5% from target)
|
|
394
|
+
result_thresh = rebalance_threshold(returns_df, equal_weight, threshold=0.05)
|
|
395
|
+
print(f"Threshold rebalances: {result_thresh.n_rebalances}")
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
[Detailed documentation: docs/portfolio.md](docs/portfolio.md)
|
|
399
|
+
|
|
400
|
+
### Backtesting Engine
|
|
401
|
+
|
|
402
|
+
Multi-asset production backtesting with configurable slippage, transaction costs, risk limits, and circuit breakers.
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
from quantlite.backtesting.engine import (
|
|
406
|
+
run_backtest, BacktestConfig, BacktestContext, RiskLimits, SlippageModel,
|
|
407
|
+
)
|
|
408
|
+
from quantlite.backtesting.analysis import performance_summary, trade_analysis
|
|
409
|
+
from quantlite.backtesting.signals import momentum_signal, volatility_targeting
|
|
410
|
+
from quantlite.data_generation import correlated_gbm
|
|
411
|
+
import numpy as np
|
|
412
|
+
import pandas as pd
|
|
413
|
+
|
|
414
|
+
# Generate multi-asset price data
|
|
415
|
+
cov = np.array([[0.04, 0.01, 0.005], [0.01, 0.03, 0.002], [0.005, 0.002, 0.01]])
|
|
416
|
+
prices_df = correlated_gbm(
|
|
417
|
+
S0_list=[100, 50, 200], mu_list=[0.08, 0.06, 0.04],
|
|
418
|
+
cov_matrix=cov, steps=504, rng_seed=42, return_as="dataframe",
|
|
419
|
+
)
|
|
420
|
+
prices_df.index = pd.bdate_range("2022-01-03", periods=len(prices_df))
|
|
421
|
+
prices_df.columns = ["Equities", "Bonds", "Commodities"]
|
|
422
|
+
|
|
423
|
+
# Momentum-based allocation with risk limits
|
|
424
|
+
def momentum_allocation(ctx: BacktestContext) -> dict[str, float]:
|
|
425
|
+
if len(ctx.historical_returns) < 60:
|
|
426
|
+
n = len(ctx.current_prices)
|
|
427
|
+
return {a: 1.0 / n for a in ctx.current_prices.index}
|
|
428
|
+
mom = momentum_signal(ctx.historical_returns.cumsum() + 100, lookback=60)
|
|
429
|
+
latest = mom.iloc[-1]
|
|
430
|
+
w = latest.clip(lower=0)
|
|
431
|
+
total = w.sum()
|
|
432
|
+
if total > 0:
|
|
433
|
+
w = w / total
|
|
434
|
+
return w.to_dict()
|
|
435
|
+
|
|
436
|
+
config = BacktestConfig(
|
|
437
|
+
initial_capital=1_000_000,
|
|
438
|
+
slippage_model=SlippageModel(kind="fixed", spread_bps=5),
|
|
439
|
+
risk_limits=RiskLimits(max_drawdown=-0.15, max_position_pct=0.5),
|
|
440
|
+
rebalance_freq="weekly",
|
|
441
|
+
fee_per_trade_pct=0.001,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
result = run_backtest(prices_df, momentum_allocation, config)
|
|
445
|
+
print(performance_summary(result))
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
[Detailed documentation: docs/portfolio.md](docs/portfolio.md)
|
|
449
|
+
|
|
450
|
+
### Data Generation
|
|
451
|
+
|
|
452
|
+
Stochastic process simulators: Geometric Brownian Motion, correlated multi-asset GBM, Ornstein-Uhlenbeck mean-reversion, and Merton jump-diffusion.
|
|
453
|
+
|
|
454
|
+
```python
|
|
455
|
+
from quantlite.data_generation import (
|
|
456
|
+
geometric_brownian_motion, correlated_gbm,
|
|
457
|
+
ornstein_uhlenbeck, merton_jump_diffusion,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
# Single asset with jumps
|
|
461
|
+
prices = merton_jump_diffusion(
|
|
462
|
+
S0=100, mu=0.05, sigma=0.2, lamb=0.5,
|
|
463
|
+
jump_mean=-0.02, jump_std=0.08, steps=252, rng_seed=42,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# Mean-reverting interest rate process
|
|
467
|
+
rates = ornstein_uhlenbeck(
|
|
468
|
+
x0=0.03, theta=0.5, mu=0.04, sigma=0.01, steps=252, rng_seed=42,
|
|
469
|
+
)
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Instruments
|
|
473
|
+
|
|
474
|
+
Black-Scholes option pricing with Greeks, bond pricing with duration and yield-to-maturity, and Monte Carlo pricing for exotic options (barrier, Asian).
|
|
475
|
+
|
|
476
|
+
```python
|
|
477
|
+
from quantlite.instruments.option_pricing import black_scholes_call, black_scholes_greeks
|
|
478
|
+
from quantlite.instruments.bond_pricing import bond_price, bond_duration
|
|
479
|
+
from quantlite.instruments.exotic_options import barrier_option_knock_out, asian_option_arithmetic
|
|
480
|
+
|
|
481
|
+
# European call option
|
|
482
|
+
call = black_scholes_call(S=100, K=105, T=0.5, r=0.05, sigma=0.2)
|
|
483
|
+
greeks = black_scholes_greeks(S=100, K=105, T=0.5, r=0.05, sigma=0.2)
|
|
484
|
+
print(f"Call price: {call:.2f}, Delta: {greeks.delta:.4f}, Gamma: {greeks.gamma:.4f}")
|
|
485
|
+
|
|
486
|
+
# Coupon bond
|
|
487
|
+
price = bond_price(face_value=1000, coupon_rate=0.05, market_rate=0.04, maturity=10)
|
|
488
|
+
dur = bond_duration(face_value=1000, coupon_rate=0.05, market_rate=0.04, maturity=10)
|
|
489
|
+
print(f"Bond price: {price:.2f}, Duration: {dur:.2f} years")
|
|
490
|
+
|
|
491
|
+
# Down-and-out barrier option
|
|
492
|
+
barrier_price = barrier_option_knock_out(S0=100, K=105, H=85, T=1, r=0.05, sigma=0.2, sims=50000)
|
|
493
|
+
print(f"Barrier option: {barrier_price:.2f}")
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Visualisation
|
|
497
|
+
|
|
498
|
+
Stephen Few-inspired charts with maximum data-ink ratio, muted palette, direct labels, and no chartjunk. Covers risk dashboards, copula contours, regime timelines, efficient frontiers, and correlation heatmaps.
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
from quantlite.viz.theme import apply_few_theme, FEW_PALETTE, few_figure, bullet_graph
|
|
502
|
+
from quantlite.viz.risk import plot_tail_distribution, plot_risk_dashboard, plot_drawdown
|
|
503
|
+
from quantlite.viz.dependency import plot_copula_contour, plot_correlation_matrix
|
|
504
|
+
from quantlite.viz.regimes import plot_regime_timeline, plot_regime_summary
|
|
505
|
+
from quantlite.viz.portfolio import plot_efficient_frontier, plot_weights_over_time
|
|
506
|
+
|
|
507
|
+
# Apply the Few theme globally
|
|
508
|
+
apply_few_theme()
|
|
509
|
+
|
|
510
|
+
# Single-page risk dashboard
|
|
511
|
+
fig, axes = plot_risk_dashboard(returns)
|
|
512
|
+
|
|
513
|
+
# Tail distribution with GPD overlay
|
|
514
|
+
from quantlite.risk.evt import fit_gpd
|
|
515
|
+
gpd = fit_gpd(returns)
|
|
516
|
+
fig, ax = plot_tail_distribution(returns, gpd_fit=gpd)
|
|
517
|
+
|
|
518
|
+
# Efficient frontier
|
|
519
|
+
fig, ax = plot_efficient_frontier(returns_df)
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
[Detailed documentation: docs/visualisation.md](docs/visualisation.md)
|
|
523
|
+
|
|
524
|
+
## Module Reference
|
|
525
|
+
|
|
526
|
+
| Module | Description |
|
|
527
|
+
|--------|-------------|
|
|
528
|
+
| `quantlite.risk.metrics` | VaR, CVaR, Sortino, Calmar, Omega, tail ratio, drawdowns |
|
|
529
|
+
| `quantlite.risk.evt` | GPD, GEV, Hill estimator, POT, return levels |
|
|
530
|
+
| `quantlite.distributions.fat_tails` | Student-t, Levy stable, regime-switching GBM, Kou jump-diffusion |
|
|
531
|
+
| `quantlite.dependency.copulas` | Gaussian, Student-t, Clayton, Gumbel, Frank copulas |
|
|
532
|
+
| `quantlite.dependency.correlation` | Rolling, EWMA, stress, rank correlation |
|
|
533
|
+
| `quantlite.dependency.clustering` | HRP (Hierarchical Risk Parity) |
|
|
534
|
+
| `quantlite.regimes.hmm` | Hidden Markov Model regime detection |
|
|
535
|
+
| `quantlite.regimes.changepoint` | CUSUM and Bayesian changepoint detection |
|
|
536
|
+
| `quantlite.regimes.conditional` | Regime-conditional risk metrics and VaR |
|
|
537
|
+
| `quantlite.portfolio.optimisation` | Mean-variance, CVaR, risk parity, HRP, Black-Litterman, Kelly |
|
|
538
|
+
| `quantlite.portfolio.rebalancing` | Calendar, threshold, and tactical rebalancing |
|
|
539
|
+
| `quantlite.backtesting.engine` | Multi-asset backtesting with circuit breakers |
|
|
540
|
+
| `quantlite.backtesting.signals` | Momentum, mean reversion, trend following, vol targeting |
|
|
541
|
+
| `quantlite.backtesting.analysis` | Performance summaries, monthly tables, regime attribution |
|
|
542
|
+
| `quantlite.data_generation` | GBM, correlated GBM, OU, Merton jump-diffusion |
|
|
543
|
+
| `quantlite.instruments` | Black-Scholes, bonds, barrier and Asian options |
|
|
544
|
+
| `quantlite.viz` | Stephen Few-themed risk, dependency, regime, and portfolio charts |
|
|
545
|
+
| `quantlite.metrics` | Basic annualised return, volatility, Sharpe, max drawdown |
|
|
546
|
+
| `quantlite.monte_carlo` | Monte Carlo simulation harness |
|
|
547
|
+
|
|
548
|
+
## Design Philosophy
|
|
549
|
+
|
|
550
|
+
1. **Fat tails are the default.** Gaussian assumptions are explicitly opt-in, never implicit.
|
|
551
|
+
2. **Typed return values.** Every function returns frozen dataclasses with clear attributes, not opaque dicts.
|
|
552
|
+
3. **Composable modules.** Risk metrics feed into portfolio optimisation which feeds into backtesting. Each layer works independently.
|
|
553
|
+
4. **Honest modelling.** If a method has known limitations (e.g., Gaussian copula has zero tail dependence), the docstring says so.
|
|
554
|
+
5. **Reproducible.** Every stochastic function accepts `rng_seed` for deterministic output.
|
|
555
|
+
|
|
556
|
+
## Requirements
|
|
557
|
+
|
|
558
|
+
- Python >= 3.10
|
|
559
|
+
- numpy >= 1.24
|
|
560
|
+
- pandas >= 2.0
|
|
561
|
+
- scipy >= 1.10
|
|
562
|
+
- matplotlib >= 3.7
|
|
563
|
+
- mplfinance
|
|
564
|
+
|
|
565
|
+
Optional: `hmmlearn` for HMM regime detection.
|
|
566
|
+
|
|
567
|
+
## Contributing
|
|
568
|
+
|
|
569
|
+
Contributions are welcome. Please ensure:
|
|
570
|
+
|
|
571
|
+
1. All new functions have type hints and docstrings
|
|
572
|
+
2. Tests pass: `pytest`
|
|
573
|
+
3. Code is formatted: `ruff check` and `ruff format`
|
|
574
|
+
4. British spellings in all documentation
|
|
575
|
+
|
|
576
|
+
## License
|
|
577
|
+
|
|
578
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
579
|
+
|
|
580
|
+
## Links
|
|
581
|
+
|
|
582
|
+
- [PyPI](https://pypi.org/project/quantlite/)
|
|
583
|
+
- [GitHub](https://github.com/prasants/QuantLite)
|
|
584
|
+
- [Issue Tracker](https://github.com/prasants/QuantLite/issues)
|