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.
Files changed (72) hide show
  1. quantlite-0.2.0/LICENSE +22 -0
  2. quantlite-0.2.0/PKG-INFO +584 -0
  3. quantlite-0.2.0/README.md +525 -0
  4. quantlite-0.2.0/pyproject.toml +68 -0
  5. quantlite-0.2.0/setup.cfg +4 -0
  6. quantlite-0.2.0/src/quantlite/__init__.py +60 -0
  7. quantlite-0.2.0/src/quantlite/backtesting/__init__.py +43 -0
  8. quantlite-0.2.0/src/quantlite/backtesting/analysis.py +226 -0
  9. quantlite-0.2.0/src/quantlite/backtesting/engine.py +407 -0
  10. quantlite-0.2.0/src/quantlite/backtesting/legacy.py +101 -0
  11. quantlite-0.2.0/src/quantlite/backtesting/signals.py +182 -0
  12. quantlite-0.2.0/src/quantlite/core/__init__.py +1 -0
  13. quantlite-0.2.0/src/quantlite/core/types.py +152 -0
  14. quantlite-0.2.0/src/quantlite/data_generation.py +228 -0
  15. quantlite-0.2.0/src/quantlite/dependency/__init__.py +1 -0
  16. quantlite-0.2.0/src/quantlite/dependency/clustering.py +177 -0
  17. quantlite-0.2.0/src/quantlite/dependency/copulas.py +663 -0
  18. quantlite-0.2.0/src/quantlite/dependency/correlation.py +192 -0
  19. quantlite-0.2.0/src/quantlite/distributions/__init__.py +1 -0
  20. quantlite-0.2.0/src/quantlite/distributions/fat_tails.py +239 -0
  21. quantlite-0.2.0/src/quantlite/instruments/__init__.py +1 -0
  22. quantlite-0.2.0/src/quantlite/instruments/bond_pricing.py +108 -0
  23. quantlite-0.2.0/src/quantlite/instruments/exotic_options.py +121 -0
  24. quantlite-0.2.0/src/quantlite/instruments/option_pricing.py +104 -0
  25. quantlite-0.2.0/src/quantlite/metrics.py +86 -0
  26. quantlite-0.2.0/src/quantlite/monte_carlo.py +87 -0
  27. quantlite-0.2.0/src/quantlite/portfolio/__init__.py +35 -0
  28. quantlite-0.2.0/src/quantlite/portfolio/optimisation.py +506 -0
  29. quantlite-0.2.0/src/quantlite/portfolio/rebalancing.py +249 -0
  30. quantlite-0.2.0/src/quantlite/regimes/__init__.py +1 -0
  31. quantlite-0.2.0/src/quantlite/regimes/changepoint.py +249 -0
  32. quantlite-0.2.0/src/quantlite/regimes/conditional.py +171 -0
  33. quantlite-0.2.0/src/quantlite/regimes/hmm.py +224 -0
  34. quantlite-0.2.0/src/quantlite/risk/__init__.py +1 -0
  35. quantlite-0.2.0/src/quantlite/risk/evt.py +261 -0
  36. quantlite-0.2.0/src/quantlite/risk/metrics.py +262 -0
  37. quantlite-0.2.0/src/quantlite/visualisation.py +202 -0
  38. quantlite-0.2.0/src/quantlite/viz/__init__.py +1 -0
  39. quantlite-0.2.0/src/quantlite/viz/dependency.py +300 -0
  40. quantlite-0.2.0/src/quantlite/viz/portfolio.py +413 -0
  41. quantlite-0.2.0/src/quantlite/viz/regimes.py +304 -0
  42. quantlite-0.2.0/src/quantlite/viz/risk.py +284 -0
  43. quantlite-0.2.0/src/quantlite/viz/theme.py +222 -0
  44. quantlite-0.2.0/src/quantlite.egg-info/PKG-INFO +584 -0
  45. quantlite-0.2.0/src/quantlite.egg-info/SOURCES.txt +70 -0
  46. quantlite-0.2.0/src/quantlite.egg-info/dependency_links.txt +1 -0
  47. quantlite-0.2.0/src/quantlite.egg-info/requires.txt +11 -0
  48. quantlite-0.2.0/src/quantlite.egg-info/top_level.txt +1 -0
  49. quantlite-0.2.0/tests/test_analysis.py +113 -0
  50. quantlite-0.2.0/tests/test_backtesting.py +33 -0
  51. quantlite-0.2.0/tests/test_changepoint.py +54 -0
  52. quantlite-0.2.0/tests/test_clustering.py +67 -0
  53. quantlite-0.2.0/tests/test_conditional.py +74 -0
  54. quantlite-0.2.0/tests/test_copulas.py +135 -0
  55. quantlite-0.2.0/tests/test_correlation.py +103 -0
  56. quantlite-0.2.0/tests/test_data_generation.py +43 -0
  57. quantlite-0.2.0/tests/test_engine.py +113 -0
  58. quantlite-0.2.0/tests/test_evt.py +116 -0
  59. quantlite-0.2.0/tests/test_fat_tails.py +118 -0
  60. quantlite-0.2.0/tests/test_hmm.py +86 -0
  61. quantlite-0.2.0/tests/test_instruments.py +56 -0
  62. quantlite-0.2.0/tests/test_metrics.py +32 -0
  63. quantlite-0.2.0/tests/test_monte_carlo.py +20 -0
  64. quantlite-0.2.0/tests/test_optimisation.py +141 -0
  65. quantlite-0.2.0/tests/test_rebalancing.py +78 -0
  66. quantlite-0.2.0/tests/test_risk_metrics.py +127 -0
  67. quantlite-0.2.0/tests/test_signals.py +93 -0
  68. quantlite-0.2.0/tests/test_visualisation.py +44 -0
  69. quantlite-0.2.0/tests/test_viz.py +105 -0
  70. quantlite-0.2.0/tests/test_viz_dependency.py +67 -0
  71. quantlite-0.2.0/tests/test_viz_portfolio.py +83 -0
  72. quantlite-0.2.0/tests/test_viz_regimes.py +53 -0
@@ -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
+
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/quantlite)](https://pypi.org/project/quantlite/)
65
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
66
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](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)