quantlite 0.9.0__tar.gz → 1.0.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.9.0/src/quantlite.egg-info → quantlite-1.0.0}/PKG-INFO +57 -1
- {quantlite-0.9.0 → quantlite-1.0.0}/README.md +56 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/pyproject.toml +1 -1
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/__init__.py +17 -1
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/antifragile/__init__.py +42 -22
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/ergodicity/__init__.py +30 -16
- quantlite-1.0.0/src/quantlite/pipeline.py +338 -0
- quantlite-1.0.0/src/quantlite/regime_integration/__init__.py +31 -0
- quantlite-1.0.0/src/quantlite/regime_integration/portfolio.py +267 -0
- quantlite-1.0.0/src/quantlite/regime_integration/reporting.py +204 -0
- quantlite-1.0.0/src/quantlite/regime_integration/risk.py +212 -0
- {quantlite-0.9.0 → quantlite-1.0.0/src/quantlite.egg-info}/PKG-INFO +57 -1
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite.egg-info/SOURCES.txt +7 -0
- quantlite-1.0.0/tests/test_pipeline.py +165 -0
- quantlite-1.0.0/tests/test_regime_integration.py +287 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/LICENSE +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/setup.cfg +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/backtesting/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/backtesting/analysis.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/backtesting/engine.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/backtesting/legacy.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/backtesting/signals.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/contagion/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/core/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/core/types.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/crypto/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/crypto/exchange.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/crypto/onchain.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/crypto/stablecoin.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/base.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/cache.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/crypto.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/fred.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/local.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/registry.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data/yahoo.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/data_generation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/dependency/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/dependency/clustering.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/dependency/copulas.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/dependency/correlation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/distributions/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/distributions/fat_tails.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/diversification/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/factors/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/factors/classical.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/factors/custom.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/factors/tail_risk.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/forensics/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/instruments/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/instruments/bond_pricing.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/instruments/exotic_options.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/instruments/option_pricing.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/metrics.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/monte_carlo.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/network/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/overfit/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/portfolio/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/portfolio/optimisation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/portfolio/rebalancing.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/regimes/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/regimes/changepoint.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/regimes/conditional.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/regimes/hmm.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/report/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/report/html_renderer.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/report/pdf_renderer.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/report/sections.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/report/tearsheet.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/resample/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/risk/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/risk/evt.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/risk/metrics.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/scenarios/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/simulation/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/simulation/copula_mc.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/simulation/evt_simulation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/simulation/regime_mc.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/visualisation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/dependency.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/plotly_backend/__init__.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/plotly_backend/dependency.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/plotly_backend/portfolio.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/plotly_backend/regimes.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/plotly_backend/risk.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/plotly_backend/theme.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/portfolio.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/regimes.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/risk.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite/viz/theme.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite.egg-info/dependency_links.txt +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite.egg-info/requires.txt +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/src/quantlite.egg-info/top_level.txt +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_analysis.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_antifragile.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_backtesting.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_changepoint.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_clustering.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_conditional.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_contagion.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_copulas.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_correlation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_crypto_exchange.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_crypto_onchain.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_crypto_stablecoin.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_data_connectors.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_data_generation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_diversification.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_engine.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_ergodicity.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_evt.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_factors_classical.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_factors_custom.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_factors_tail_risk.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_fat_tails.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_forensics.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_hmm.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_instruments.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_metrics.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_monte_carlo.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_network.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_optimisation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_overfit.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_plotly_viz.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_rebalancing.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_report.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_resample.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_risk_metrics.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_scenarios.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_signals.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_sim_copula.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_sim_evt.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_sim_regime.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_visualisation.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_viz.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_viz_dependency.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_viz_portfolio.py +0 -0
- {quantlite-0.9.0 → quantlite-1.0.0}/tests/test_viz_regimes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantlite
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: A fat-tail-native quantitative finance toolkit: EVT, risk metrics, and honest modelling for markets that bite.
|
|
5
5
|
Author-email: Prasant Sudhakaran <code@prasant.net>
|
|
6
6
|
License: MIT License
|
|
@@ -1096,6 +1096,62 @@ print(f"Tail diversification: {td['tail_diversification']:.3f}")
|
|
|
1096
1096
|
| `quantlite.network` | Correlation networks, eigenvector centrality, cascade simulation, community detection |
|
|
1097
1097
|
| `quantlite.diversification` | Effective Number of Bets, entropy diversification, tail diversification, diversification ratio, Herfindahl index |
|
|
1098
1098
|
|
|
1099
|
+
## v1.0: The Dream API
|
|
1100
|
+
|
|
1101
|
+
QuantLite v1.0 introduces the **Dream API**, a five-function pipeline that chains the entire quant workflow:
|
|
1102
|
+
|
|
1103
|
+
```python
|
|
1104
|
+
import quantlite as ql
|
|
1105
|
+
|
|
1106
|
+
data = ql.fetch(["AAPL", "BTC-USD", "GLD", "TLT"], period="5y")
|
|
1107
|
+
regimes = ql.detect_regimes(data, n_regimes=3)
|
|
1108
|
+
weights = ql.construct_portfolio(data, regime_aware=True, regimes=regimes)
|
|
1109
|
+
result = ql.backtest(data, weights)
|
|
1110
|
+
ql.tearsheet(result, regimes=regimes, save="portfolio.txt")
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
### Regime-Aware Portfolio Construction
|
|
1114
|
+
|
|
1115
|
+
Weights automatically tilt defensive during crisis regimes, increasing allocations to bonds and gold while reducing equity exposure:
|
|
1116
|
+
|
|
1117
|
+

|
|
1118
|
+
|
|
1119
|
+
### Regime Risk Analysis
|
|
1120
|
+
|
|
1121
|
+
VaR, CVaR, volatility, skewness, and kurtosis computed separately for each market regime:
|
|
1122
|
+
|
|
1123
|
+

|
|
1124
|
+
|
|
1125
|
+
### Regime-Filtered Backtesting
|
|
1126
|
+
|
|
1127
|
+
Different weight sets applied per regime, with full performance attribution:
|
|
1128
|
+
|
|
1129
|
+

|
|
1130
|
+
|
|
1131
|
+
### v1.0 Module Reference
|
|
1132
|
+
|
|
1133
|
+
| Module | Description |
|
|
1134
|
+
|--------|-------------|
|
|
1135
|
+
| `quantlite.pipeline` | Dream API: `fetch`, `detect_regimes`, `construct_portfolio`, `backtest`, `tearsheet` |
|
|
1136
|
+
| `quantlite.regime_integration` | Regime-conditional risk, defensive portfolio tilting, filtered backtesting, tearsheets |
|
|
1137
|
+
| `quantlite.regimes` | HMM regime detection, Bayesian changepoint detection, conditional metrics |
|
|
1138
|
+
| `quantlite.portfolio` | Markowitz, CVaR, risk parity, HRP, Black-Litterman, Kelly optimisation |
|
|
1139
|
+
| `quantlite.backtesting` | Multi-asset engine with circuit breakers, slippage, regime-aware signals |
|
|
1140
|
+
| `quantlite.risk` | VaR, CVaR, Sortino, Calmar, omega ratio, tail ratio, drawdown analysis |
|
|
1141
|
+
| `quantlite.data` | Unified fetching: Yahoo Finance, CCXT, FRED, local files |
|
|
1142
|
+
| `quantlite.distributions` | Student-t, stable, GPD, GEV fitting and simulation |
|
|
1143
|
+
| `quantlite.simulation` | Fat-tail Monte Carlo: EVT-based, copula-based engines |
|
|
1144
|
+
| `quantlite.viz` | Stephen Few-inspired charts: regimes, portfolios, risk dashboards |
|
|
1145
|
+
| `quantlite.factors` | Classical factors, custom factors, tail risk factors |
|
|
1146
|
+
| `quantlite.ergodicity` | Time-average vs ensemble-average growth, Kelly sizing |
|
|
1147
|
+
| `quantlite.antifragile` | Barbell metrics, convexity scores, stress testing |
|
|
1148
|
+
| `quantlite.scenarios` | Historical, hypothetical, and Monte Carlo scenario analysis |
|
|
1149
|
+
| `quantlite.forensics` | Overfitting detection, data snooping tests, walk-forward analysis |
|
|
1150
|
+
| `quantlite.contagion` | CoVaR, Delta CoVaR, MES, Granger causality |
|
|
1151
|
+
| `quantlite.network` | Correlation networks, centrality, cascade simulation |
|
|
1152
|
+
| `quantlite.diversification` | Effective Number of Bets, entropy, tail diversification |
|
|
1153
|
+
| `quantlite.crypto` | On-chain risk, stablecoin depeg, exchange risk scoring |
|
|
1154
|
+
|
|
1099
1155
|
## Design Philosophy
|
|
1100
1156
|
|
|
1101
1157
|
1. **Fat tails are the default.** Gaussian assumptions are explicitly opt-in, never implicit.
|
|
@@ -1015,6 +1015,62 @@ print(f"Tail diversification: {td['tail_diversification']:.3f}")
|
|
|
1015
1015
|
| `quantlite.network` | Correlation networks, eigenvector centrality, cascade simulation, community detection |
|
|
1016
1016
|
| `quantlite.diversification` | Effective Number of Bets, entropy diversification, tail diversification, diversification ratio, Herfindahl index |
|
|
1017
1017
|
|
|
1018
|
+
## v1.0: The Dream API
|
|
1019
|
+
|
|
1020
|
+
QuantLite v1.0 introduces the **Dream API**, a five-function pipeline that chains the entire quant workflow:
|
|
1021
|
+
|
|
1022
|
+
```python
|
|
1023
|
+
import quantlite as ql
|
|
1024
|
+
|
|
1025
|
+
data = ql.fetch(["AAPL", "BTC-USD", "GLD", "TLT"], period="5y")
|
|
1026
|
+
regimes = ql.detect_regimes(data, n_regimes=3)
|
|
1027
|
+
weights = ql.construct_portfolio(data, regime_aware=True, regimes=regimes)
|
|
1028
|
+
result = ql.backtest(data, weights)
|
|
1029
|
+
ql.tearsheet(result, regimes=regimes, save="portfolio.txt")
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
### Regime-Aware Portfolio Construction
|
|
1033
|
+
|
|
1034
|
+
Weights automatically tilt defensive during crisis regimes, increasing allocations to bonds and gold while reducing equity exposure:
|
|
1035
|
+
|
|
1036
|
+

