EthTopTraderLongShortRatioContrarianFade4H
Hypotheses
ETH Top Trader Long/Short Ratio Contrarian Fade on 4H
Hypotheses
A simple long-and-short contrarian strategy on ETHUSDT perpetual futures using 4-hour bars and Binance's top-trader long/short account ratio as the sole signal. Binance publishes per-symbol L/S ratios computed from the top 20% of traders by collateral — a strong proxy for sophisticated positioning. The hypothesis is that when top-trader positioning becomes extreme in either direction, the trade is overcrowded and mean-reverts within 12-48 hours. The strategy is deliberately minimalist: one instrument, one supplementary data input (L/S ratio), one timeframe, four parameters total. Risk profile: 1.5% capital risk per trade with stronger-signal scaling (2% risk when ratio > 2.5 or < 0.5), fixed 1.5% adverse-move stop, 2% take-profit target, max 25% equity per position. The 4H timeframe + 12-48h hold window gives ~3-6 trades per month — low enough that the 0.10% RT fee is well below the 2% target per trade. Distinct from the existing SOL funding contrarian (uses funding_rate, 1H, single-side short) by using L/S ratio data, 4H timeframe, and bidirectional entries.
Hypotheses
Iteration 3 fixes the sole Layer-3 blocker ('No trades produced'). Root cause found by inspecting the real data path: the top_account_ratio loader returns parquet rows via df.to_dict('records'), so the strategy receives the RAW column names long_short_ratio / long_account / short_account / timestamp_ms, not the documented longShortRatio / longAccount / shortAccount that the Layer-2 synthetic generator emits. The previous _ensure_supp read only the documented keys, so on real data every row was skipped, the log-ratio buffer never filled, _zscore always returned None, _z_valid stayed False, and should_enter returned None on all 14210 bars. The fix reads BOTH key spellings in _ensure_supp (documented keys first — keeping Layer 2 green since the synthetic generator uses them — then the raw parquet keys), plus a NaN guard. No imports, signal math, thresholds, exits, or sizing changed — the smallest fix that preserves every previously passing layer. Verified against the actual ETH top_account_ratio parquet: over its ~56-day window the corrected parsing yields 276 computed z-scores and 33 entry-eligible bars at the default entry_z=2.0 (z range -2.35 to +2.82), comfortably exceeding the >=1 trade Layer-3 requirement.
Hypotheses
The strategy is structurally unbacktestable over the required horizon: although the full 2019-2026 bar history was processed (14,214 bars), the top_account_ratio supplementary stream appears to have almost no historical coverage, so a valid z-score (z_min=30 over a 120-bar 4H baseline) only forms in the last ~2 months. Result: just 9 trades across ~12 data_days, all in 2026-04/05, net-losing. This is a data-history limitation, not a parameter or code defect the developer can fix by editing the strategy (the L/S-key parsing works — it produces trades wherever data exists). The 3-phase optimizer needs multi-year data to populate walk-forward and holdout; 9 recent trades would only curve-fit noise. Abandon as delivered. If a longer-history top-trader L/S-ratio / Binance 'metrics' stream can be backfilled by the data engineer (multi-year depth), the fade mechanism could be revisited then — but that is a data-sourcing task, not an optimization or developer-iteration step.
Implementation
Long-short contrarian mean reversion on ETHUSDT.BINANCE 4H bars driven solely by Binance's top-trader long/short account ratio (top_account_ratio). It z-scores the log L/S ratio over a rolling baseline; when top-trader positioning is extreme (z >= +entry_z it fades short, z <= -entry_z it fades long), expecting reversion within 12-48h. Exits on z reverting inside +/-exit_z, a 1.5% stop, 2% take-profit, or a 12-bar time stop. Risk 1.5% of equity (2% on raw-ratio extremes), capped at 25% equity notional. Stays flat when L/S data is absent (no price-only fallback).
Verification Results
Verification failed (Layer 3 — sandbox backtest): No trades produced
Bar type used: ETHUSDT.BINANCE-4-HOUR-LAST-EXTERNAL, Bars processed: 14214
Diagnostics: should_enter() returned a side 0 times over 14210 evaluated bars -> your ENTRY CONDITION never triggered. Loosen the entry logic / thresholds.
Ensure your strategy produces trades with the given data and parameters.
Backtest Review
Code matches the hypothesis this time (ETH L/S-ratio contrarian fade, 4H, bidirectional), and the supplementary-key parsing fix works where data exists
Backtest Review
Minimalist, sensible design: single instrument, single signal, 4 core params, low fee footprint per trade
Backtest Review
Grossly insufficient sample: only 9 trades over ~12 data_days, all clustered in 2026-04/05 despite 14,214 bars processed across 2019-2026
Backtest Review
The top_account_ratio supplementary stream has almost no historical depth (funding_events_available=40) — the z-score baseline only becomes valid in the last ~2 months, so the full-history backtest is empty until recently
Backtest Review
What little sample exists is net-losing (total_return -1.95%, Sharpe -7.0, profit_factor 0.39, expectancy -$216, win_rate 0.33) — not evaluable but not encouraging
Backtest Review
Optimization requires multi-year walk-forward + holdout; 9 recent trades cannot support WF-OOS or DSR — the optimizer would curve-fit noise
Outcome Summary
EthTopTraderLongShortRatioContrarianFade4H proposed a clean, four-parameter contrarian fade of extreme top-trader L/S positioning on ETH 4H, and by this third iteration the code correctly parsed both the synthetic and real-data key formats. But the top_account_ratio supplementary stream had almost no historical depth, so despite processing seven years of bars the z-score baseline only became valid in the final ~2 months, producing just 9 net-losing trades over ~12 data-days. The analyst abandoned it at backtest review before any optimization, judging it a data-sourcing limitation rather than a code or parameter defect. If a multi-year top-trader L/S history is ever backfilled by the data engineer, the fade mechanism could be revisited — but as delivered it simply could not be evaluated.
Outcome Summary
A sound, hypothesis-aligned signal is worthless if its underlying supplementary data lacks multi-year historical depth — verify the data stream's historical coverage before committing to a strategy that depends on it as the sole input.
Outcome Summary
The analyst abandoned it at the pre-optimization BACKTEST_REVIEW gate (verdict: abandon; optimization never ran) because the strategy is structurally unbacktestable over the required horizon — the shallow supplementary-data history makes the signal valid only recently, and 9 trades cannot support walk-forward or holdout validation without curve-fitting noise.
Outcome Summary
A minimalist long-short contrarian mean-reversion strategy on ETHUSDT 4H perp futures that z-scores Binance's top-trader long/short account ratio and fades extreme positioning — shorting crowded longs and buying crowded shorts, expecting reversion within 12–48 hours.
Outcome Summary
Although all 14,214 bars from 2019–2026 were processed, a valid z-score only formed in the last ~2 months (top_account_ratio had just 40 events of history), yielding only 9 trades over ~12 data-days clustered in April–May 2026, which were net-losing: -1.95% total return, Sharpe -7.04, profit factor 0.39, 33% win rate, and -$216 expectancy per trade.
Iteration History
Verification failed (Layer 2 — synthetic scenarios):
Parameters used: ['z_min', 'exit_z', 'tp_pct', 'entry_z', 'risk_pct', 'stop_pct', 'ls_window', 'smooth_bars', 'min_notional', 'max_hold_bars', 'ls_tolerance_s', 'risk_pct_strong', 'max_notional_pct', 'ratio_strong_low', 'ratio_strong_high']
Check that __init__ sets all attributes from self.parameters.get().
- steady_uptrend: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735689840000)
- steady_downtrend: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735689840000)
- flat_ranging: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735689840000)
- volatility_spike: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735689840000)
- zero_volume: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735689840000)
- price_gap: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735689840000)
Backtest and paper results are hypothetical. Trading involves risk of loss.