Skip to content

View original

MultiLookbackEnsembleTrendLongShortBasketNoLeverage

Hypotheses

Binance USD-M Multi-Lookback Ensemble Trend Long-Short Basket (Established Majors, Frozen Lookback Fan 20/40/80/160, Averaged Trend Score, Weekly Rebalance, Overfit-Resistant)

Hypotheses

A long-short, daily managed-futures trend basket across ~10 established, confirmed-full-history Binance USD-M majors (BTC, ETH, SOL, BNB, XRP, DOGE, ADA, LTC, LINK, BCH), designed explicitly to be OVERFIT-RESISTANT BY CONSTRUCTION — the analyst-prescribed fix for why the vol-targeted dual-timeframe variant was abandoned. That variant had a genuine, strong edge (+392%, Sharpe 2.59 at defaults) but the walk-forward optimizer OVERFIT it by tuning a single fast_lookback (20→13), spiking in-sample while out-of-sample collapsed to a negative holdout. The fix here removes the exploitable parameter entirely: instead of one tunable lookback, the trend signal for each asset is the AVERAGE of trend signs computed at a FROZEN FAN of lookbacks (20, 40, 80, 160 days). There is no single lookback for an optimizer to over-concentrate on — the fan is fixed, so the IS/OOS overfit-collapse mode is structurally defused, and the strategy is built to optimize for OOS robustness, not max-IS Sharpe. This is the standard CTA multi-horizon robustness technique. It stays strictly inside the ONLY validated edge (DAILY symmetric trend on established majors — the promoted AbsoluteMomentum sibling is Sharpe 1.48; intraday/4H single-bar directional and short-only variants are all refuted), uses pure majors' daily OHLCV (data-safe — broad-alt/HL baskets die on data), and is genuinely distinct from every placed single-signal basket (it is a multi-horizon ENSEMBLE, not one lookback / breakout / rank). Long-short and symmetric improves the 87%-long skew; weekly rebalance keeps fees trivial; the near-empty tunable surface resists the overfitting that has been the recurring strategy-level killer.

Hypotheses