|
|
1037
|
+
|
|
1038
|
+
### Regime Risk Analysis
|
|
1039
|
+
|
|
1040
|
+
VaR, CVaR, volatility, skewness, and kurtosis computed separately for each market regime:
|
|
1041
|
+
|
|
1042
|
+

|
|
1043
|
+
|
|
1044
|
+
### Regime-Filtered Backtesting
|
|
1045
|
+
|
|
1046
|
+
Different weight sets applied per regime, with full performance attribution:
|
|
1047
|
+
|
|
1048
|
+

|
|
1049
|
+
|
|
1050
|
+
### v1.0 Module Reference
|
|
1051
|
+
|
|
1052
|
+
| Module | Description |
|
|
1053
|
+
|--------|-------------|
|
|
1054
|
+
| `quantlite.pipeline` | Dream API: `fetch`, `detect_regimes`, `construct_portfolio`, `backtest`, `tearsheet` |
|
|
1055
|
+
| `quantlite.regime_integration` | Regime-conditional risk, defensive portfolio tilting, filtered backtesting, tearsheets |
|
|
1056
|
+
| `quantlite.regimes` | HMM regime detection, Bayesian changepoint detection, conditional metrics |
|
|
1057
|
+
| `quantlite.portfolio` | Markowitz, CVaR, risk parity, HRP, Black-Litterman, Kelly optimisation |
|
|
1058
|
+
| `quantlite.backtesting` | Multi-asset engine with circuit breakers, slippage, regime-aware signals |
|
|
1059
|
+
| `quantlite.risk` | VaR, CVaR, Sortino, Calmar, omega ratio, tail ratio, drawdown analysis |
|
|
1060
|
+
| `quantlite.data` | Unified fetching: Yahoo Finance, CCXT, FRED, local files |
|
|
1061
|
+
| `quantlite.distributions` | Student-t, stable, GPD, GEV fitting and simulation |
|
|
1062
|
+
| `quantlite.simulation` | Fat-tail Monte Carlo: EVT-based, copula-based engines |
|
|
1063
|
+
| `quantlite.viz` | Stephen Few-inspired charts: regimes, portfolios, risk dashboards |
|
|
1064
|
+
| `quantlite.factors` | Classical factors, custom factors, tail risk factors |
|
|
1065
|
+
| `quantlite.ergodicity` | Time-average vs ensemble-average growth, Kelly sizing |
|
|
1066
|
+
| `quantlite.antifragile` | Barbell metrics, convexity scores, stress testing |
|
|
1067
|
+
| `quantlite.scenarios` | Historical, hypothetical, and Monte Carlo scenario analysis |
|
|
1068
|
+
| `quantlite.forensics` | Overfitting detection, data snooping tests, walk-forward analysis |
|
|
1069
|
+
| `quantlite.contagion` | CoVaR, Delta CoVaR, MES, Granger causality |
|
|
1070
|
+
| `quantlite.network` | Correlation networks, centrality, cascade simulation |
|
|
1071
|
+
| `quantlite.diversification` | Effective Number of Bets, entropy, tail diversification |
|
|
1072
|
+
| `quantlite.crypto` | On-chain risk, stablecoin depeg, exchange risk scoring |
|
|
1073
|
+
|
|
1018
1074
|
## Design Philosophy
|
|
1019
1075
|
|
|
1020
1076
|
1. **Fat tails are the default.** Gaussian assumptions are explicitly opt-in, never implicit.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quantlite"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "1.0.0"
|
|
8
8
|
description = "A fat-tail-native quantitative finance toolkit: EVT, risk metrics, and honest modelling for markets that bite."
|
|
9
9
|
requires-python = ">=3.9"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -6,7 +6,7 @@ portfolio optimisation, multi-asset backtesting, and
|
|
|
6
6
|
Stephen Few-inspired visualisation.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "0.
|
|
9
|
+
__version__ = "1.0.0"
|
|
10
10
|
|
|
11
11
|
from .backtesting import (
|
|
12
12
|
BacktestConfig,
|
|
@@ -79,6 +79,14 @@ __all__ = [
|
|
|
79
79
|
"crypto",
|
|
80
80
|
# Fat-tail Monte Carlo simulation
|
|
81
81
|
"simulation",
|
|
82
|
+
# Regime-aware integration
|
|
83
|
+
"regime_integration",
|
|
84
|
+
# Dream API (pipeline)
|
|
85
|
+
"fetch",
|
|
86
|
+
"detect_regimes",
|
|
87
|
+
"construct_portfolio",
|
|
88
|
+
"backtest",
|
|
89
|
+
"tearsheet",
|
|
82
90
|
]
|
|
83
91
|
|
|
84
92
|
from . import ( # noqa: E402
|
|
@@ -90,7 +98,15 @@ from . import ( # noqa: E402
|
|
|
90
98
|
forensics,
|
|
91
99
|
network,
|
|
92
100
|
overfit,
|
|
101
|
+
regime_integration,
|
|
93
102
|
resample,
|
|
94
103
|
scenarios,
|
|
95
104
|
simulation,
|
|
96
105
|
)
|
|
106
|
+
from .pipeline import ( # noqa: E402
|
|
107
|
+
backtest,
|
|
108
|
+
construct_portfolio,
|
|
109
|
+
detect_regimes,
|
|
110
|
+
tearsheet,
|
|
111
|
+
)
|
|
112
|
+
from .pipeline import fetch as fetch # noqa: E402
|
|
@@ -218,44 +218,40 @@ def barbell_allocation(
|
|
|
218
218
|
def lindy_estimate(age: float, confidence: float = 0.95) -> dict[str, float]:
|
|
219
219
|
"""Estimate remaining life expectancy using the Lindy effect.
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
Models non-perishable entities (ideas, technologies, institutions)
|
|
222
|
+
with a Pareto survival distribution (alpha = 1). Under this model:
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
P(T > age + t | T > age) = age / (age + t)
|
|
225
|
+
|
|
226
|
+
Key properties:
|
|
227
|
+
|
|
228
|
+
* **Expected remaining life** = age (the longer it has survived,
|
|
229
|
+
the longer we expect it to last).
|
|
230
|
+
* **Lower bound at confidence level c**: the additional time *t*
|
|
231
|
+
such that we are *c*-confident the entity survives at least *t*
|
|
232
|
+
more units. Solve ``age / (age + t) = 1 - c`` to get
|
|
233
|
+
``t = age * c / (1 - c)``.
|
|
226
234
|
|
|
227
235
|
Parameters
|
|
228
236
|
----------
|
|
229
237
|
age : float
|
|
230
238
|
Current age of the entity (in any consistent unit).
|
|
231
239
|
confidence : float
|
|
232
|
-
Confidence level for the survival bound (default 0.95).
|
|
240
|
+
Confidence level for the survival lower bound (default 0.95).
|
|
233
241
|
|
|
234
242
|
Returns
|
|
235
243
|
-------
|
|
236
244
|
dict
|
|
237
|
-
Keys: 'age', 'expected_remaining' (
|
|
238
|
-
|
|
245
|
+
Keys: 'age', 'expected_remaining', 'lower_bound' (at the
|
|
246
|
+
given confidence), 'total_expected'.
|
|
239
247
|
"""
|
|
240
248
|
if age <= 0:
|
|
241
249
|
raise ValueError("age must be positive")
|
|
242
250
|
if not 0 < confidence < 1:
|
|
243
251
|
raise ValueError("confidence must be between 0 and 1")
|
|
244
252
|
|
|
245
|
-
# Under Lindy (Pareto with alpha=1), expected remaining life = age
|
|
246
253
|
expected_remaining = age
|
|
247
|
-
|
|
248
|
-
# Lower bound: at confidence level, survival beyond this point
|
|
249
|
-
# P(survive t more) = age / (age + t), so t = age * (1/p - 1)
|
|
250
|
-
lower_bound = age * (1.0 / (1.0 - confidence) - 1.0) * (1.0 - confidence)
|
|
251
|
-
# Simplifies to: age * confidence / (1 - confidence) * (1 - confidence) = age * confidence
|
|
252
|
-
# Actually: P(T > age + t | T > age) = age / (age + t) for Pareto
|
|
253
|
-
# Set this = 1 - confidence: age/(age+t) = 1 - confidence
|
|
254
|
-
# t = age * confidence / (1 - confidence)
|
|
255
|
-
lower_bound = age * (1.0 - confidence) / confidence
|
|
256
|
-
# That's the point we're confident we'll reach (small value)
|
|
257
|
-
# More useful: expected remaining at median
|
|
258
|
-
# P(T > age + t | T > age) = 0.5 => t = age (median remaining = age)
|
|
254
|
+
lower_bound = age * confidence / (1.0 - confidence)
|
|
259
255
|
|
|
260
256
|
return {
|
|
261
257
|
"age": age,
|
|
@@ -268,11 +264,25 @@ def lindy_estimate(age: float, confidence: float = 0.95) -> dict[str, float]:
|
|
|
268
264
|
def skin_in_game_score(
|
|
269
265
|
manager_returns: ArrayLike,
|
|
270
266
|
fund_returns: ArrayLike,
|
|
267
|
+
alignment_weight: float = 0.4,
|
|
268
|
+
downside_weight: float = 0.4,
|
|
269
|
+
asymmetry_weight: float = 0.2,
|
|
271
270
|
) -> dict[str, float]:
|
|
272
271
|
"""Measure principal-agent alignment via payoff asymmetry.
|
|
273
272
|
|
|
274
273
|
Compares the manager's exposure to downside vs upside relative
|
|
275
|
-
to the fund.
|
|
274
|
+
to the fund. A good score means the manager shares the pain.
|
|
275
|
+
|
|
276
|
+
The composite score weights three components:
|
|
277
|
+
|
|
278
|
+
* **Alignment** (default 0.4): correlation between manager and fund
|
|
279
|
+
returns. Are incentives actually correlated?
|
|
280
|
+
* **Downside sharing** (default 0.4): when the fund loses, does the
|
|
281
|
+
manager bleed proportionally? This matters as much as alignment —
|
|
282
|
+
asymmetric downside is the hallmark of agency problems.
|
|
283
|
+
* **Upside asymmetry** (default 0.2): does the manager capture
|
|
284
|
+
disproportionate upside? A secondary check — some asymmetry is
|
|
285
|
+
expected (performance fees), but extreme values signal misalignment.
|
|
276
286
|
|
|
277
287
|
Parameters
|
|
278
288
|
----------
|
|
@@ -280,6 +290,12 @@ def skin_in_game_score(
|
|
|
280
290
|
Returns experienced by the manager (compensation-adjusted).
|
|
281
291
|
fund_returns : array-like
|
|
282
292
|
Returns experienced by the fund investors.
|
|
293
|
+
alignment_weight : float
|
|
294
|
+
Weight for the alignment (correlation) component (default 0.4).
|
|
295
|
+
downside_weight : float
|
|
296
|
+
Weight for the downside-sharing component (default 0.4).
|
|
297
|
+
asymmetry_weight : float
|
|
298
|
+
Weight for the upside-asymmetry component (default 0.2).
|
|
283
299
|
|
|
284
300
|
Returns
|
|
285
301
|
-------
|
|
@@ -317,7 +333,11 @@ def skin_in_game_score(
|
|
|
317
333
|
|
|
318
334
|
# Composite score: high alignment + high downside sharing + low upside asymmetry = good
|
|
319
335
|
# Normalise to [0, 1] approximately
|
|
320
|
-
score = (
|
|
336
|
+
score = (
|
|
337
|
+
alignment * alignment_weight
|
|
338
|
+
+ min(downside_sharing, 1.0) * downside_weight
|
|
339
|
+
+ max(0, 1.0 - abs(upside_asymmetry - 1.0)) * asymmetry_weight
|
|
340
|
+
)
|
|
321
341
|
|
|
322
342
|
return {
|
|
323
343
|
"alignment": alignment,
|
|
@@ -91,11 +91,13 @@ def ergodicity_gap(returns: ArrayLike) -> float:
|
|
|
91
91
|
def kelly_fraction(returns: ArrayLike, risk_free: float = 0.0) -> float:
|
|
92
92
|
"""Compute the optimal Kelly fraction for geometric growth.
|
|
93
93
|
|
|
94
|
-
The Kelly criterion maximises the expected logarithmic growth rate
|
|
95
|
-
For a simple binary-style approximation from empirical returns,
|
|
96
|
-
we optimise f to maximise E[log(1 + f * (r - risk_free))].
|
|
94
|
+
The Kelly criterion maximises the expected logarithmic growth rate:
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
f* = argmax_f E[log(1 + f * (r - r_f))]
|
|
97
|
+
|
|
98
|
+
Uses ``scipy.optimize.minimize_scalar`` with bounded search on
|
|
99
|
+
[-0.5, 3.0]. Falls back to Brent-bounded optimisation if the
|
|
100
|
+
primary solve fails.
|
|
99
101
|
|
|
100
102
|
Parameters
|
|
101
103
|
----------
|
|
@@ -110,24 +112,36 @@ def kelly_fraction(returns: ArrayLike, risk_free: float = 0.0) -> float:
|
|
|
110
112
|
Optimal fraction of capital to deploy. Can be < 0 (short)
|
|
111
113
|
or > 1 (leveraged).
|
|
112
114
|
"""
|
|
115
|
+
from scipy.optimize import minimize_scalar
|
|
116
|
+
|
|
113
117
|
r = _to_array(returns)
|
|
114
118
|
excess = r - risk_free
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
fractions = np.linspace(-0.5, 3.0, 3500)
|
|
118
|
-
best_f = 0.0
|
|
119
|
-
best_g = -np.inf
|
|
120
|
-
|
|
121
|
-
for f in fractions:
|
|
120
|
+
def neg_expected_log_growth(f: float) -> float:
|
|
122
121
|
portfolio = 1.0 + f * excess
|
|
123
122
|
if np.any(portfolio <= 0):
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
return 1e10 # infeasible
|
|
124
|
+
return -float(np.mean(np.log(portfolio)))
|
|
125
|
+
|
|
126
|
+
bounds = (-0.5, 3.0)
|
|
127
|
+
|
|
128
|
+
result = minimize_scalar(
|
|
129
|
+
neg_expected_log_growth, bounds=bounds, method="bounded",
|
|
130
|
+
options={"xatol": 1e-8, "maxiter": 500},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if result.success:
|
|
134
|
+
return float(round(result.x, 4))
|
|
135
|
+
|
|
136
|
+
# Fallback: try again with Brent in the same interval
|
|
137
|
+
result2 = minimize_scalar(
|
|
138
|
+
neg_expected_log_growth, bracket=(-0.5, 0.5, 3.0), method="brent",
|
|
139
|
+
)
|
|
140
|
+
if result2.success and bounds[0] <= result2.x <= bounds[1]:
|
|
141
|
+
return float(round(result2.x, 4))
|
|
129
142
|
|
|
130
|
-
return
|
|
143
|
+
# Last resort: return 0 (no bet)
|
|
144
|
+
return 0.0
|
|
131
145
|
|
|
132
146
|
|
|
133
147
|
def leverage_effect(
|