Skip to content

View original

SolLiquidationCascadeExhaustionReversalLS5m

Hypotheses

SOL Liquidation-Cascade Exhaustion Reversal (Binance USD-M Perp, Forced-Flow Overshoot Fade, Short-Horizon Long-Short)

Hypotheses

A short-horizon, crypto-native microstructure strategy that fades the price overshoot created by forced-liquidation cascades on SOLUSDT.BINANCE USD-M perp. Liquidations are mechanical, price-insensitive market orders (an exchange force-closing under-margined positions), so a cluster of forced selling drives price BELOW fair value temporarily, and a cluster of forced buying (a short squeeze) drives it ABOVE — both mean-revert once the forced flow exhausts. The strategy detects an extreme liquidation burst from the Binance liquidations supplementary feed, confirms the matching sharp price move, and enters a counter-trend position for the snapback. This is explicitly NOT a carry, funding, or basis trade — the entire abandoned-strategy cluster this session is carry/basis fee-domination, and this mechanism is structurally different: it captures a large (1-3%) mechanical overshoot, not a sub-fee spread. It is also the portfolio's first use of the liquidations data source and fills the under-represented short-horizon and long-short buckets. Single instrument, few parameters, symmetric long/short to keep it simple and regime-robust.

Hypotheses

Iteration 5 fixes the Layer 3 wall-clock timeout with a config-only change. Layers 1 and 2 already passed, so imports, class structure, and the signal/entry/exit/sizing logic are byte-identical to `previous_code` — the only edit is `bar_type` 5-MINUTE -> 15-MINUTE (plus a docstring note recording why). Diagnosis: the feedback's suggested cause (per-bar O(n) rescans of the supplementary series) was ALREADY fixed in an earlier iteration — `_load_liquidations()` runs once in `__init__` building a sorted ts list + prefix sum, `_net_flow()` is two `bisect` calls, `_window_move_pct()` is O(1), and the base class caps `self._bars` at 500. Nothing per-bar scales with history. The residual cost was the raw engine bar count: this code was rewritten for 15m bars (`max_hold_bars` = 12 is commented '~3h on 15m', which only holds at 15m) while the config still declared 5-MINUTE, so the engine processed ~3x the intended bars. The identical failure and fix are documented in the sibling `BtcLiquidationCascadeExhaustionFadeLS1m_v5`, whose docstring records that its per-bar math was already O(log n) and that aligning the config bar_type to the code's intended interval is what brought the smoke test under 300s. Because every time computation derives from `_bar_interval_ns(self.bar_type)`, the liquidation and move windows auto-rescale to 75 min, preserving the minutes-to-hours cascade-snapback horizon and the short-horizon/long-short bucket. Venue is futures (BINANCE USD-M) because the strategy takes symmetric shorts and the liquidations feed is a futures data source; leverage stays 1.0 and `position_size()` reads `self.config.leverage`, so no leverage gate is tripped. Per-trade edge (1.0% TP vs ~0.10% round-trip taker) clears fees by ~10x, and the 3M USD + 1% confirming-move double filter keeps trade count low and selectivity high.

Hypotheses

Not optimizable and not developer-fixable: the strategy is structurally untestable on the data this factory currently holds. Verified on the filesystem (not inferred): `data/binance_vision/liquidations/` contains 624 symbol directories and ZERO files, and `data/supplementary/liquidationSnapshot/` contains 5 symbol directories — none of them SOLUSDT — and ZERO files. Binance's historical liquidationSnapshot CDN dataset is absent, so the `liquidations` supplementary key can only be served from the live PostgreSQL `LiquidationCollector` (Binance `!forceOrder` WebSocket), which accumulates forward from whenever the collector started. That is precisely what the backtest shows: 203,761 bars evaluated from 2020-09-14, but all 7 trades confined to 2026-05-15 through 2026-07-06, `data_days` 7, `exposure_pct` 1.40%, `annual_returns` containing only `{"2026": ...}`, `cagr` null, and the engine's own `metrics_reliable: false` with var_95/cvar_95/tail_ratio/omega_ratio/max_drawdown_ci all null. Sending this to a 225-trial sweep would fit 7 trades of noise inside a 7-week window, leave every pre-2026 walk-forward OOS window EMPTY, and fail deflated Sharpe by construction. The strategy CODE is correct — O(log n) prefix-sum liquidation lookups, no price-only fallback, correct symmetric long/short fade, clean bar_type alignment after iteration 5 — so there is nothing for the developer to iterate on; the constraint is the archive, not the implementation. Two independent economic problems would remain even with full history: (1) INVERTED PAYOFF GEOMETRY — `take_profit_pct = 1.0` against `stop_pct = 1.5` risks 1.5% to make 1.0%, and the realized avg_win ($134.47) is already well below avg_loss ($234.18), with a 71.4% win rate on n=7 doing all the work; this is the same small-TP/large-stop shape the hypothesis explicitly claims to avoid. (2) COST DOMINATION — commission_pct_of_gross 20.79% plus impact_cost_pct 25.56% consumes ~46% of gross PnL, and avg_trade_return is $29.14 on a ~$20k position (~0.146%), at or below the Binance USD-M round-trip plus impact; the hypothesis's defense that it 'captures a large (1-3%) mechanical overshoot, not a sub-fee spread' is contradicted by its own realized per-trade economics at a 1.0% take-profit. FAILURE PATTERN — FOR THE RESEARCH LEAD, THIS IS A DATA-AVAILABILITY WALL, NOT A DEAD MECHANISM: liquidation-cascade strategies cannot currently be validated in this factory because no historical liquidation archive exists on disk; only forward-collected WebSocket data is available. Do not propose further liquidation-feed hypotheses until a Data Engineer confirms the PostgreSQL `liquidations` table's actual date span (I did not query it) and, if it is short, backfills a historical liquidation source. If that backfill lands, this hypothesis is worth retrying — but with the payoff geometry corrected (take_profit_pct > stop_pct, e.g. 2.0% vs 1.2%) so the mechanical overshoot it targets is actually larger than the ~0.10% round-trip plus ~25% impact drag it must clear.