Iteration 2 fixes exactly the two QA code defects and nothing else. (1) Calendar anchoring: the weekly cadence no longer uses the process-relative `_rebal_count` counter (checklist #18 anti-pattern) — it is now a pure function of the bar's own timestamp, `day_index = ts // 86_400_000_000_000`, firing when `day_index % rebalance_days == 0` on a day not yet rebalanced. The grid is therefore identical across restarts, walk-forward windows, the holdout and paper replay, curing the parity/grid-drift harm. (2) Strict per-asset cap: `min(raw/total, max_weight)` followed by renormalization could push capped names back above the cap; weights are now built by water-filling (breaching names pinned AT the cap, residual re-proportioned among the rest), verified to yield max weight exactly 0.20 and gross <= 1.0 in every feasible and infeasible configuration. The edge_concern (drawdown / impact fragility) was flagged as an analyst question, not a code defect, so the signal, lookback fan, vol floor, sizing formula, order flow and universe are unchanged.

Hypotheses

Corrupted, account-destroying sizing/netting blowup on top of a negative-edge directional basket — decisive abandon, not worth 2 hours. The equity curve is incoherent: CAGR -100% (recovery_factor -1.0, account went to zero) alongside a reported total_return of +1883%, with physically impossible single-day returns of -481.7% (2023-08-17), -206% (2023-07-13) and -198.9% (2024-10-24) and return_kurtosis 211 — a -481% day means the book went deeply negative. The strategy's core 'no-leverage, gross_cap 1.0' invariant is violated (exposure_pct 789%, end_unrealized_pct 849%, annualized_volatility 107%): the delta-resize/netting path accumulates positions far past the cap, and this is iteration 2 of exposure-control fixes that still failed. Long dead stretches (2023-08 → 2024-04 all 0.0) confirm the account blew out. Even setting the artifact aside, the underlying edge is negative (avg_trade_return_pct -3.5%) in the L45/L52 directional LS trend-basket family, with max_drawdown 63% (CI to 99%), 31 max consecutive losses, and mostly negative stress windows. No optimization can run on a PnL destroyed by a netting defect, and a fixed version would still be a negative-per-trade basket in a zero-survivor class. Failure pattern: broken sizing/netting blowup + no_edge directional LS trend basket (L45/L52).

Implementation

Long-short daily managed-futures trend basket across 10 established Binance USD-M majors (BTC, ETH, SOL, BNB, XRP, DOGE, ADA, LTC, LINK, BCH). Each asset's trend score is the AVERAGE of trend signs over a FROZEN fan of lookbacks (20/40/80/160 days), so there is no single lookback for the optimizer to over-concentrate on. Weights are conviction-weighted equal-risk (|score| / floored realized vol), strictly capped per asset at 20% by water-filling and normalized so gross exposure never exceeds 1.0x equity (no leverage, no vol-target scalar). Direction is sign(score); rebalance is weekly on a calendar-anchored grid (days-since-epoch modulo 7), with material-delta resizing to keep turnover and fees low.

Verification Results

Verification failed (Layer 4 — QA review) [class=code_defect]: - [edge_concern] Sandbox max_drawdown 86.45% (CI to 96.6%) — extreme even for a gross-1.0, no-leverage long-short basket, well past the >50%-DD hard-abandon lens. Sizing is genuinely gross-capped at 1.0x (weights renormalize to sum 1; avg_position_pct 18.9%), so it's regime-bleed, not a leverage blow-up — but severe. Compounded by impact_cost_pct 14.77% folded, capacity only ~$4.6M, turnover 55.6, so the net edge is impact-fragile. total_return +55.9%, Sharpe 0.33 (CI straddles zero). The overfit-resistant construction is sound in principle, but the drawdown and impact fragility are the real full-history questions for the analyst. - The weekly rebalance cadence is driven by a process-relative bar counter (self._rebal_count += 1; if (self._rebal_count - 1) % self._rebalance_days == 0: self._rebalance()), with _extract_ts used only to detect a new bar, never to anchor the schedule — the uptime-anchored anti-pattern (checklist #18). On a live restart _rebal_count resets to 0 and the first bar rebalances immediately, re-anchoring the weekly grid to the restart moment and drifting rebalance dates off any fixed calendar grid. Consequence: backtest/paper/live parity failure (a run started on a different day holds a different book at any given time). The target book is deterministic so restart-churn is bounded, but the grid-drift/parity harm fully applies. Backtest is deterministic (valid sandbox), but the schedule is not live-safe or replay-stable. - Weight capping does min(raw/total, max_weight) then renormalizes to sum 1, which can push some assets back above max_weight (soft cap). Gross stays 1.0x, so not a correctness failure, but the per-asset cap is not strictly enforced.

Backtest Review

Overfit-resistant frozen-lookback-fan concept is sound in principle; stays in the validated daily-trend-basket envelope

Backtest Review

Clean data (10 full-history majors); calendar-anchored schedule fix is correct

Backtest Review

Corrupted blowup backtest: CAGR -100% (account destroyed) with recovery_factor -1.0, yet total_return +1883% — irreconcilable

Backtest Review

Physically impossible single-day returns (-481.7%, -206%, -198.9%; kurtosis 211) — the account went deeply negative

Backtest Review

'No-leverage gross_cap 1.0' invariant violated: exposure_pct 789%, end_unrealized_pct 849%, vol 107% — the delta-resize/netting path accumulates far past the cap despite two prior exposure-control iterations

Backtest Review

Negative underlying edge (avg_trade_return_pct -3.5%); L45/L52 directional LS trend-basket family; max_drawdown 63% (CI to 99%), 31 max consecutive losses

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['assets', 'gross_cap', 'vol_floor', 'max_weight', 'resize_frac', 'lookback_fan', 'min_notional', 'vol_lookback', 'qty_precision', 'rebalance_days', 'default_qty_precision'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: MultiLookbackEnsembleTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735699260000) - steady_downtrend: TypeError: MultiLookbackEnsembleTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735699260000) - flat_ranging: TypeError: MultiLookbackEnsembleTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735699260000) - volatility_spike: TypeError: MultiLookbackEnsembleTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735699260000) - zero_volume: TypeError: MultiLookbackEnsembleTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735699260000) - price_gap: TypeError: MultiLookbackEnsembleTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735699260000)
Strategy report

Backtest and paper results are hypothetical. Trading involves risk of loss.