Strategies
Precious-Metals Complex Mean-Reverting Portfolio (Zhao et al. 2018 MRP design): 3-Asset GDX/GLD/SLV Spread Built by Minimum-Predictability Weights Under a Variance Floor and an L1 Leverage Budget, Z-Score Entry/Exit With an Explicit Flat State (USEQ DAILY, long-short)
Outcome: Abandoned
GdxGldSlvMinPredictabilityMRP
Outcome Summary
GdxGldSlvMinPredictabilityMRP tried to trade a three-asset precious-metals spread (GDX/GLD/SLV). It used minimum-predictability eigenvector weights, re-estimated quarterly, and z-score entries, with a pre-registered estimate of roughly 7-10 trades a year. Over 3 iterations the code never passed verification. The Layer 3 sandbox backtest kept exceeding the 300s limit on a ~20,000-bar daily window. The run was abandoned as a sandbox timeout in the verification loop, so backtest review, optimization, analyst review and risk review were never reached and no performance metrics exist.
Hypothesis
Iteration 3 preserves the previously passing strategy logic and changes only the costly extra-leg access path. Extra-bar prices are cached once in on_extra_bar and read by exact close timestamp in O(1) time, eliminating repeated access to the accumulated extra-bar history during the 20,000-bar sandbox run.
verification_loop: sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of GDX.USEQ-1-DAY-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).
Implementation
A daily USEQ long-short mean-reverting portfolio over GDX, GLD, and SLV. It periodically estimates minimum-predictability weights from lagged covariance structure, enforces a spread-volatility floor, and trades the resulting spread by z-score.
Verification Results
sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of GDX.USEQ-1-DAY-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).
Outcome Summary
A daily multi-leg strategy has to keep its per-bar path in calculate_signal cheap, meaning O(1) or O(log n), and never recompute over the full history on each bar. The code should be profiled against the 20k-bar sandbox budget before it is submitted.
It was abandoned in the verification loop after 3 iterations. Layer 3 (the sandbox backtest) failed because the smoke test ran past the 300s wall-clock limit on a ~20,000-bar, 2400-day GDX.USEQ daily window, which points to per-bar work that grows with history length.
Following the Zhao et al. (2018) design, it built a mean-reverting GDX/GLD/SLV spread on daily USEQ bars. Weights came from a minimum Box-Tiao predictability eigenvector, re-estimated every 63 sessions with a variance floor and an L1 leverage budget. It traded z-score entries at |z|>=2 and exits at |z|<=0.25, with a flat state as the default.
There are no performance numbers. The strategy never finished a backtest: the backtest report is empty, optimization never ran, and no trades, returns or Sharpe were recorded.
Analysis
Optimal Portfolio Design for Statistical Arbitrage in Finance
Backtest and paper results are hypothetical. Trading involves risk of loss.