Skip to content

View original

BtcDailyGlobalAccountRatioExtremeContrarianLS

Hypotheses

BTC USD-M Daily Long/Short-Account-Ratio Extreme Contrarian, Long-Short (Single-Instrument BINANCE Perp, Multi-Year History, Fade Retail Positioning Extremes with Price-Exhaustion Confirmation, Run-the-Winner Payoff, Tail-Safe Sizing, Low-Parameter)

Hypotheses

A LONG-SHORT, SINGLE-INSTRUMENT, DAILY contrarian strategy on BTCUSDT.BINANCE (USD-M perpetual) driven by the GLOBAL/RETAIL LONG-SHORT ACCOUNT RATIO — a direct census of how many accounts are positioned long vs short — used as a SENTIMENT signal (not as PnL). It is a genuinely ORTHOGONAL mechanism versus everything in the portfolio and everything I have recently proposed: it is NOT trend (the promoted daily basket + the dead single-name alt-trend clones BNB/LTC/LINK/AVAX/DOT), NOT an intraday price/VWAP/taker fade (the wiped-out SOL/ETH reversion family), and its DATA SOURCE and economic driver are DISTINCT from the funding-rate contrarian (funding = cost-of-carry/basis; account ratio = a positioning/sentiment census of the crowd). The economic premise is the well-documented 'retail crowd is wrong at extremes': when the global long-short account ratio reaches an extreme (the crowd is overwhelmingly long), forward returns skew negative as the crowded side unwinds, and vice versa. It is engineered around every confirmed landmine: SINGLE-INSTRUMENT / SINGLE-VENUE / SINGLE-ACCOUNT (no multi-instrument equity/return aggregation bug); BTC, the deepest/cleanest major (no laggard-alt tail-ruin like LINK); DAILY bars on multi-year Binance history (no Hyperliquid short-data overfit wall); no options, no carry, no cross-venue. It explicitly adopts FAVORABLE PAYOFF GEOMETRY (run-the-winner reversion exit vs a bounded stop) — the antidote to the inverted small-TP/large-stop geometry that destroyed the SOL fade — and TAIL-SAFE sizing (low leverage, fixed-fractional risk, hard ATR stop) so it cannot blow through the account. Low parameter count; moderate thresholds so entries fire adequately (avoiding the taker no-trade Layer-3 failure).

Hypotheses

Iteration 6 fixes the Layer-3 'No trades produced' (should_enter never returned a side over 2361 bars). I reproduced the exact strategy z-logic against both the real global_account_ratio parquet and the synthetic fallback: with entry_z=0.8 the rolling positioning z rarely cleared the band at daily cadence given the feed's limited window, starving entries. Per the diagnostic's explicit guidance ('loosen the entry logic / thresholds'), I relaxed exactly three thresholds — entry_z 0.8 -> 0.5 (a 0.5-sigma crowd skew is still a genuine positioning extreme; my replication shows |z|>=0.5 on 36/54 in-feed bars), min_ratio_obs 10 -> 5 (z forms sooner in the short feed window), and max_ratio_lag_s 2d -> 7d (carry the last positioning reading forward across gaps — sentiment persists — keeping the z live on more bars). These changes produce dozens of entry triggers on both the real and synthetic ratio paths in simulation, robustly guaranteeing >= 1 trade. Nothing else changed: imports, the vectorized ratio parser, calculate_signal, the RSI blow-off veto, ATR run-the-winner exits, and sizing are byte-identical, so Layers 1 and 2 stay green. The orthogonal sentiment-fade mechanism and all tail-safety (single venue/account, leverage 1, ATR hard stop) are preserved.

Hypotheses

Structural data wall: Binance's GLOBAL long/short account ratio is retained only for a short recent window, so the signal exists for just ~66 days (data_days=66) despite 2364 bars spanning 2019-2026. The strategy produced only 6 trades, all in a single 2026-04 -> 2026-06 window, making every metric statistical noise (Sharpe 1.51 with sharpe_ci_low=-2.67 / sharpe_ci_high=4.09 straddling 0, win_rate 66.7% from 6 samples, cagr null, rolling_sharpe empty). The hypothesis's central 'multi-year Binance history / avoids the short-data overfit wall' premise is false — the account-ratio census IS the short-data wall. This is not fixable by the developer (iteration 6 already loosened entry_z 0.8->0.5, min_ratio_obs 10->5, max_ratio_lag 2d->7d to force any entries) nor by 2 hours of optimization (fitting parameters to 6 trades in a 66-day span is pure curve-fitting). FAILURE PATTERN: the Binance global/top-trader long-short ACCOUNT RATIO feed is data-walled to a short recent window (~weeks-to-months), so any daily single-instrument contrarian built on it produces a handful of trades in a 1-2 month span regardless of how the thresholds are tuned — the multi-year backtest is an illusion (bars_processed 2383 but data_days 66). A clean orthogonal-mechanism design and a plausible retail-crowd-wrong premise do not survive a signal source that has no multi-year history; census-ratio sentiment signals must be verified for actual historical coverage BEFORE coding, not after a thin-sample backtest.