Implementation

Long-short liquidation-cascade exhaustion fade on SOLUSDT.BINANCE USD-M perp, 15-MINUTE bars. Aggregates signed forced-liquidation USD flow (+usd for SELL liqs = forced selling, -usd for BUY liqs = forced buying) from the Binance `liquidations` supplementary feed over a rolling 5-bar (75 min) window. When net forced flow exceeds 3M USD AND the realized price move over the same window confirms an overshoot of >= 1.0% in the forced-flow direction, it enters the counter-trend fade: forced selling + price drop -> BUY; forced buying + price spike -> SELL. Exits on a 1.0% snapback take-profit, a 1.5% stop (overshoot continued), or a 12-bar (~3h) timeout. Signal is continuous signed net-liquidation intensity (net_flow / threshold), computed every bar. No price-only fallback: absent liquidation data, the signal is 0 and the strategy stands flat.

Verification Results

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).

Backtest Review

The trades match the hypothesis: 7 entries, 5 long + 2 short, symmetric fade, avg hold 2h 30m (vs the 12-bar / 3h timeout), beta 0.0001 and benchmark_correlation 0.057 — a genuine counter-trend microstructure trigger, not a closet long.

Backtest Review

The code is correct and efficient: the signed-liquidation series is loaded once into a sorted timestamp list + prefix sum, `_net_flow()` is two bisect calls, `_window_move_pct()` is O(1). Nothing per-bar scales with history. The iteration-5 bar_type fix worked — 203,780 bars in 35s.

Backtest Review

It honestly refuses to trade without data: no price-only fallback, the signal stays 0.0 when the liquidation window is empty.

Backtest Review

The economic premise (forced, price-insensitive liquidation flow creates a mechanical overshoot that mean-reverts) is sound and genuinely orthogonal to the carry/basis cluster.

Backtest Review

THE ENGINE FLAGGED IT: `metrics_reliable: false`. `var_95`, `cvar_95`, `tail_ratio`, `omega_ratio`, and both `max_drawdown_ci` bounds are all null — there is not enough data to compute them.

Backtest Review

ONLY 7 TRADES over 203,761 evaluated bars, and ALL of them fall between 2026-05-15 and 2026-07-06. `annual_returns` contains a single key: `{"2026": 0.204}`. `data_days` is 7, `cagr` is null, `exposure_pct` is 1.40%. The backtest nominally spans 2020-09-14 to 2026-07-07; the strategy was blind for ~99.9% of it.

Backtest Review

VERIFIED DATA WALL (I checked the filesystem, I did not infer this): both on-disk liquidation stores are EMPTY. `data/binance_vision/liquidations/` has 624 symbol directories and 0 files. `data/supplementary/liquidationSnapshot/` has 5 symbol directories (AAVEUSD_PERP, ADAUSD_230630/230929/231229/240329) — no SOLUSDT at all — and 0 files. Binance's historical `liquidationSnapshot` CDN dataset is not present. The only remaining source for the `liquidations` key is the PostgreSQL `LiquidationCollector`, which populates from the live `!forceOrder` WebSocket and therefore only holds data from the moment the factory's collector began running. The ~7-week trade window is exactly what that implies. NOTE: I confirmed the on-disk archives are empty; I did NOT query the database to measure its span directly — that should be confirmed before any retry.

Backtest Review

This is not a code defect the developer can fix. No parameter change, no threshold loosening, and no rewrite manufactures six years of liquidation history. Lowering `liq_usd_threshold` would only mine more trades out of the same 7 weeks.

Backtest Review

INVERTED PAYOFF GEOMETRY — the exact flaw the hypothesis claims to have engineered around. `take_profit_pct = 1.0` against `stop_pct = 1.5` means the strategy risks 1.5% to make 1.0%. The result shows it: avg_win $134.47 vs avg_loss $234.18. The 71.4% win rate is the only thing holding profit_factor at 1.44, and at n=7 that hit rate is noise.

