quantlite 0.7.0__tar.gz → 0.9.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.7.0/src/quantlite.egg-info → quantlite-0.9.0}/PKG-INFO +162 -1
- {quantlite-0.7.0 → quantlite-0.9.0}/README.md +161 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/pyproject.toml +4 -1
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/__init__.py +3 -0
- quantlite-0.9.0/src/quantlite/factors/__init__.py +43 -0
- quantlite-0.9.0/src/quantlite/factors/classical.py +288 -0
- quantlite-0.9.0/src/quantlite/factors/custom.py +340 -0
- quantlite-0.9.0/src/quantlite/factors/tail_risk.py +296 -0
- quantlite-0.9.0/src/quantlite/simulation/__init__.py +45 -0
- quantlite-0.9.0/src/quantlite/simulation/copula_mc.py +238 -0
- quantlite-0.9.0/src/quantlite/simulation/evt_simulation.py +304 -0
- quantlite-0.9.0/src/quantlite/simulation/regime_mc.py +328 -0
- {quantlite-0.7.0 → quantlite-0.9.0/src/quantlite.egg-info}/PKG-INFO +162 -1
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite.egg-info/SOURCES.txt +14 -0
- quantlite-0.9.0/tests/test_factors_classical.py +208 -0
- quantlite-0.9.0/tests/test_factors_custom.py +205 -0
- quantlite-0.9.0/tests/test_factors_tail_risk.py +224 -0
- quantlite-0.9.0/tests/test_sim_copula.py +159 -0
- quantlite-0.9.0/tests/test_sim_evt.py +164 -0
- quantlite-0.9.0/tests/test_sim_regime.py +211 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/LICENSE +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/setup.cfg +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/antifragile/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/backtesting/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/backtesting/analysis.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/backtesting/engine.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/backtesting/legacy.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/backtesting/signals.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/contagion/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/core/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/core/types.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/crypto/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/crypto/exchange.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/crypto/onchain.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/crypto/stablecoin.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/base.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/cache.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/crypto.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/fred.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/local.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/registry.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data/yahoo.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/data_generation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/dependency/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/dependency/clustering.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/dependency/copulas.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/dependency/correlation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/distributions/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/distributions/fat_tails.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/diversification/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/ergodicity/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/forensics/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/instruments/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/instruments/bond_pricing.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/instruments/exotic_options.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/instruments/option_pricing.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/metrics.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/monte_carlo.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/network/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/overfit/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/portfolio/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/portfolio/optimisation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/portfolio/rebalancing.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/regimes/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/regimes/changepoint.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/regimes/conditional.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/regimes/hmm.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/report/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/report/html_renderer.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/report/pdf_renderer.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/report/sections.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/report/tearsheet.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/resample/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/risk/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/risk/evt.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/risk/metrics.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/scenarios/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/visualisation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/dependency.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/plotly_backend/__init__.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/plotly_backend/dependency.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/plotly_backend/portfolio.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/plotly_backend/regimes.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/plotly_backend/risk.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/plotly_backend/theme.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/portfolio.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/regimes.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/risk.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite/viz/theme.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite.egg-info/dependency_links.txt +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite.egg-info/requires.txt +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/src/quantlite.egg-info/top_level.txt +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_analysis.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_antifragile.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_backtesting.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_changepoint.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_clustering.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_conditional.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_contagion.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_copulas.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_correlation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_crypto_exchange.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_crypto_onchain.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_crypto_stablecoin.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_data_connectors.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_data_generation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_diversification.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_engine.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_ergodicity.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_evt.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_fat_tails.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_forensics.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_hmm.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_instruments.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_metrics.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_monte_carlo.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_network.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_optimisation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_overfit.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_plotly_viz.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_rebalancing.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_report.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_resample.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_risk_metrics.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_scenarios.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_signals.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_visualisation.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_viz.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_viz_dependency.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.0}/tests/test_viz_portfolio.py +0 -0
- {quantlite-0.7.0 → quantlite-0.9.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: 0.9.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
|
|
@@ -713,6 +713,167 @@ Same Stephen Few theme, same muted palette, but with hover info, zoom, and nativ
|
|
|
713
713
|
| `quantlite.crypto.stablecoin` | Depeg probability, peg deviation tracking, recovery time, reserve risk scoring |
|
|
714
714
|
| `quantlite.crypto.exchange` | Exchange concentration (HHI), wallet risk, proof of reserves, liquidity risk, slippage |
|
|
715
715
|
| `quantlite.crypto.onchain` | Wallet exposure, TVL tracking, DeFi dependency graphs, smart contract risk scoring |
|
|
716
|
+
| `quantlite.factors.classical` | Fama-French three/five-factor, Carhart four-factor, factor attribution, factor summary |
|
|
717
|
+
| `quantlite.factors.custom` | CustomFactor, significance testing, correlation matrix, factor portfolios, decay analysis |
|
|
718
|
+
| `quantlite.factors.tail_risk` | CVaR decomposition, regime factor exposure, crowding score, tail factor beta |
|
|
719
|
+
| `quantlite.simulation.evt_simulation` | EVT tail simulation, parametric tail simulation, historical bootstrap EVT, scenario fan |
|
|
720
|
+
| `quantlite.simulation.copula_mc` | Gaussian copula MC, t-copula MC, stress correlation MC, joint tail probability |
|
|
721
|
+
| `quantlite.simulation.regime_mc` | Regime-switching simulation, stress test scenarios, reverse stress test, simulation summary |
|
|
722
|
+
|
|
723
|
+
## v0.9: Fat-Tail Monte Carlo
|
|
724
|
+
|
|
725
|
+
Three simulation families that go beyond naive Monte Carlo. Standard MC assumes returns are Gaussian and independent. QuantLite's fat-tail MC uses EVT for realistic tails, copulas for joint dependence, and regime switching for structural breaks.
|
|
726
|
+
|
|
727
|
+
### EVT Tail Simulation
|
|
728
|
+
|
|
729
|
+
Generate scenarios with GPD-fitted tails that respect the true shape of financial returns.
|
|
730
|
+
|
|
731
|
+
```python
|
|
732
|
+
import numpy as np
|
|
733
|
+
from quantlite.simulation import evt_tail_simulation, scenario_fan
|
|
734
|
+
|
|
735
|
+
rng = np.random.default_rng(42)
|
|
736
|
+
returns = np.concatenate([
|
|
737
|
+
rng.normal(0.0003, 0.01, 900),
|
|
738
|
+
rng.standard_t(3, 100) * 0.03,
|
|
739
|
+
])
|
|
740
|
+
|
|
741
|
+
# EVT-based scenario generation
|
|
742
|
+
simulated = evt_tail_simulation(returns, n_scenarios=20000, seed=42)
|
|
743
|
+
print(f"Historical 1st pctl: {np.percentile(returns, 1):.4f}")
|
|
744
|
+
print(f"Simulated 1st pctl: {np.percentile(simulated, 1):.4f}")
|
|
745
|
+
|
|
746
|
+
# Fan chart across multiple horizons
|
|
747
|
+
fan = scenario_fan(returns, horizons=[1, 5, 21, 63, 252])
|
|
748
|
+
for h in fan["horizons"]:
|
|
749
|
+
p5, p95 = fan["fans"][h]["5"], fan["fans"][h]["95"]
|
|
750
|
+
print(f" {h:>3}d: [{p5:+.2%}, {p95:+.2%}]")
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+

|
|
754
|
+
|
|
755
|
+

|
|
756
|
+
|
|
757
|
+
### Copula Monte Carlo
|
|
758
|
+
|
|
759
|
+
Multivariate simulation that preserves fat-tailed marginals and captures tail dependence.
|
|
760
|
+
|
|
761
|
+
```python
|
|
762
|
+
import numpy as np
|
|
763
|
+
from quantlite.simulation import t_copula_mc, joint_tail_probability
|
|
764
|
+
|
|
765
|
+
rng = np.random.default_rng(42)
|
|
766
|
+
fund_a = np.concatenate([rng.normal(0.0005, 0.012, 900), rng.standard_t(3, 100) * 0.03])
|
|
767
|
+
fund_b = np.concatenate([rng.normal(0.0003, 0.015, 900), rng.standard_t(4, 100) * 0.025])
|
|
768
|
+
corr = np.array([[1.0, 0.6], [0.6, 1.0]])
|
|
769
|
+
|
|
770
|
+
# t-copula captures tail dependence that Gaussian copula misses
|
|
771
|
+
simulated = t_copula_mc([fund_a, fund_b], corr, df=4, n_scenarios=50000)
|
|
772
|
+
|
|
773
|
+
result = joint_tail_probability(simulated, thresholds=[-0.03, -0.03])
|
|
774
|
+
print(f"Joint crash probability: {result['joint_probability']:.4f}")
|
|
775
|
+
print(f"Marginal probabilities: {result['marginal_probabilities']}")
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+

|
|
779
|
+
|
|
780
|
+

|
|
781
|
+
|
|
782
|
+
### Regime-Switching Simulation
|
|
783
|
+
|
|
784
|
+
Paths that switch between calm, volatile, and crisis regimes via a Markov chain.
|
|
785
|
+
|
|
786
|
+
```python
|
|
787
|
+
import numpy as np
|
|
788
|
+
from quantlite.simulation import (
|
|
789
|
+
regime_switching_simulation,
|
|
790
|
+
reverse_stress_test,
|
|
791
|
+
simulation_summary,
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
regimes = [
|
|
795
|
+
{"mu": 0.0004, "sigma": 0.008}, # Calm
|
|
796
|
+
{"mu": 0.0001, "sigma": 0.020}, # Volatile
|
|
797
|
+
{"mu": -0.002, "sigma": 0.035}, # Crisis
|
|
798
|
+
]
|
|
799
|
+
transition = np.array([
|
|
800
|
+
[0.95, 0.04, 0.01],
|
|
801
|
+
[0.10, 0.80, 0.10],
|
|
802
|
+
[0.05, 0.15, 0.80],
|
|
803
|
+
])
|
|
804
|
+
|
|
805
|
+
sim = regime_switching_simulation(regimes, transition, n_steps=252, n_scenarios=5000)
|
|
806
|
+
stats = simulation_summary(sim["returns"])
|
|
807
|
+
print(f"VaR 95%: {stats['var']['95%']:.2%}")
|
|
808
|
+
print(f"CVaR 95%: {stats['cvar']['95%']:.2%}")
|
|
809
|
+
print(f"P(loss > 20%): {stats['probability_of_ruin']['20%']:.2%}")
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+

|
|
813
|
+
|
|
814
|
+

|
|
815
|
+
|
|
816
|
+
See [docs/simulation_evt.md](docs/simulation_evt.md), [docs/simulation_copula.md](docs/simulation_copula.md), and [docs/simulation_regime.md](docs/simulation_regime.md) for the full API reference.
|
|
817
|
+
|
|
818
|
+
## v0.8: Factor Models
|
|
819
|
+
|
|
820
|
+
Three modules for comprehensive factor analysis: classical academic models, custom factor tools, and tail-risk-aware factor decomposition.
|
|
821
|
+
|
|
822
|
+
### Classical Factor Models
|
|
823
|
+
|
|
824
|
+
Decompose returns into systematic factor exposures and genuine alpha.
|
|
825
|
+
|
|
826
|
+
```python
|
|
827
|
+
from quantlite.factors import fama_french_three, factor_summary
|
|
828
|
+
|
|
829
|
+
# Fama-French three-factor regression
|
|
830
|
+
result = fama_french_three(fund_returns, market, smb, hml)
|
|
831
|
+
print(f"Alpha: {result['alpha']:.5f} (t={result['t_stats']['alpha']:.2f})")
|
|
832
|
+
print(f"Market beta: {result['betas']['market']:.3f}")
|
|
833
|
+
print(f"R-squared: {result['r_squared']:.3f}")
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+

|
|
837
|
+
|
|
838
|
+
### Custom Factor Tools
|
|
839
|
+
|
|
840
|
+
Build, test, and analyse proprietary factors.
|
|
841
|
+
|
|
842
|
+
```python
|
|
843
|
+
from quantlite.factors import CustomFactor, factor_portfolio, factor_decay
|
|
844
|
+
|
|
845
|
+
# Test factor decay
|
|
846
|
+
decay = factor_decay(returns, momentum_signal, max_lag=20)
|
|
847
|
+
print(f"Half-life: {decay['half_life']:.1f} periods")
|
|
848
|
+
|
|
849
|
+
# Build long-short portfolios
|
|
850
|
+
result = factor_portfolio(stock_returns, factor_values, n_quantiles=5)
|
|
851
|
+
print(f"Long-short spread: {result['spread'] * 252:.2%} annualised")
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+

|
|
855
|
+
|
|
856
|
+
### Tail Risk Factor Analysis
|
|
857
|
+
|
|
858
|
+
Understand how factor exposures behave in the tails and across regimes.
|
|
859
|
+
|
|
860
|
+
```python
|
|
861
|
+
from quantlite.factors import tail_factor_beta, factor_crowding_score
|
|
862
|
+
|
|
863
|
+
# Tail betas: how exposures amplify in crises
|
|
864
|
+
result = tail_factor_beta(returns, [market, value], ["Market", "Value"])
|
|
865
|
+
for name in ["Market", "Value"]:
|
|
866
|
+
print(f"{name}: full={result['full_betas'][name]:.2f}, "
|
|
867
|
+
f"tail={result['tail_betas'][name]:.2f}")
|
|
868
|
+
|
|
869
|
+
# Factor crowding detection
|
|
870
|
+
crowd = factor_crowding_score([value_rets, momentum_rets])
|
|
871
|
+
print(f"Crowding score: {crowd['current_score']:.3f}")
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+

|
|
875
|
+
|
|
876
|
+
See [docs/factors_classical.md](docs/factors_classical.md), [docs/factors_custom.md](docs/factors_custom.md), and [docs/factors_tail_risk.md](docs/factors_tail_risk.md) for the full API reference.
|
|
716
877
|
|
|
717
878
|
## v0.4: The Taleb Stack
|
|
718
879
|
|
|
@@ -632,6 +632,167 @@ Same Stephen Few theme, same muted palette, but with hover info, zoom, and nativ
|
|
|
632
632
|
| `quantlite.crypto.stablecoin` | Depeg probability, peg deviation tracking, recovery time, reserve risk scoring |
|
|
633
633
|
| `quantlite.crypto.exchange` | Exchange concentration (HHI), wallet risk, proof of reserves, liquidity risk, slippage |
|
|
634
634
|
| `quantlite.crypto.onchain` | Wallet exposure, TVL tracking, DeFi dependency graphs, smart contract risk scoring |
|
|
635
|
+
| `quantlite.factors.classical` | Fama-French three/five-factor, Carhart four-factor, factor attribution, factor summary |
|
|
636
|
+
| `quantlite.factors.custom` | CustomFactor, significance testing, correlation matrix, factor portfolios, decay analysis |
|
|
637
|
+
| `quantlite.factors.tail_risk` | CVaR decomposition, regime factor exposure, crowding score, tail factor beta |
|
|
638
|
+
| `quantlite.simulation.evt_simulation` | EVT tail simulation, parametric tail simulation, historical bootstrap EVT, scenario fan |
|
|
639
|
+
| `quantlite.simulation.copula_mc` | Gaussian copula MC, t-copula MC, stress correlation MC, joint tail probability |
|
|
640
|
+
| `quantlite.simulation.regime_mc` | Regime-switching simulation, stress test scenarios, reverse stress test, simulation summary |
|
|
641
|
+
|
|
642
|
+
## v0.9: Fat-Tail Monte Carlo
|
|
643
|
+
|
|
644
|
+
Three simulation families that go beyond naive Monte Carlo. Standard MC assumes returns are Gaussian and independent. QuantLite's fat-tail MC uses EVT for realistic tails, copulas for joint dependence, and regime switching for structural breaks.
|
|
645
|
+
|
|
646
|
+
### EVT Tail Simulation
|
|
647
|
+
|
|
648
|
+
Generate scenarios with GPD-fitted tails that respect the true shape of financial returns.
|
|
649
|
+
|
|
650
|
+
```python
|
|
651
|
+
import numpy as np
|
|
652
|
+
from quantlite.simulation import evt_tail_simulation, scenario_fan
|
|
653
|
+
|
|
654
|
+
rng = np.random.default_rng(42)
|
|
655
|
+
returns = np.concatenate([
|
|
656
|
+
rng.normal(0.0003, 0.01, 900),
|
|
657
|
+
rng.standard_t(3, 100) * 0.03,
|
|
658
|
+
])
|
|
659
|
+
|
|
660
|
+
# EVT-based scenario generation
|
|
661
|
+
simulated = evt_tail_simulation(returns, n_scenarios=20000, seed=42)
|
|
662
|
+
print(f"Historical 1st pctl: {np.percentile(returns, 1):.4f}")
|
|
663
|
+
print(f"Simulated 1st pctl: {np.percentile(simulated, 1):.4f}")
|
|
664
|
+
|
|
665
|
+
# Fan chart across multiple horizons
|
|
666
|
+
fan = scenario_fan(returns, horizons=[1, 5, 21, 63, 252])
|
|
667
|
+
for h in fan["horizons"]:
|
|
668
|
+
p5, p95 = fan["fans"][h]["5"], fan["fans"][h]["95"]
|
|
669
|
+
print(f" {h:>3}d: [{p5:+.2%}, {p95:+.2%}]")
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+

|
|
673
|
+
|
|
674
|
+

|
|
675
|
+
|
|
676
|
+
### Copula Monte Carlo
|
|
677
|
+
|
|
678
|
+
Multivariate simulation that preserves fat-tailed marginals and captures tail dependence.
|
|
679
|
+
|
|
680
|
+
```python
|
|
681
|
+
import numpy as np
|
|
682
|
+
from quantlite.simulation import t_copula_mc, joint_tail_probability
|
|
683
|
+
|
|
684
|
+
rng = np.random.default_rng(42)
|
|
685
|
+
fund_a = np.concatenate([rng.normal(0.0005, 0.012, 900), rng.standard_t(3, 100) * 0.03])
|
|
686
|
+
fund_b = np.concatenate([rng.normal(0.0003, 0.015, 900), rng.standard_t(4, 100) * 0.025])
|
|
687
|
+
corr = np.array([[1.0, 0.6], [0.6, 1.0]])
|
|
688
|
+
|
|
689
|
+
# t-copula captures tail dependence that Gaussian copula misses
|
|
690
|
+
simulated = t_copula_mc([fund_a, fund_b], corr, df=4, n_scenarios=50000)
|
|
691
|
+
|
|
692
|
+
result = joint_tail_probability(simulated, thresholds=[-0.03, -0.03])
|
|
693
|
+
print(f"Joint crash probability: {result['joint_probability']:.4f}")
|
|
694
|
+
print(f"Marginal probabilities: {result['marginal_probabilities']}")
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+

|
|
698
|
+
|
|
699
|
+

|
|
700
|
+
|
|
701
|
+
### Regime-Switching Simulation
|
|
702
|
+
|
|
703
|
+
Paths that switch between calm, volatile, and crisis regimes via a Markov chain.
|
|
704
|
+
|
|
705
|
+
```python
|
|
706
|
+
import numpy as np
|
|
707
|
+
from quantlite.simulation import (
|
|
708
|
+
regime_switching_simulation,
|
|
709
|
+
reverse_stress_test,
|
|
710
|
+
simulation_summary,
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
regimes = [
|
|
714
|
+
{"mu": 0.0004, "sigma": 0.008}, # Calm
|
|
715
|
+
{"mu": 0.0001, "sigma": 0.020}, # Volatile
|
|
716
|
+
{"mu": -0.002, "sigma": 0.035}, # Crisis
|
|
717
|
+
]
|
|
718
|
+
transition = np.array([
|
|
719
|
+
[0.95, 0.04, 0.01],
|
|
720
|
+
[0.10, 0.80, 0.10],
|
|
721
|
+
[0.05, 0.15, 0.80],
|
|
722
|
+
])
|
|
723
|
+
|
|
724
|
+
sim = regime_switching_simulation(regimes, transition, n_steps=252, n_scenarios=5000)
|
|
725
|
+
stats = simulation_summary(sim["returns"])
|
|
726
|
+
print(f"VaR 95%: {stats['var']['95%']:.2%}")
|
|
727
|
+
print(f"CVaR 95%: {stats['cvar']['95%']:.2%}")
|
|
728
|
+
print(f"P(loss > 20%): {stats['probability_of_ruin']['20%']:.2%}")
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+

|
|
732
|
+
|
|
733
|
+

|
|
734
|
+
|
|
735
|
+
See [docs/simulation_evt.md](docs/simulation_evt.md), [docs/simulation_copula.md](docs/simulation_copula.md), and [docs/simulation_regime.md](docs/simulation_regime.md) for the full API reference.
|
|
736
|
+
|
|
737
|
+
## v0.8: Factor Models
|
|
738
|
+
|
|
739
|
+
Three modules for comprehensive factor analysis: classical academic models, custom factor tools, and tail-risk-aware factor decomposition.
|
|
740
|
+
|
|
741
|
+
### Classical Factor Models
|
|
742
|
+
|
|
743
|
+
Decompose returns into systematic factor exposures and genuine alpha.
|
|
744
|
+
|
|
745
|
+
```python
|
|
746
|
+
from quantlite.factors import fama_french_three, factor_summary
|
|
747
|
+
|
|
748
|
+
# Fama-French three-factor regression
|
|
749
|
+
result = fama_french_three(fund_returns, market, smb, hml)
|
|
750
|
+
print(f"Alpha: {result['alpha']:.5f} (t={result['t_stats']['alpha']:.2f})")
|
|
751
|
+
print(f"Market beta: {result['betas']['market']:.3f}")
|
|
752
|
+
print(f"R-squared: {result['r_squared']:.3f}")
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+

|
|
756
|
+
|
|
757
|
+
### Custom Factor Tools
|
|
758
|
+
|
|
759
|
+
Build, test, and analyse proprietary factors.
|
|
760
|
+
|
|
761
|
+
```python
|
|
762
|
+
from quantlite.factors import CustomFactor, factor_portfolio, factor_decay
|
|
763
|
+
|
|
764
|
+
# Test factor decay
|
|
765
|
+
decay = factor_decay(returns, momentum_signal, max_lag=20)
|
|
766
|
+
print(f"Half-life: {decay['half_life']:.1f} periods")
|
|
767
|
+
|
|
768
|
+
# Build long-short portfolios
|
|
769
|
+
result = factor_portfolio(stock_returns, factor_values, n_quantiles=5)
|
|
770
|
+
print(f"Long-short spread: {result['spread'] * 252:.2%} annualised")
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+

|
|
774
|
+
|
|
775
|
+
### Tail Risk Factor Analysis
|
|
776
|
+
|
|
777
|
+
Understand how factor exposures behave in the tails and across regimes.
|
|
778
|
+
|
|
779
|
+
```python
|
|
780
|
+
from quantlite.factors import tail_factor_beta, factor_crowding_score
|
|
781
|
+
|
|
782
|
+
# Tail betas: how exposures amplify in crises
|
|
783
|
+
result = tail_factor_beta(returns, [market, value], ["Market", "Value"])
|
|
784
|
+
for name in ["Market", "Value"]:
|
|
785
|
+
print(f"{name}: full={result['full_betas'][name]:.2f}, "
|
|
786
|
+
f"tail={result['tail_betas'][name]:.2f}")
|
|
787
|
+
|
|
788
|
+
# Factor crowding detection
|
|
789
|
+
crowd = factor_crowding_score([value_rets, momentum_rets])
|
|
790
|
+
print(f"Crowding score: {crowd['current_score']:.3f}")
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+

|
|
794
|
+
|
|
795
|
+
See [docs/factors_classical.md](docs/factors_classical.md), [docs/factors_custom.md](docs/factors_custom.md), and [docs/factors_tail_risk.md](docs/factors_tail_risk.md) for the full API reference.
|
|
635
796
|
|
|
636
797
|
## v0.4: The Taleb Stack
|
|
637
798
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quantlite"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.9.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" }
|
|
@@ -73,3 +73,6 @@ python_version = "3.10"
|
|
|
73
73
|
warn_return_any = true
|
|
74
74
|
warn_unused_configs = true
|
|
75
75
|
disallow_untyped_defs = false
|
|
76
|
+
|
|
77
|
+
[tool.pytest.ini_options]
|
|
78
|
+
testpaths = ["tests"]
|
|
@@ -77,6 +77,8 @@ __all__ = [
|
|
|
77
77
|
"diversification",
|
|
78
78
|
# Crypto-native risk
|
|
79
79
|
"crypto",
|
|
80
|
+
# Fat-tail Monte Carlo simulation
|
|
81
|
+
"simulation",
|
|
80
82
|
]
|
|
81
83
|
|
|
82
84
|
from . import ( # noqa: E402
|
|
@@ -90,4 +92,5 @@ from . import ( # noqa: E402
|
|
|
90
92
|
overfit,
|
|
91
93
|
resample,
|
|
92
94
|
scenarios,
|
|
95
|
+
simulation,
|
|
93
96
|
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Factor models: classical, custom, and tail risk factor analysis.
|
|
2
|
+
|
|
3
|
+
Provides tools for multi-factor attribution, custom factor construction,
|
|
4
|
+
factor significance testing, and tail-risk-aware factor decomposition.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from quantlite.factors.classical import (
|
|
8
|
+
carhart_four,
|
|
9
|
+
factor_attribution,
|
|
10
|
+
factor_summary,
|
|
11
|
+
fama_french_five,
|
|
12
|
+
fama_french_three,
|
|
13
|
+
)
|
|
14
|
+
from quantlite.factors.custom import (
|
|
15
|
+
CustomFactor,
|
|
16
|
+
factor_correlation_matrix,
|
|
17
|
+
factor_decay,
|
|
18
|
+
factor_portfolio,
|
|
19
|
+
test_factor_significance,
|
|
20
|
+
)
|
|
21
|
+
from quantlite.factors.tail_risk import (
|
|
22
|
+
factor_crowding_score,
|
|
23
|
+
factor_cvar_decomposition,
|
|
24
|
+
regime_factor_exposure,
|
|
25
|
+
tail_factor_beta,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"fama_french_three",
|
|
30
|
+
"fama_french_five",
|
|
31
|
+
"carhart_four",
|
|
32
|
+
"factor_attribution",
|
|
33
|
+
"factor_summary",
|
|
34
|
+
"CustomFactor",
|
|
35
|
+
"test_factor_significance",
|
|
36
|
+
"factor_correlation_matrix",
|
|
37
|
+
"factor_portfolio",
|
|
38
|
+
"factor_decay",
|
|
39
|
+
"factor_cvar_decomposition",
|
|
40
|
+
"regime_factor_exposure",
|
|
41
|
+
"factor_crowding_score",
|
|
42
|
+
"tail_factor_beta",
|
|
43
|
+
]
|