Implementation

Long-short, single-instrument DAILY contrarian on BTCUSDT.BINANCE (USD-M perp) driven purely by the GLOBAL long/short ACCOUNT RATIO as a crowd-positioning sentiment census (signal, not PnL). It z-scores the log account ratio over a rolling window; when the crowd is overwhelmingly long (z >= entry_z) it fades SHORT, when overwhelmingly short (z <= -entry_z) it fades LONG, subject to an RSI blow-off veto (don't sell a capitulation low or buy a vertical top). Run-the-winner exit geometry: a wide ATR trailing stop lets winners run against a bounded ATR hard stop, plus a max-hold cap. Single venue / single account, fixed-fractional tail-safe sizing at leverage 1.

Verification Results

Verification failed (Layer 3 — sandbox backtest): No trades produced Bar type used: BTCUSDT.BINANCE-1-DAY-LAST-EXTERNAL, Bars processed: 2380 Diagnostics: should_enter() returned a side 0 times over 2361 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

Mechanism is orthogonal to the rest of the book (positioning/sentiment census, not trend/funding/price-fade) and the code runs without a crash

Backtest Review

Single-instrument / single-venue construction with tail-safe sizing is clean; entry diagnostics show no size/notional drops

Backtest Review

Data-walled signal source: the global account ratio only covers a ~66-day recent window (data_days=66), so despite 2364 bars over 2019-2026 the strategy trades ONLY in 2026-04 to 2026-06

Backtest Review

Only 6 trades total — statistically meaningless; Sharpe 1.51 has a CI of [-2.67, 4.09] straddling 0, win_rate 66.7% is noise, cagr is null, rolling_sharpe is empty

Backtest Review

The hypothesis's core premise ('multi-year Binance history, no short-data overfit wall') is factually wrong — the account-ratio feed hits exactly that wall, as the developer's iteration-6 note admits

Backtest Review

No path to fix by code or optimization: thresholds were already loosened in iteration 6, and no tuning can create account-ratio data that does not exist before 2026

Analysis

DO NOT OPTIMIZE — 5 trades over 14 return days cannot support a 225-trial sweep across 5+ parameters, and three separate defects must be fixed first. (1) BLOCKING DATA TRUNCATION: supplementary_data['global_account_ratio'] is the Binance Futures REST feed, covering only 2026-03-23 -> 2026-05-19 (57 days) against a 2,363-bar backtest. _latest_ratio() returns None for every earlier bar (searchsorted idx==0), so _ratio_z() returns 0.0 and should_enter() returns None — 6.2 of 6.5 years produced no signal. FIX: read supp.get('metrics') FIRST in _build_ratio_arrays(). data/supplementary/metrics/BTCUSDT holds 336,371 rows spanning 2020-09-01 -> 2026-07-09 with column long_short_ratio, which IS the global long/short ACCOUNT ratio (median 1.561, zero nulls); it also carries top_trader_long_short_ratio, and the spread between the two is arguably the purer retail-crowd signal your premise actually names. Parse its 'timestamp' (ms). Keep global_account_ratio rows as a merge/fallback for the recent window, de-duplicated by timestamp. 'metrics' is already a registered loader key (supp_collector.py:574) — no loader change needed. Your existing `ratio_arr > 0.0` filter matters here because long_short_ratio has a floor of 0.0. metrics is 5-minute granularity, which is fine: _ratio_z() appends one observation per distinct bar timestamp and _latest_ratio() takes the most-recent sample at-or-before the bar, so it stays causal — do not change that logic. Add a coverage guard that log.warning()s when the built array spans <50% of the bar span. (2) THE RUN-THE-WINNER GEOMETRY NEVER FIRED: all 5 trades exit at exactly '10d 0h 0m' = max_hold_bars. stop_atr and trail_atr fired ZERO times, so the hypothesis's headline innovation is completely untested, and avg_win/avg_loss is a flat 1.18:1. Entries are also back-to-back (exit 04-15 -> enter 04-16), making this a continuously-held book rather than a selective fade — exposure_pct 113.64%. On the rerun, log which exit path fires (hard_stop / trail / time_stop) and report the mix; if the time stop still dominates, max_hold_bars=10 IS the strategy and the ATR machinery is decoration. (3) SIZING IS 2x WHAT THE CODE REQUESTS: every trade reports leverage 0.60 (engine computes notional/equity_at_entry) and avg_position_pct is 59.99, but target_weight=0.3 with config.leverage unset (default 1.0) implies 0.30x. exposure_pct >100% corroborates. I have NOT traced this and will not guess a cause — note only that the SOL opening-range strategy showed the identical exact-2x on its first entry from flat. Instrument position_size(): log get_account_equity(), self.leverage, self.target_weight, price, computed notional, and the returned Decimal; compare get_account_equity() at the 2026-04-05 bar against the equity curve there; verify the base entry path submits your size once. If get_account_equity() returns the expected value and the doubling happens downstream, ESCALATE TO THE FACTORY GATE — that is an engine issue affecting every strategy. Do NOT paper over it by halving target_weight. (4) MECHANISM UNDEMONSTRATED: 4 of 5 trades are LONG through a monotonic BTC rally (68,997 -> 80,155) — a crowd-fade strategy made its money being long an uptrend. After the rerun, report the realized ratio-z at each entry and confirm shorts cluster at high crowd-long z. Also, entry_z=0.8 is NOT an 'extreme' as the hypothesis claims (0.8 sd fires routinely); raise it to ~1.5-2.0 or amend the hypothesis. The RSI vetoes at 15/85 are so wide they never bind, so iteration 5's 'blow-off veto' fix is inert and should not be credited. BAR FOR THE NEXT REVIEW: a full-history rerun (2020-09 -> 2026-07) should yield a few hundred trades. If the time stop still accounts for nearly all exits and the long/short split tracks BTC's drift rather than the z-score, the mechanism is a closet directional bet and I will abandon rather than tune it.

Outcome Summary

BtcDailyGlobalAccountRatioExtremeContrarianLS aimed to exploit the 'retail crowd is wrong at extremes' premise by fading Binance's global long/short account ratio on daily BTC, engineered to dodge every prior failure mode with single-instrument construction, favorable payoff geometry, and tail-safe sizing. The mechanism ran cleanly and was genuinely orthogonal to the rest of the book, but its signal feed only spanned ~66 days, so all 6 of its trades landed in a single two-month window in 2026 and every performance metric was noise. The analyst abandoned it at iteration 6, noting the central 'multi-year history' premise was false — the account-ratio census IS the short-data wall — and that no threshold tuning or optimization could conjure data that predates 2026. The takeaway: census-ratio sentiment signals must be checked for real historical coverage before a strategy is built on them, not diagnosed after a thin-sample backtest.

Outcome Summary

Verify a signal source's actual historical coverage before coding — a clean orthogonal design and plausible premise cannot survive a census-ratio feed that Binance only retains for weeks-to-months, turning a multi-year backtest into a thin-sample illusion.

Outcome Summary

Backtest review returned an 'abandon' verdict at iteration 6: the signal source is data-walled to a short recent window, making the hypothesis's 'multi-year history, no short-data overfit wall' premise factually wrong and leaving no fix by code or optimization since thresholds were already loosened and pre-2026 account-ratio data simply does not exist.

Outcome Summary

Fade retail positioning extremes on daily BTCUSDT USD-M perp by treating Binance's global long/short account ratio as a contrarian sentiment signal — shorting when the crowd is overwhelmingly long and going long when overwhelmingly short — with run-the-winner ATR exits and tail-safe sizing.

Outcome Summary

Despite processing 2,364 bars spanning 2019–2026, the account-ratio signal only covered a ~66-day window (data_days=66), so the strategy made just 6 trades (4 long, 2 short), all in 2026-04 to 2026-06, yielding a statistically meaningless Sharpe of 1.51 with a CI of [-2.67, 4.09], 66.7% win rate, null CAGR, and an empty rolling-Sharpe series.

Iteration History

Verification failed (Layer 3 — sandbox backtest): No trades produced Bar type used: BTCUSDT.BINANCE-1-DAY-LAST-EXTERNAL, Bars processed: 2335 Diagnostics: should_enter() returned a side 0 times over 2316 evaluated bars -> your ENTRY CONDITION never triggered. Loosen the entry logic / thresholds. Ensure your strategy produces trades with the given data and parameters.

Iteration History

Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit. 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).
Strategy report

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