Skip to content

View translation

BtcLiquidationCascadeReversalLS

Hypotheses

BTC USD-M Liquidation-Capitulation Reversal, Long-Short (Single-Instrument BINANCE Perp, Daily Bars + REAL Liquidations Feed, Buy the Day After a Forced-Selling Climax / Fade Short-Squeeze Climaxes, Event-Driven Contrarian, Low-Parameter)

Hypotheses

A LONG-SHORT, SINGLE-INSTRUMENT, crypto-native EVENT-DRIVEN contrarian strategy on BTCUSDT.BINANCE (USD-M perpetual, DAILY bars) that trades the well-documented post-capitulation bounce: when a day prints an extreme cluster of FORCED LIQUIDATIONS, the marginal leveraged holders have already been flushed, so price tends to revert over the following 1-3 days. It is deliberately ORTHOGONAL to everything the factory already has: it is NOT trend/momentum (the promoted daily basket) and NOT a generic price-mean-reversion (which has no edge on majors) — the signal is the REAL historical liquidations feed (a forced-flow event), not price alone. It is explicitly engineered around the engine and edge landmines proven this session: (1) SINGLE-INSTRUMENT / SINGLE-VENUE / SINGLE-ACCOUNT, so it cannot trigger the multi-instrument equity/return aggregation artifact that corrupted every pairs / rotation / fixed-weight-sleeve / cash-carry run; (2) DAILY horizon, avoiding the intraday-bar chop/whipsaw that gave the 15m/1H breakout, impulse, and vol-compression variants negative expectancy on majors; (3) MODERATE percentile thresholds (top ~10-15%, not extreme tails) so the entry FIRES enough times to clear the Layer-3 'must produce trades' gate — directly fixing the prior taker-flow reversion that died with zero trades from over-strict triple-gating; (4) it ENTERS AFTER the cascade (post-climax reversal), not by holding a directional position INTO an event, which is what made the XRP single-alt book uninsurable. BTC is chosen for the deepest, cleanest liquidation history and the lowest single-name news fragility. Low parameter count to resist overfitting.

Hypotheses

Iteration 7 fix for Layer-3 'no trades produced'. Root cause: the strategy's directional breakdown only matched side=='SELL'/'BUY', but the historical liquidations feed uses inconsistent `side` conventions across collectors — the WS stream (liquidation_stream.py:112) writes 'SELL'/'BUY', the REST collector (liquidation_collector.py:55) writes 'long'/'short', and the Layer-2 synthetic generator writes 'LONG'/'SHORT'. Whenever the sandbox DB held 'long'/'short' rows, long_usd and short_usd stayed 0 for every event, so tot<=0 forced calculate_signal to return 0.0 on every bar, _cascade was never set, and should_enter returned a side 0 times — exactly the reported diagnostic. Fix: a new _side_bucket() helper normalizes side.upper() across {SELL,LONG}->long-liquidation and {BUY,SHORT}->short-liquidation, so the directional flow is never silently zeroed. This is the minimal change to the reported failure and cannot regress Layer 1 (imports/structure unchanged) or Layer 2 (still no crash; now also produces directional signals on synthetic 'LONG'/'SHORT' data). Secondarily, I modestly loosened the entry gates in the same more-trades direction to ensure the ~90-day live-accrued liquidation window clears the '>=1 trade' gate: entry_percentile 0.85->0.75, side_dominance 0.65->0.58, min_liq_days 10->8, max_stale_days 2->3. All signal scaffolding, sizing, exits, and the no-lookahead ranking that already passed earlier layers are untouched.

Hypotheses

Structural data insufficiency (not a code defect) — the same wall that abandoned four sibling liquidation-cascade strategies this session. The Binance liquidations feed is live-accrued to ~90 recent days in the DB, so across the 2019→2026 daily-bar window only 3 entries ever fired (all in July 2026), producing an all-losing sample: total_return -1.12%, profit_factor 0.03, expectancy -$370/trade, commissions 218% of gross, metrics_reliable=false. No developer change extends ~90 days of liquidation history, and the code is already clean (single-instrument, no fabricated zeros, no lookahead). With 3 trades the 3-phase pipeline is impossible — walk-forward windows and the 15-day holdout would hold 0-1 trades each, so optimization would fit noise. Recommend the Research Lead stop proposing liquidation-feed-driven strategies (event-driven reversal or otherwise) until >1-2yr of historical liquidation data is collected; until then this entire family is structurally unbacktestable regardless of instrument, timeframe, or direction.

