quantmine 0.2.0__py3-none-any.whl
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.
- quantmine/__init__.py +97 -0
- quantmine/back_testing.py +381 -0
- quantmine/config.py +115 -0
- quantmine/data_acquisition.py +326 -0
- quantmine/datareader.py +119 -0
- quantmine/factor_attribution.py +70 -0
- quantmine/factor_mining.py +192 -0
- quantmine/factor_register.py +73 -0
- quantmine/ic_calculator.py +543 -0
- quantmine/load_config.py +16 -0
- quantmine-0.2.0.dist-info/METADATA +222 -0
- quantmine-0.2.0.dist-info/RECORD +14 -0
- quantmine-0.2.0.dist-info/WHEEL +4 -0
- quantmine-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quantmine
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Equity factor research library with honest statistics: Newey-West IC tests, multiple-testing control, point-in-time universe, turnover-based costs, Carhart attribution
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Requires-Dist: numpy>=2.4
|
|
9
|
+
Requires-Dist: pandas>=3.0
|
|
10
|
+
Requires-Dist: pyarrow>=24.0
|
|
11
|
+
Requires-Dist: pyyaml>=6.0
|
|
12
|
+
Requires-Dist: scipy>=1.17
|
|
13
|
+
Requires-Dist: statsmodels>=0.14
|
|
14
|
+
Provides-Extra: data
|
|
15
|
+
Requires-Dist: beautifulsoup4>=4.14; extra == 'data'
|
|
16
|
+
Requires-Dist: curl-cffi>=0.15; extra == 'data'
|
|
17
|
+
Requires-Dist: pandas-datareader>=0.11; extra == 'data'
|
|
18
|
+
Requires-Dist: yfinance>=1.4; extra == 'data'
|
|
19
|
+
Provides-Extra: viz
|
|
20
|
+
Requires-Dist: plotly>=6.8; extra == 'viz'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# quantmine
|
|
24
|
+
|
|
25
|
+
An equity factor research library built around one principle: **statistical
|
|
26
|
+
honesty**. Every step that commonly inflates backtest results — survivorship
|
|
27
|
+
bias, overlapping-return autocorrelation, multiple testing, look-ahead in
|
|
28
|
+
orthogonalization, unrealistic transaction costs — is explicitly addressed,
|
|
29
|
+
and conclusions are reported with their uncertainty, not just their point
|
|
30
|
+
estimates.
|
|
31
|
+
|
|
32
|
+
The repo doubles as a full S&P 500 research case study: the library
|
|
33
|
+
(`quantmine/`), the daily Airflow pipeline (`pipelines/`), and the findings
|
|
34
|
+
below were produced by the same code you can `pip install`.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install quantmine # library only
|
|
40
|
+
pip install "quantmine[data]" # + yfinance download stack
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For development (repo checkout, Python ≥ 3.13, [uv](https://docs.astral.sh/uv/)):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv sync
|
|
47
|
+
python -m pytest test
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
Bring your own price data — any wide DataFrame (index = trading days,
|
|
53
|
+
columns = tickers) works; nothing is hard-wired to yfinance or the S&P 500:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import pandas as pd
|
|
57
|
+
import quantmine as qf
|
|
58
|
+
|
|
59
|
+
# 1) Wrap your data (or use qf.ParquetSource / qf.YFinanceSource)
|
|
60
|
+
data = qf.MarketData(close=close_df, volume=volume_df)
|
|
61
|
+
|
|
62
|
+
# 2) Register a custom factor — built-in factors register automatically
|
|
63
|
+
@qf.factor_register("my_reversal")
|
|
64
|
+
def my_reversal(close: pd.DataFrame, tickers: list) -> pd.DataFrame:
|
|
65
|
+
return -close[tickers].pct_change(5)
|
|
66
|
+
|
|
67
|
+
# 3) Compute all registered factors (dependencies resolve automatically)
|
|
68
|
+
pool = qf.build_param_pool(data, day=5, halflife=10, period=20)
|
|
69
|
+
failed, factors = qf.calculate_all_factors(pool)
|
|
70
|
+
|
|
71
|
+
# 4) Cross-sectional IC with Newey-West t-stats and multiple-testing control
|
|
72
|
+
fwd = qf.forward_return(data.close, periods=[1, 5, 20])
|
|
73
|
+
cs_ic = qf.CS_Information_Correlation(factors, fwd, output_path="cs_ic.parquet")
|
|
74
|
+
report = qf.multiple_testing(qf.newey_west_summary(cs_ic))
|
|
75
|
+
|
|
76
|
+
# 5) Quantile backtest on a point-in-time universe, turnover-based costs
|
|
77
|
+
universe = qf.MembershipTableSource(membership_df) # or qf.StaticUniverse([...])
|
|
78
|
+
results, history = qf.quantile_backtest(universe, factors, ["my_reversal"], fwd)
|
|
79
|
+
daily = qf.expand_all_to_daily_returns(history, data.close)
|
|
80
|
+
|
|
81
|
+
# 6) Carhart four-factor attribution of the long-short returns (daily, HAC)
|
|
82
|
+
french = qf.load_french_factors("ff3_daily.csv", "momentum_daily.csv")
|
|
83
|
+
model = qf.carhart_attribution(daily[("my_reversal", 20)]["long_short"], french)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Extension points
|
|
87
|
+
|
|
88
|
+
| Protocol / hook | Purpose | Ships with |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `DataSource.load(...) -> MarketData` | plug in any price/volume source | `ParquetSource`, `CSVSource`, `ExcelSource`, `YFinanceSource` |
|
|
91
|
+
| `ConstituentsSource.get_constituents(date) -> set` | point-in-time universe from any provider | `MembershipTableSource` (interval table), `StaticUniverse` |
|
|
92
|
+
| `@factor_register(name)` | add factors; params injected by name, factor-on-factor dependencies resolved | 8 built-in factors |
|
|
93
|
+
| `config.example.yaml` | every pipeline parameter as validated dataclasses via `qf.load_configs` | defaults documented in-file |
|
|
94
|
+
|
|
95
|
+
## Headline result (S&P 500 case study)
|
|
96
|
+
|
|
97
|
+
The 20-day average volume factor (`TwentyDayAvgVol`) is the only candidate
|
|
98
|
+
that survives the full testing gauntlet:
|
|
99
|
+
|
|
100
|
+
| Stage | Result |
|
|
101
|
+
|---|---|
|
|
102
|
+
| Train IC (2015–2023, Newey-West) | t = 3.78 (20d holding), passes Bonferroni & Benjamini-Hochberg across all 18 factor × horizon tests |
|
|
103
|
+
| Out-of-sample quintiles (2024–2026) | Monotonic (Spearman 0.9); long-short gross ~14 %/yr (Sharpe ~1.5), ~1.4 Sharpe net of turnover-based costs |
|
|
104
|
+
| Carhart 4-factor attribution (daily, HAC, net of costs) | Market beta 0.24 (significant), large-cap tilt (SMB −0.13); momentum & value loadings insignificant |
|
|
105
|
+
| Net alpha | ~10 %/yr net of costs (t = 1.78, p ≈ 0.07, n = 603 daily obs) — economically meaningful, **marginally short of the 5 % significance bar** on 2.4 years of out-of-sample data |
|
|
106
|
+
|
|
107
|
+
The honest conclusion: the factor's IC is robustly significant in-sample under
|
|
108
|
+
conservative testing; its out-of-sample net alpha is economically meaningful
|
|
109
|
+
but does not clear conventional significance. Live verification over a longer
|
|
110
|
+
window is required — and that is exactly what a research report should say.
|
|
111
|
+
(A low-power period-level regression, n ≈ 30, says nothing either way —
|
|
112
|
+
p ≈ 0.38 with a confidence interval wide enough to hold any conclusion. Test
|
|
113
|
+
power and test bookkeeping move the verdict as much as the signal does.)
|
|
114
|
+
|
|
115
|
+
The remaining seven candidate factors (momentum, short-term reversal,
|
|
116
|
+
volatility, downside volatility, volume-price correlation, …) fail the
|
|
117
|
+
corrected significance tests. Documenting *why* they fail is part of the
|
|
118
|
+
point.
|
|
119
|
+
|
|
120
|
+
## Methodology highlights
|
|
121
|
+
|
|
122
|
+
- **Survivorship-bias correction** — the universe is rebuilt from historical
|
|
123
|
+
S&P 500 membership (764 tickers over 2015–2026); 569 were recoverable via
|
|
124
|
+
yfinance, and the residual gap is disclosed rather than hidden.
|
|
125
|
+
- **Point-in-time universe** — each backtest cross-section only contains
|
|
126
|
+
stocks that were actually index members on that date.
|
|
127
|
+
- **Newey-West IC tests** — daily ICs on overlapping k-day forward returns are
|
|
128
|
+
autocorrelated; plain `t = IR·√n` overstates significance several-fold. NW
|
|
129
|
+
(Bartlett kernel, lag = 2(k−1)) uses all daily observations while correcting
|
|
130
|
+
the standard error. A down-sampled IID test is kept as a robustness control.
|
|
131
|
+
- **Multiple-testing control** — Bonferroni and Benjamini-Hochberg across all
|
|
132
|
+
factor × holding-period combinations.
|
|
133
|
+
- **Train/test split with embargo** — factors are selected and the
|
|
134
|
+
orthogonalization is fit on 2015–2023 only; a gap of one month before the
|
|
135
|
+
test window prevents overlapping forward returns from leaking across the
|
|
136
|
+
split.
|
|
137
|
+
- **Expanding-window orthogonalization** — correlated factors are residualized
|
|
138
|
+
with betas estimated on data available up to each date (no full-sample
|
|
139
|
+
look-ahead).
|
|
140
|
+
- **Turnover-based transaction costs** — costs are charged on actual
|
|
141
|
+
membership turnover per rebalance, not on a flat 100 %-turnover assumption.
|
|
142
|
+
- **Sanity checks** — factor displacement and cross-sectional shuffling tests
|
|
143
|
+
confirm the backtest machinery itself is not the source of the returns.
|
|
144
|
+
- **Tested** — the research chain is covered by a unit + golden-value test
|
|
145
|
+
suite (`test/`), including hand-computed backtest fixtures, determinism
|
|
146
|
+
checks, and point-in-time universe edge cases.
|
|
147
|
+
|
|
148
|
+
## Pipeline
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
yfinance (batch download, retry, blacklist, checkpoints)
|
|
152
|
+
│ quantmine/data_acquisition.py · pipelines/task_1.py
|
|
153
|
+
▼
|
|
154
|
+
cleaning & merge (ffill, dedup) Airflow DAG: pipelines/DAG_pipeline.py
|
|
155
|
+
│ pipelines/task_2.py
|
|
156
|
+
▼
|
|
157
|
+
factor computation (registry-driven, vectorized pandas)
|
|
158
|
+
│ quantmine/factor_mining.py · pipelines/task_3.py
|
|
159
|
+
▼
|
|
160
|
+
IC testing: cross-sectional & time-series IC, NW t, BH/Bonferroni,
|
|
161
|
+
train/test split, orthogonalization quantmine/ic_calculator.py
|
|
162
|
+
▼
|
|
163
|
+
quintile backtest: PIT universe, monotonicity, turnover costs,
|
|
164
|
+
displacement/shuffle sanity tests quantmine/back_testing.py
|
|
165
|
+
▼
|
|
166
|
+
Carhart 4-factor attribution (daily, HAC) quantmine/factor_attribution.py
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Repository layout:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
quantmine/ research library (importable package)
|
|
173
|
+
pipelines/ Airflow DAG + daily CLI tasks
|
|
174
|
+
test/ pytest suite (unit + golden-value)
|
|
175
|
+
config.example.yaml all pipeline parameters, documented defaults
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Reproducing the case study
|
|
179
|
+
|
|
180
|
+
**Market data is not included** (Yahoo Finance terms of service do not permit
|
|
181
|
+
redistribution). To reproduce:
|
|
182
|
+
|
|
183
|
+
1. Historical S&P 500 membership: provide a CSV with `ticker`, `start_date`,
|
|
184
|
+
`end_date` columns and point `SP500_MEMBERSHIP_CSV` at it.
|
|
185
|
+
2. Prices/volumes: `python pipelines/task_1.py --date <ds> --batch manual`
|
|
186
|
+
downloads in batches with checkpointing, then `pipelines/task_2.py` cleans
|
|
187
|
+
and merges.
|
|
188
|
+
3. Fama-French factors: download the daily FF3 and momentum CSVs from the
|
|
189
|
+
[Ken French Data Library](https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html)
|
|
190
|
+
into `tmp/ff3/`.
|
|
191
|
+
4. Run the research chain: `python pipelines/task_3.py ...` for factors, then
|
|
192
|
+
the IC → backtest → attribution steps as in the quick start (or
|
|
193
|
+
`python -m quantmine.ic_calculator` for the packaged train/test workflow).
|
|
194
|
+
|
|
195
|
+
For the Airflow DAG, set `QUANT_PROJECT_ROOT` and `QUANT_PYTHON_BIN` (see
|
|
196
|
+
`DAG_pipeline.py` docstring) and copy `airflow.cfg.example` keys into your own
|
|
197
|
+
config — never commit a real `airflow.cfg`.
|
|
198
|
+
|
|
199
|
+
## Known limitations
|
|
200
|
+
|
|
201
|
+
- ~195 of 764 historical members could not be recovered from yfinance (mostly
|
|
202
|
+
true delistings/acquisitions), so a residual survivorship bias remains and
|
|
203
|
+
likely flatters the results slightly.
|
|
204
|
+
- The out-of-sample window (2024–2026) covers a single market regime.
|
|
205
|
+
- The long-short portfolio carries a significant 0.24 market beta; a
|
|
206
|
+
beta-hedged variant is on the roadmap.
|
|
207
|
+
- Transaction cost model is a flat per-turnover rate; no market-impact or
|
|
208
|
+
borrow-cost modeling.
|
|
209
|
+
|
|
210
|
+
## Roadmap
|
|
211
|
+
|
|
212
|
+
- [ ] Migrate storage from parquet files to PostgreSQL/DuckDB
|
|
213
|
+
- [ ] REST API + MCP server exposing the research chain as agent-callable tools
|
|
214
|
+
- [ ] RAG-based automated research reports (ChromaDB + LLM)
|
|
215
|
+
- [ ] Extend the Airflow DAG to cover IC testing → backtest → reporting
|
|
216
|
+
- [ ] Rebuild the analytics dashboard (frontend rewrite in progress)
|
|
217
|
+
- [ ] Scale the data layer (full US market) with PySpark
|
|
218
|
+
- [ ] Beta-hedged long-short variant; GARCH volatility targeting
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
quantmine/__init__.py,sha256=jxKZ1GXRmvLT5ib3gdpifXl5B-f5S1-gEbuWtK3asFY,3352
|
|
2
|
+
quantmine/back_testing.py,sha256=4kpyJjnp9FsAxSYhgPRWUf5r2auhMNJuDQJFYOwTiC4,17344
|
|
3
|
+
quantmine/config.py,sha256=zBLyvfrvFgmMG8HRaIuJee2Bzt0WCvGhBo1jEiF-xU4,3596
|
|
4
|
+
quantmine/data_acquisition.py,sha256=KWc0ecjx6rDUvFdxb3UE14RI5fNHhKEbxg6LChQAlxA,13040
|
|
5
|
+
quantmine/datareader.py,sha256=soQg7IPWZlkkPnNqsS_eC2UHyGE_0b5VCirCxnKKKPU,4932
|
|
6
|
+
quantmine/factor_attribution.py,sha256=YuWLJVeJbkymTUugBzcI8IMgs7LYn9h570FXPQXWwTg,2967
|
|
7
|
+
quantmine/factor_mining.py,sha256=c4A7vANKLyXXwfyBXSmT_iwWfWGm5PpeTTM4miA__E0,7396
|
|
8
|
+
quantmine/factor_register.py,sha256=6ok5hz8sJ3nDTzZ3t9I-dRDGjf0uWRU7PllKFWmHApE,2706
|
|
9
|
+
quantmine/ic_calculator.py,sha256=i3LbgUAo8qKA50w_VyyJfbzZQlYPgnccgxOtd9cRSAg,24302
|
|
10
|
+
quantmine/load_config.py,sha256=NESNgRhvaSTDzvM-9Cdc5-5pyAPTitIgwNPFjDzfLXo,734
|
|
11
|
+
quantmine-0.2.0.dist-info/METADATA,sha256=b91o1yKSeA7PM2t36GgvxcA_R25q6BnnG99ZSMWQ_R8,10289
|
|
12
|
+
quantmine-0.2.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
13
|
+
quantmine-0.2.0.dist-info/licenses/LICENSE,sha256=85eZoHh_6k3x8ldYseHu6oNP-hYd4z-FFjjTbc9oTl0,1091
|
|
14
|
+
quantmine-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 b00831205-web
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|