6.9 KiB
Case Study: ETF Cross-Asset Exposures
This case study applies the ML4T workflow to 100 exchange-traded funds spanning equities, fixed income, commodities, currencies, and real estate. ETFs offer a clean laboratory for cross-asset rotation: standardized pricing, deep liquidity, and broad asset-class coverage at a single rebalance cadence.
The configuration is the most cost-favorable in the book — long-only rank-and-rebalance, monthly month-end decisions on a 21-day forward-return label, with a 5--15 bps-per-leg cost model. That cadence makes it the natural setting for the broadest model-family comparison in the book: linear, GBM, tabular DL, sequence DL, latent factors, and causal DML are all trained on the same feature panel. The teaching point is the gap between IC and Sharpe — the family with the highest rank correlation is not the family with the highest portfolio Sharpe — which makes ETFs the canonical setting for the "portfolio construction mediates prediction quality" thread that runs through Ch16--Ch20.
At a Glance
| Property | Value |
|---|---|
| Asset Class | Multi-asset ETFs |
| Frequency | Daily data, monthly decisions |
| Universe | 100 ETFs across 9 categories |
| History | 2006--2025 |
| Primary Label | fwd_ret_21d |
| CV Folds | 8 (10Y train, 1Y val) |
| Cost Model | Material (5--15 bps per leg) |
Pipeline
| Stage | Notebook | Chapter | Description |
|---|---|---|---|
| Feasibility | 01_feasibility_analysis |
Ch6 | Universe breadth, point-in-time eligibility, horizon-cost feasibility, walk-forward demonstration |
| Labels | 02_labels |
Ch7 | 21-day and 5-day forward returns with walk-forward splits |
| Features | 03_financial_features |
Ch8 | Momentum, volatility, and cross-asset ranking features |
| Temporal | 04_model_based_features |
Ch9 | ARIMA, HMM, and spectral features from walk-forward fits |
| Evaluation | 05_evaluation |
Ch7--9 | Feature-label IC diagnostics across all engineered features |
| Linear | 06_linear |
Ch11 | Ridge, LASSO, ElasticNet baseline for cross-asset momentum |
| GBM | 07_gbm |
Ch12 | LightGBM with Optuna testing non-linear interactions |
| Tabular DL | 08_tabular_dl |
Ch12 | TabM rank-1 adapter MLP ensemble |
| LSTM | 09_dl_lstm |
Ch13 | Temporal gating over sequential ETF return windows |
| TSMixer | 10_dl_tsmixer |
Ch13 | Cross-asset lead-lag patterns via time-feature mixing |
| Latent Factors | 11_latent_factors |
Ch14 | Factor extraction across the ETF universe |
| Causal DML | 12_causal_dml |
Ch15 | Does momentum cause future ETF returns or reflect confounders? |
| Model Analysis | 13_model_analysis |
Ch11--15 | Cross-family IC comparison, checkpoint sensitivity, fold stability |
| Backtest | 14_backtest |
Ch16 | Strategy simulation with falsification against equal-weight |
| Portfolio | 15_portfolio_management |
Ch17 | Score-weighted, risk-parity, inverse-vol, MVO, HRP, and conformal-weighted allocation |
| Costs | 16_costs |
Ch18 | Transaction cost impact on the momentum edge |
| Risk | 17_risk_management |
Ch19 | Position-level stop-loss, trailing-stop, and time-exit overlays calibrated against the in-sample MAE distribution |
| Strategy Analysis | 18_strategy_analysis |
Ch20 | End-to-end strategy assessment with IC, Sharpe, and cost analysis |
Key Results
Signal quality: Daily-pooled IC for the highest-Sharpe configuration is +0.052 [+0.009, +0.095] (HAC t=2.37, p=0.018, excludes zero on the positive side); pct-positive is 56.4%, modestly above coin flip. The rank-correlation prior is statistically resolved at the validation window, with magnitude small but credibly nonzero.
Strategy-stage performance with CIs: The cross-stage rank-1 configuration is deep_learning/lstm_h64 on fwd_ret_21d resolved at the risk-overlay stage (risk-parity top-20 + MAE-calibrated trailing overlay trailing_mae_p25_h20_4p3pct). Validation Sharpe is +1.21 [+0.61, +1.87], PSR p=0.00029 — both Sharpe CI and PSR exclude zero on the positive side. Selection-adjusted DSR (effective-rank) is +0.072 (p=4.1\mathrm{e}{-5}) on the 20-variant family cohort, min_trl_periods is 461 (the 2016-day validation window clears the MinTRL bar by ≈4×), and PBO is 0.157 across 8 folds × 70 combinations — modest in-sample overfitting on the combinatorially shuffled folds but well inside the "low" band; the cross-stage label cohort (714 variants spanning every family × allocator × cost × overlay) carries DSR_ER +0.067 on the same leader.
Holdout closure: Validation→holdout Sharpe difference is -0.14 [-1.87, $+1.53$] (p=0.886, straddles zero with an extremely wide CI — the 481-day holdout cannot resolve decay magnitude under the disjoint-window pairing convention). Against an equal-weight benchmark, the holdout-period Sharpe difference is -0.34 [-1.69, $+0.81$] (p=0.582); the two-sided test does not reject. Holdout EW Sharpe runs at +1.36 — unusually high, driven by the 2024--2025 broad-equity rally where cross-asset rotation toward bonds and commodities gave back ground to a static equity-weighted universe.
Friction floor: Cost sensitivity scans 11 levels from 0 to 50 bps per leg. The highest-Sharpe configuration stays positive across the full grid; median Sharpe across all configurations stays positive through realistic ETF friction (≤5 bps). Both kill gates pass — validation Sharpe lower bound ≥ 0, and holdout strategy CI does not exclude zero negatively.
Running
# From repo root
uv run python case_studies/etfs/01_feasibility_analysis.py
uv run python case_studies/etfs/02_labels.py
uv run python case_studies/etfs/03_financial_features.py
uv run python case_studies/etfs/04_model_based_features.py
uv run python case_studies/etfs/05_evaluation.py
uv run python case_studies/etfs/06_linear.py
uv run python case_studies/etfs/07_gbm.py
uv run python case_studies/etfs/08_tabular_dl.py
uv run python case_studies/etfs/09_dl_lstm.py
uv run python case_studies/etfs/10_dl_tsmixer.py
uv run python case_studies/etfs/11_latent_factors.py
uv run python case_studies/etfs/12_causal_dml.py
uv run python case_studies/etfs/13_model_analysis.py
uv run python case_studies/etfs/14_backtest.py
uv run python case_studies/etfs/15_portfolio_management.py
uv run python case_studies/etfs/16_costs.py
uv run python case_studies/etfs/17_risk_management.py
uv run python case_studies/etfs/18_strategy_analysis.py
Run Log
Model training runs, predictions, and backtest results are tracked in a content-addressed registry under run_log/registry.db.