Implementation

Event-driven post-capitulation reversal on BTCUSDT.BINANCE USD-M perp, DAILY bars, driven by the REAL historical liquidations feed. When the most-recent event day (within max_stale_days) prints a top-tail liquidation-USD cluster (rank >= entry_percentile of its trailing 60-day event-day distribution) that is clearly one-sided (>= side_dominance), it fades the just-completed climax: longs-flushed/forced-selling -> BUY the bounce, shorts-squeezed/forced-buying -> SELL the fade. Single-instrument, single-venue, single-account. Exits on stop-loss, take-profit, or a 2-day time stop covering the 1-3 day reversal window.

Verification Results

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

Verification Results

Analyst: reject as statistically underpowered if real-backtest total_trades < ~30. Research: this hypothesis is fundamentally bounded by ~90 days of liquidation history; no code change raises trade count.

Verification Results

Validatability / trade-count limit. The strategy is only active inside the liquidations feed's ~90-day coverage window (entry requires the latest event day within max_stale_days=3 of the current bar). Over ~90 tradeable daily bars, with 2-day holds and the single-cascade re-entry guard, the entry gate can fire at most ~10-20 times — structurally below the ~100 trades needed to distinguish edge from noise. The sandbox (3 trades, data_days=3, metrics_reliable=false) already reflects a degenerate window. This is a data-coverage constraint the hypothesis acknowledges, not a code defect, so non-blocking — but the analyst must gate on real trade count and treat any Sharpe/PF as non-significant if trades < ~30.

Verification Results

If trade count is adequate, tighten entry_percentile toward 0.85-0.90 in optimization to match the stated climax definition.

Verification Results

entry_percentile=0.75 (top 25%) and side_dominance=0.58 are looser than the hypothesis's stated 'top ~10-15%'. Structure matches (percentile tail on liquidation USD + one-sided dominance); config rationale documents the loosening as a deliberate trade-count fix. Magnitude difference, not a mechanism swap.

Verification Results

Optionally set the re-entry guard on confirmed fill (on_position_opened) rather than in should_enter.

Verification Results

should_enter sets self._last_entry_day before the base template submits the order; a failed submission would still mark the cascade as faded and suppress a retry. Low impact (missed retry, not a wrong trade).

Backtest Review

Code is honestly engineered: single-instrument/venue/account (avoids the multi-instrument aggregation artifact), no fabricated-zero ranking, strictly-before reference day (no lookahead), re-entry guard, robust side normalization

Backtest Review

Daily horizon and moderate percentile thresholds are the right design choices to avoid chop and zero-trade over-gating

Backtest Review

Only 3 trades over a 2019→2026 window (2365 bars seen, 3 signaled) — all clustered in July 2026, the only period with liquidation coverage

Backtest Review

Structural data wall: the Binance liquidations feed is live-accrued (~90 days in DB); the strategy itself documents this — no code change can extend it

Backtest Review

All-losing sample: total_return -1.12%, profit_factor 0.03, expectancy -$370/trade, avg_trade_return_pct -1.37%, commissions 218% of gross; metrics_reliable=false

Backtest Review

Same liquidation-cascade-reversal family has been abandoned 4x this session on this identical ~90-day coverage limit

Analysis

Code↔hypothesis misalignment found by the semantic auditor — the code does NOT implement the hypothesis. Re-code the strategy to implement the hypothesis EXACTLY (instrument, timeframe, direction, the named edge/mechanic, sizing). Concrete issues: Hypothesis specifies DAILY bars in its title and body and lists 'DAILY horizon, avoiding the intraday-bar chop/whipsaw that gave the 15m/1H breakout... variants negative expectancy on majors' as core design principle (2), but config.bar_type is 'BTCUSDT.BINANCE-1-HOUR-LAST-EXTERNAL' and the code operates entirely on hourly bars (spike_window_hours, rank_lookback_hours=168, hold_hours=36 in hours, _NS_PER_HOUR bucketing). The strategy runs on exactly the intraday timeframe the hypothesis said to avoid, contradicting its own stated design principle. (Note: the deviation is deliberately disclosed in the code docstring/rationale due to the ~90-day liquidation-feed coverage limit, but the hypothesis text was never reconciled, so the mismatch is real.)

Outcome Summary

