Strategies
Asset-Class Trend Following (Faber 10-Month SMA) on Five USEQ ETFs: SPY / EFA / IEF / DBC / GLD, Equal-Weight Long-or-Cash, Month-End Decision on DAILY Bars
Outcome: Abandoned
UseqFaberAssetClassTrendSma10
Outcome Summary
UseqFaberAssetClassTrendSma10 set out to replicate the published Faber 10-month-SMA long-or-cash rule on five commission-free USEQ asset-class ETFs, with equal-weight 20% sleeves decided at each month end. The code was designed to keep per-bar work O(1), but across 3 iterations it never passed Layer 3 verification because the sandbox smoke test kept exceeding the 300s limit on a 2400-day daily window. The run was abandoned as a sandbox_timeout verification loop before any backtest, optimization or analyst review took place, so the trend-following hypothesis itself was never tested.
Hypothesis
Iteration 3: the iteration-2 submission accidentally shipped a placeholder string instead of source (Layer 1 syntax error). This restores the full iteration-1 source and applies only the intended Layer-2 fix: _pending() no longer unconditionally calls cache.orders_inflight (missing on the synthetic mock cache); it checks orders_open and calls orders_inflight only when the cache class provides it. Signal, month-end logic, sizing (5 x 20% = 100% gross, whole shares) unchanged.
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
Faber (2007) asset-class trend following on SPY/EFA/IEF/DBC/GLD (USEQ, daily bars). On each month's last session each ETF's close is compared with the mean of its last 10 month-end closes; above -> hold that sleeve at 1/5 of equity, below -> cash. Long-only, 1x, equal weight, no other filters.
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).
Iteration History
Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['sma_months', 'min_notional', 'sleeve_weight'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: AttributeError: '_MockCache' object has no attribute 'orders_inflight' (bar timestamp: 1735689660000) - steady_downtrend: AttributeError: '_MockCache' object has no attribute 'orders_inflight' (bar timestamp: 1735689660000) - flat_ranging: AttributeError: '_MockCache' object has no attribute 'orders_inflight' (bar timestamp: 1735689660000) - volatility_spike: AttributeError: '_MockCache' object has no attribute 'orders_inflight' (bar timestamp: 1735689660000) - zero_volume: AttributeError: '_MockCache' object has no attribute 'orders_inflight' (bar timestamp: 1735689660000) - price_gap: AttributeError: '_MockCache' object has no attribute 'orders_inflight' (bar timestamp: 1735689660000)
Verification failed (Layer 1 — static analysis): - Syntax error: invalid character '—' (U+2014) (<unknown>, line 1) (line 1)
Outcome Summary
The hypothesis and code both claimed O(1) per-bar work, yet this multi-leg daily strategy still timed out repeatedly, so a future iteration should measure actual per-bar cost across all five legs before resubmitting rather than assume the monthly-clock design is cheap.
The run was abandoned in a verification loop after 3 iterations because the Layer 3 sandbox backtest went over the 300s wall-clock limit on a ~20000-bar window (2400 days of SPY.USEQ-1-DAY-LAST-EXTERNAL). The backtest review, optimization, analyst and risk stages were never reached.
A direct test of Faber's (2007) asset-class trend following on five USEQ ETFs (SPY, EFA, IEF, DBC, GLD, with GLD replacing the unavailable VNQ): each sleeve is held at 1/5 of equity when its month-end close is above its 10-month SMA and sits in cash otherwise, with no leverage or shorting.
There are no performance figures: the backtest report is empty and no optimization report was produced, so no return, Sharpe or trade-count numbers exist.
Analysis
Asset Class Trend Following
Backtest and paper results are hypothetical. Trading involves risk of loss.