LongHorizonTrendFollowingMajorsBasketLS
Hypotheses
Long-Horizon Trend-Following Basket Across Majors (~200-Day Absolute Trend, Long-Short, Binance USD-M Futures, Pure OHLCV, Weekly Rebalance)
Hypotheses
A LONG-SHORT, MULTI-INSTRUMENT LONG-HORIZON TREND-FOLLOWING basket across ~10 confirmed-full-history Binance USD-M major perpetuals, holding each asset LONG when it is in a long-horizon (~200-day / 40-week) uptrend and SHORT when in a long-horizon downtrend, equal-risk weighted. It captures the ONE validated crypto edge -- MOMENTUM/CONTINUATION (cross-sectional momentum is positive and in optimization; every fade/reversion was catastrophically anti-predictive) -- at the LONG-HORIZON timescale that the portfolio's medium-horizon momentum strategies (60-day cross-sectional / absolute / dual / nearness-to-high baskets) do not cover. Long-horizon trend (Faber 2007 / managed-futures) is empirically a DISTINCT factor from medium-horizon momentum: it has a different return-autocorrelation structure, much LOWER turnover (fewer, longer-held positions), and stronger structural bear-market avoidance -- not a re-parameterization of the shipped baskets. It also fills the single most under-represented portfolio horizon bucket (macro_1w_plus, ~0.3%). EVERY design choice respects this session's binding lessons: (1) MOMENTUM direction -- the only edge that works. (2) FUTURES venue -- SPOT strategies repeatedly hit a recurring sizing/accounting bug; USD-M futures size cleanly and allow the short leg. (3) PURE OHLCV, NO supplementary/funding -- funding_rates ends ~2024-12 (empty holdout) and all supplementary-stream attempts died on data coverage; this uses only price. (4) MAJORS-ONLY CONFIRMED-DATA universe -- single-asset/pattern transfers (ADA 3-bar 0-for-8) and thin-alt/HL baskets all died on data/transfer failure; this uses only 2020-listed full-history majors with a drop-and-continue guard. (5) DIVERSIFIED BASKET -- the robust form; across ~10 names a slow trend still produces a distributed multi-year trade stream (basket density compensates for the long horizon, avoiding the event-sparsity / trade-starvation killers). (6) NET-DIRECTIONAL crash protection -- net-long in broad uptrends, net-short in broad downtrends. (7) LET-TRENDS-RUN exit (flip on long-horizon trend reversal), not small-TP/big-SL. Intentionally minimal: 3 tunables (trend_lookback, rebalance_days, vol_lookback).
Hypotheses
Iteration 3 targets the reported Layer-3 sandbox timeout, and I measured the cause instead of guessing at it. I reran the previous code through the same subprocess runner, venue and 2400-day/1-DAY smoke window the sandbox uses: 10 legs / band 0.25 = 94.5s and 89.0s across two runs (969 orders); 10 legs / band 1.0 = 89.0s (313 orders); 6 legs / band 1.0 = 88.8s; and a single-instrument strategy that places ZERO trades over the same window = 50.9s. So ~51s of every run on this box is fixed engine + catalog + metrics overhead, and neither leg count nor order count moves the total materially - the strategy has no per-bar work that scales with history (buffers are O(1) appends, bounded to trend_lookback+2, and the rebalance is O(legs) on a weekly grid). The 300s failure was CPU contention: the box was running 11-17 concurrent backtest subprocesses during my measurements, which is the same starvation the pipeline notes have been fixing. Given that, I made the two changes that genuinely cut this strategy's own cost and improve it, and left the passing signal logic untouched: (1) rebalance_band default 0.25 -> 1.0, which drops orders 969 -> 313 (-68%) because a leg now only trades on a trend sign FLIP, a new arm, or a close, instead of being re-marked to equity drift every week. This is also the more faithful reading of the hypothesis (much LOWER turnover, let-trends-run) and it cut fee drag enough to move the smoke-window result from -14.7% total return / -7.2% avg trade to +41.6% / +4.1%. (2) The rebalance schedule is now CALENDAR-anchored - the period index is int(bar.ts_init) // (86400e9 * rebalance_days) and a rebalance fires on the first bar of each new period - replacing the process-relative bar counter, which re-anchored on every restart, drifted off the fixed grid and made backtest/paper replay unmatchable. I also declared _param_bounds for every parameter the code clamps so the optimizer never searches values the code silently rewrites. One further data point landed after the first three: the SUBMITTED code (10 legs, band 1.0, calendar anchor) ran the same window in 203.1s while the box had 17 concurrent backtest subprocesses, versus 89.0s for byte-identical work at 11 - the wall clock tracks concurrency, not the strategy. It still finished inside the 300s cap under the worst load I could observe (266 trades, +58.8% total return, Sharpe 0.43, avg trade +4.06%), but the margin is load-dependent. If Layer 3 still times out, the remaining lever is infrastructure (concurrent-slot pressure or SF_SANDBOX_BAR_BUDGET), not per-bar strategy work - the 51s zero-trade baseline is the floor no strategy code can go under.
Hypotheses
Negative expectancy on a reliable 273-trade / 6.7-year sample: profit_factor 0.962 (<1), avg_trade_pnl_usd -39.30, expectancy -$39.30/trade, CAGR -2.19%. The +48.2% total_return is a mark-to-market artifact — end_unrealized_pct 58.9 means the ENTIRE headline is un-exited open positions marked to the last bar; the realized record is negative. The high equal-weighted avg_trade_return_pct (3.03%) is contradicted by the negative size-weighted USD expectancy: losing legs carry larger notional. Sharpe 0.35 with CI [-0.43, 1.08] straddling zero, losing in 3 of the last 5 full years (2025 -38.3%), and the edge confined to the normal-vol tercile confirm no robust gross edge. Commissions are only 1.18% of gross, so this is not fee-fragility — the long-horizon net-directional trend signal is gross-negative on majors, and optimizing the 3 tunables cannot invert a PF<1 signal. This is the multi-instrument long-short basket signature (L45/L63, multi_instrument ~0/84) whose headline return is beta/leverage MTM, not per-name edge. Not worth 2 hours of optimization. abandon_class=negative_expectancy.
Implementation
Long-short long-horizon (Faber / managed-futures) absolute-trend basket across 10 full-history Binance USD-M majors (BTC, ETH, BNB, XRP, ADA, SOL, DOGE, LTC, LINK, BCH) on 1-DAY bars. Each leg is held LONG when its own ~200-day trailing return is positive and SHORT when negative, sized equal-RISK by inverse realized volatility (vol_target / std(20d returns), clamped to [0.3, 1.5]) and capped so aggregate gross never exceeds 1x equity. Because the signal is ABSOLUTE (own-trend) rather than cross-sectional, the book is net-directional: net-long in broad uptrends, net-short in broad downtrends, which is the structural crash protection. Positions move to target through a single netting delta order per leg on each calendar rebalance date; with the default no-trade band of 1.0 a leg trades only when its trend sign FLIPS, when it is newly armed, or when its target goes to zero, so trends are allowed to run and turnover stays at a few flips per name per year. Pure OHLCV, no supplementary data, leverage 1.0.
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 BTCUSDT.BINANCE-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).
Verification Results
Optionally revise the hypothesis text to say '200-day trend lookback, weekly rebalance, on daily bars' to stop the heuristic from re-flagging it. No code change required.
Verification Results
The Layer 1.5 consistency check flagged the hypothesis title ('~200-Day / 40-week / Weekly Rebalance') against config.bar_type=1-DAY as a possible timeframe contradiction. On inspection this is a FALSE POSITIVE: the hypothesis specifies a ~200-DAY trend LOOKBACK and a WEEKLY REBALANCE cadence, not weekly bars. The code correctly uses 1-DAY bars with trend_lookback=200 (days) and rebalance_days=7 (weekly grid). A weekly-bar implementation would make the 200-lookback span 200 weeks and would be the actual mismatch. No change needed; noting so the warning is not re-raised as blocking.
Verification Results
No action needed; the override is deliberate and respected by the base.
Verification Results
min_bars_required(self) overrides a base framework method (static analyzer base_shadow warning). Verified against base_template.py line 412: the base DOES call self.min_bars_required() to gate primary processing, so the override (returns max(trend_lookback,vol_lookback)+2 = 202) is correctly honored and is actually beneficial (ensures 200-day warmup). calculate_signal also independently guards with an internal length check, so even a mis-wired warmup would not cause a look-ahead. Benign.
Verification Results
Sandbox is positive and coherent (total_return +58.8%, PF 1.21, avg_trade_return_pct +4.06% over 266 trades, avg hold 74d), but risk-adjusted edge is modest and regime-dependent: Sharpe 0.43 with CI [-0.34, 1.18] straddling zero, and regime_performance shows the edge lives almost entirely in the 'normal' vol tercile (+82.2%, Sharpe 1.43) while both 'calm' (-8.3%) and 'stressed' (-5.5%) terciles are negative. Max drawdown 39% (CI to 65%) and a 699-day underwater span. This is a single unoptimized draw over a truncated window, so it does not block — but the analyst should confirm the long-horizon trend factor survives outside the normal-vol regime and clears the medium-horizon momentum baskets it claims to be distinct from before spending optimization time.
Backtest Review
Well-formed diversified basket on full-history majors; adequate 273-trade / 6.7-year sample (metrics_reliable=true)
Backtest Review
Trades implement the hypothesis (139 long / 134 short net-directional legs, ~72d holds — the low-turnover let-trends-run design is faithfully coded)
Backtest Review
avg_trade_return_pct 3.03% clears the fee floor on an equal-weighted basis; commissions only 1.18% of gross, so this is NOT a fee-drag death
Backtest Review
Negative gross edge: profit_factor 0.962 (<1), avg_trade_pnl_usd -39.30, expectancy -$39.30/trade — size-weighted USD expectancy is negative because losers carry larger notional than the positive equal-weighted avg_trade_return_pct suggests
Backtest Review
The +48.2% headline is a mark-to-market illusion: end_unrealized_pct 58.9 means the entire headline is un-exited open positions at backtest end; realized track record is negative and CAGR is -2.19%
Backtest Review
Sharpe 0.35 with CI [-0.43, 1.08] straddles zero; loses in most calendar years (2021 -26.7%, 2024 -3.6%, 2025 -38.3%) and the edge lives only in the normal-vol tercile (calm -14.5%, stressed +9.8% but normal +56.8%) — QA's regime concern borne out
Backtest Review
Long-horizon trend as a distinct factor is not demonstrated: benchmark_meaningful=false, and the strategy does not beat simply holding the basket on a realized basis
Analysis
Backtest gate:
- metrics unreliable: 261 trades / 2437 data days is too little evidence (need >=5 trades over >=10 days) — increase signal frequency or widen the window
Backtest and paper results are hypothetical. Trading involves risk of loss.