BtcLiquidationCascadeReversalLS was carefully engineered around every landmine proven that session — single-instrument/venue/account to avoid the aggregation artifact, daily bars to dodge intraday chop, moderate percentile thresholds to clear the must-trade gate, and post-climax entry rather than holding into events — with honest code (no fabricated zeros, no lookahead, a re-entry guard, robust side normalization). None of that mattered: the Binance liquidations feed holds only ~90 live-accrued days, so across seven iterations the backtest could only ever fire 3 trades, all in July 2026, for a profit factor of 0.03 and commissions exceeding gross PnL. The reviewer abandoned it as a structural data wall rather than a strategy flaw, noting four sibling liquidation-cascade strategies had already died on the same ~90-day limit this session. The reversal edge was never testable; the recommendation was to halt the whole family until years of liquidation history exist.

Outcome Summary

The entire liquidation-feed-driven family is structurally unbacktestable until 1-2+ years of historical liquidation data is collected — no choice of instrument, timeframe, direction, or gating fixes ~90 days of coverage, so the Research Lead should stop proposing this family until the data wall is removed.

Outcome Summary

The backtest reviewer returned 'abandon' on grounds of structural data insufficiency, not a code defect: the Binance liquidations feed is live-accrued to only ~90 recent days in the DB, so only 3 entries ever fired across the multi-year window, making the 3-phase pipeline impossible — and this was the fourth-plus sibling in the same liquidation-cascade family abandoned this session on the identical coverage wall.

Outcome Summary

A long-short, single-instrument, event-driven contrarian strategy on BTCUSDT.BINANCE USD-M perp (daily bars) that used the real liquidations feed to fade forced-flow climaxes — buying the day after an extreme long-liquidation (forced-selling) cascade and shorting after a dominant short-squeeze (forced-buying) cascade, betting on a 1-3 day post-capitulation reversion.

Outcome Summary

The backtest fired only 3 trades across a 2019-2026 window (2,365 bars seen), all clustered in July 2026 — the only period with liquidation coverage — for an all-losing sample: total return -1.12%, profit factor 0.03, expectancy -$370/trade, avg_trade_return_pct -1.37%, commissions at 218% of gross, and metrics_reliable=false. No optimization stage ran.

Iteration History

BtcDailyLiquidationCapitulationReversalLS

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 2306 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 4 — QA review): - The entry gate does not implement the hypothesis's core mechanic. The hypothesis trades an 'extreme cluster of FORCED LIQUIDATIONS' at 'MODERATE percentile thresholds (top ~10-15%)'. The code computes rank = mean(arr <= today_total), i.e. the percentile rank of the prior day within the trailing window, and enters whenever |signal| >= entry_percentile = 0.60 — that is the top 40% of liquidation days, roughly every other qualifying day. A day at the 61st percentile of liquidation volume is not a capitulation climax; it is an ordinary day. The mechanism actually being traded is 'above-median liquidations', not 'post-climax reversal', so the stated edge is not what the backtest will measure. - The liquidations feed cannot support this strategy over the backtest window. I queried the source directly (supp_collector.py loads liquidations from the PostgreSQL `liquidations` table): BTCUSDT has 48,619 rows spanning 2026-04-21 to 2026-07-20 — ~90 days, live-accrued. The configured bar history is 2306 daily bars (6+ years). calculate_signal returns 0.0 on ~96% of bars because self._liq_by_day.get(target_day) is None. The sandbox result confirms it: total_trades=5, data_days=5, metrics_reliable=false, sharpe_note='Unreliable: only 5 daily returns'. Even in the best case, ~90 coverage days at a 2-day hold caps the strategy at roughly 20-45 lifetime trades — far below the ~100 needed to distinguish edge from noise, and the 3-window walk-forward plus 15-day holdout will place OOS windows in periods containing zero liquidation data and therefore zero trades. - min_liq_days = 5 makes the percentile gate statistically meaningless at exactly the times it fires most. rank is computed via np.mean(arr <= total) over `trailing`, and the guard only requires len(trailing) >= 5. With 5 samples the rank can only take the values 0.2/0.4/0.6/0.8/1.0, so the 0.60 threshold admits 60% of days, and since the target day is included in its own distribution the rank is never below 1/n. Because the liquidation feed starts abruptly (~2026-04-21), the strategy spends its first weeks — a meaningful fraction of the only 90 days it can ever trade — ranking against 5-15 samples. This is why loosening the threshold produced trades: the fix operated on the sample-size artifact, not on a real signal. - self.side_dominance is read from parameters (default 0.55) and never used anywhere in the file. The side split in calculate_signal hardcodes a 0.5 cut: sign = 1.0 if long_frac >= 0.5 else -1.0. The declared dominance gate the hypothesis describes is therefore not implemented, and the optimizer will spend trials tuning a parameter with exactly zero effect on results. - The iteration-3 rationale claims the root cause of the previous zero-trade failure was that the liquidations feed's timestamp_ns is 'stored in milliseconds, not true nanoseconds'. This is false. supp_collector.py line 467 constructs the field as int(row[0].timestamp() * 1_000_000_000) — genuine nanoseconds. _norm_ns is a harmless no-op on this feed, which means the change that actually produced trades was the undisclosed threshold loosening from 0.70 to 0.60, not the timestamp fix. The stated diagnosis should not be carried into the next iteration as established fact. - When a liquidation day has no side breakdown (tot <= 0), the code unconditionally sets sign = 1.0, turning every such day into a BUY. On a feed where the side column were ever null or unrecognised, the long-short strategy silently degenerates into long-only on high-liquidation days — the opposite of the hypothesis's symmetric fade. The BTCUSDT feed does populate `side`, so this is latent rather than active. - self.min_notional_param is read from parameters and never referenced. The base template's $5 min-notional check is what actually applies. Dead config surface.