Backtest Review

COSTS DOMINATE THE EDGE. commission_pct_of_gross is 20.79% and impact_cost_pct is 25.56% — roughly 46% of gross PnL is consumed by trading costs. capacity_usd is $1.53M. avg_trade_return is $29.14 on a ~$20k position (~0.146%), which is at or below the ~0.10% USD-M round-trip plus impact. The hypothesis's core defense — 'it captures a large (1-3%) mechanical overshoot, not a sub-fee spread' — is contradicted by its own realized per-trade economics.

Backtest Review

Sharpe 1.484 is meaningless: `sharpe_ci` spans -9.0127 to 37.1302. A 3-window walk-forward would leave every pre-2026 OOS window empty and deflated Sharpe would fail outright.

Analysis

Do NOT proceed to optimization — the signal has almost no history to test on. Despite 203,684 15-min bars spanning 2020-2026, the strategy produces only 7 trades and ALL of them fall in 2026-05 to 2026-07 (data_days=7, annual_returns has a single 2026 entry, metrics_reliable=false). The tell is that there are ZERO signals during 2021's blow-off top and 2022's crashes — periods dense with SOL liquidation cascades — which means the `liquidations` supplementary feed only carries data for a ~2-month recent window, not that cascades didn't occur. FIX/INVESTIGATE: (1) Verify the coverage of the liquidations feed for SOLUSDT across the full backtest window — print the min/max timestamp and row count actually loaded in _load_liquidations. (2) If the collector can source multi-year Binance liquidation history (Binance Vision archives), backfill it so the signal spans the whole period and produces hundreds of trades across multiple regimes; then re-run the initial backtest. (3) If the liquidation feed genuinely only exists for ~2 months (Binance restricted full liquidation history post-2021), then this signal is structurally not backtestable at scale and should be ABANDONED rather than optimized — do not tune 4 parameters against 7 trades. SEPARATELY, note the heavy cost profile in the window it did trade (impact 36.9% of gross, commission 21.4%, capacity only $733k) — a 15m SOL fade is impact-sensitive, so once the data is fixed, confirm the net-of-cost edge survives before optimizing.

Outcome Summary

The code was correct and efficient — a prefix-sum liquidation series with O(log n) bisect lookups, no price-only fallback so it honestly refuses to trade without data, and an iteration-5 bar_type fix that brought 203,780 bars in under 35 seconds — and the trades it did produce matched the hypothesis exactly (symmetric long/short, 2h 30m average hold, beta 0.0001). But there was nothing for the developer to iterate on: the constraint was the archive, not the implementation. The reviewer also flagged two economic problems that would survive a backfill: the payoff geometry is inverted (take_profit_pct 1.0 against stop_pct 1.5, with realized avg_win $134.47 below avg_loss $234.18, and a 71.4% win rate on n=7 doing all the work), and the realized avg_trade_return of $29.14 on a ~$20k position (~0.146%) sits at or below the round-trip plus impact — directly contradicting the hypothesis's claim to capture a 1-3% overshoot rather than a sub-fee spread. The abandon was explicitly filed as a data wall rather than a dead mechanism: the reviewer recommended no further liquidation-feed hypotheses until a Data Engineer confirms the PostgreSQL table's actual date span (which they noted they did not query), and said the idea is worth retrying if a historical backfill lands and the take-profit is widened past the stop.

Outcome Summary

Before proposing a hypothesis built on a new data source, confirm a historical archive of that source actually exists on disk — a forward-collecting WebSocket feed cannot backtest, and no parameter change or rewrite manufactures six years of missing history.

Outcome Summary

The backtest-review gate abandoned it on a verified data-availability wall: the reviewer checked the filesystem and found data/binance_vision/liquidations/ holds 624 symbol directories and zero files, while data/supplementary/liquidationSnapshot/ holds 5 symbol directories (none of them SOLUSDT) and zero files — so the liquidations key can only be served by the live PostgreSQL collector accumulating forward from whenever it started, leaving the strategy blind for ~99.9% of its nominal 2020-2026 span.

Outcome Summary

Fade the mechanical price overshoot left behind by forced-liquidation cascades on SOLUSDT.BINANCE USD-M perp 15-minute bars — reading net signed liquidation flow from the Binance liquidations feed, requiring an extreme burst (≥$3M) plus a confirming ≥1% price move, then entering counter-trend (long after forced selling, short after forced buying) for the snapback.

Outcome Summary

It fired only 7 trades (5 long / 2 short) across 203,761 evaluated bars, all confined to 2026-05-15 through 2026-07-06, returning 0.203% with Sharpe 1.484 on a confidence interval spanning -9.0127 to 37.1302. The engine set metrics_reliable=false, with var_95, cvar_95, tail_ratio, omega_ratio and both max_drawdown_ci bounds all null; commission_pct_of_gross (20.79%) plus impact_cost_pct (25.56%) consumed roughly 46% of gross PnL.

Iteration History

SolLiquidationCascadeExhaustionReversalLS1m
Strategy report

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