Strategies
Chartist-minus-Fundamentalist Signal (Bouchaud et al. 2017, 'Black was right'): 6-Month Vol-Normalised Trend Minus 5-Year Log-Price Deviation, Long/Flat, Per-Asset on a 10-ETF Asset-Class Panel (USEQ DAILY)
Outcome: Abandoned
ChartistMinusFundamentalistEtfPanelLongFlat
Outcome Summary
The strategy was built to test whether Bouchaud et al.'s 'fundamentalist' 5-year mean-reversion term adds anything to a 6-month trend signal, trading a 10-ETF asset-class panel long/flat on USEQ daily bars with a k=0 control. The hypothesis's own pre-study already suggested it would not (Sharpe 0.32 vs 0.33 without the value term). The code used incremental O(1) indicators and weekly batched decisions. Even so, all 3 iterations timed out in the Layer 3 sandbox backtest (over 300s on ~2400 days of SPY data), and the run was abandoned before any engine backtest, optimization or analyst verdict.
Hypothesis
Iteration 3 preserves the prior passing signal and sizing logic while batching position and order-cache lookups once per weekly panel decision. This removes repeated per-asset cache scans from the decision loop and keeps bar processing constant-time.
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 SPY.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
Independent long/flat ETF sleeves using a six-month volatility-normalized trend minus five-year log-price deviation signal, evaluated weekly with incremental indicator state.
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 SPY.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 multi-instrument panel strategy with a 1260-session warm-up has to fit inside the sandbox's 300s smoke-test budget before its research claim can be tested at all, so profile the runtime with every extra instrument feed attached, not just the per-bar indicator math.
After 3 iterations it was abandoned in the verification loop. The Layer 3 sandbox backtest went over the 300s wall-clock limit on a ~20,000-bar window (2400 days of SPY.USEQ daily bars), so it never reached backtest review, optimization, analyst or risk review.
A falsification test of Bouchaud et al. (2017): trade each of 10 asset-class ETFs (SPY, EFA, EEM, EWJ, TLT, IEF, GLD, SLV, DBC, USO) long/flat on USEQ daily bars. The signal was S = T - k*V, where T is a vol-normalised 126-session EMA trend and V is the log deviation from the 1260-session SMA, with k=1 fixed in advance, weekly decisions, and a k=0 control.
No engine backtest or optimization was produced (empty backtest report, no optimization report). The hypothesis's own point-in-time pre-study gave a pooled Sharpe of 0.32 for k=1 and 0.33 for k=0 (2006-2026, 15 of 21 years positive), so the value term added nothing, and the pre-study expected the strategy to fail the 0.5 OOS floor.
Analysis
Black was right: Price is within a factor 2 of Value
Backtest and paper results are hypothetical. Trading involves risk of loss.