Iteration History

Verification failed (Layer 4 — QA review): - TIMEFRAME MISMATCH. The hypothesis is explicitly and repeatedly DAILY: the title says 'Daily Bars', the description says 'DAILY bars' and 'price tends to revert over the following 1-3 days', and it lists '(2) DAILY horizon, avoiding the intraday-bar chop/whipsaw that gave the 15m/1H breakout, impulse, and vol-compression variants negative expectancy on majors' as an explicit engineered constraint. The config is `BTCUSDT.BINANCE-1-HOUR-LAST-EXTERNAL`. The code is not merely on a different timeframe — it is on the exact timeframe class the hypothesis names as the known failure mode it was designed to avoid, and the sandbox result (Sharpe -4.57, PF 0.51, avg_trade_return_pct -0.27%) is precisely the intraday chop the hypothesis predicted. The developer's rationale acknowledges the switch and justifies it on data-coverage grounds, but a data-coverage problem is not authority to re-specify the hypothesis. - SAMPLE SIZE IS UNMEASURABLE AND WALK-FORWARD IS STRUCTURALLY INVALID. The sandbox produced 17 trades over data_days=17. The docstring itself states the liquidations feed is live-accrued with only ~90 days of history against a multi-year bar window. Extrapolated, the full covered window supports ~90 trades — below the ~100-trade floor at which edge separates from noise. The reported metrics confirm it: probabilistic_sharpe 0.13, Sharpe CI [-14.0, +2.16] spanning zero by a wide margin. Worse, the 3-phase optimization places walk-forward windows across the full bar history; every window earlier than the liquidation coverage start contains zero events and therefore zero trades — a hard gate reject. - NEAR-DUPLICATE OF ALREADY-ABANDONED SIBLINGS. Fourth attempt at this family this session: BtcDailyLiquidationCapitulationReversalLS (same instrument, venue, feed, and percentile-fade-the-climax mechanism), BtcDailyLiquidationCascadeReversalLongShort (abandoned at Layer 4 on the coverage defect), plus two abandoned on 'supplementary:liquidations: got 0, needed 1'. The binding constraint is the data, and it has not changed. - Coverage gaps are encoded as genuine zeros, inflating the tail rank. `_build_liq_buckets` fills every event-less hour in [h0, h1] with 0.0. Since the feed is live-accrued, collector downtime produces holes indistinguishable from quiet hours. Because the gate is a relative rank (>= 0.85), a trailing window full of outage-zeros makes any ordinary cascade rank top-tail. Milder form of the zero-padding defect that killed the prior daily version — `_wsum` clamps the outer edges but does nothing about interior holes. - Layer 2 provides no coverage of the core mechanism — all six scenarios report a frozen 0.0 across 451 bars, meaning `calculate_signal` returned early every bar and the synthetic liquidations either weren't injected or don't match the expected `{timestamp_ns, usd_value, side}` schema. The entry path, side-dominance branch, and rank computation are entirely unexercised; only the 17-trade sandbox touched them. - side_dominance=0.55 is too weak to represent a 'forced-selling climax'. A 55/45 split is a near-balanced two-sided cascade, not a one-sided flush. Realized long_win_rate 0.43 / short_win_rate 0.40 is consistent with the directional call carrying no information. It was loosened alongside entry_percentile 0.90->0.85 to raise trade count, trading away the mechanism to satisfy a sample-size gate.
Strategy report

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