Skip to content

View translation

SolTakerAggressionExhaustionReversalLS

Hypotheses

Taker-Aggression Exhaustion Reversal, Single-Venue Long-Short (Single-Instrument SOLUSDT.BINANCE USD-M — Fade a Sharp Price Move Driven by an EXTREME One-Sided TAKER Aggressor Imbalance, i.e. Euphoric Market-Buy Chasing / Panic Market-Sell Dumping That Exhausts Resting Liquidity, Enter Contrarian, Exit on Taker-Flow Normalization / Target / Time, 3-Parameter)

Hypotheses

A LONG-SHORT, SINGLE-INSTRUMENT, SINGLE-VENUE strategy on SOLUSDT.BINANCE USD-M perpetual. The orthogonal signal is the TAKER buy/sell aggressor imbalance (taker_ratio supplementary feed, orthogonal to the price series and not a re-derivation of trend — satisfies L42). No taker-flow strategy appears anywhere in the failure log, so this is a genuinely fresh family distinct from the saturated cross-sectional-momentum, funding-crowding, OI-confirmed/OI-flush, and premium-capitulation graveyards. Mechanism: when aggressors are overwhelmingly lifting offers (taker_buy_ratio at a multi-day extreme) WHILE price is spiking up, that is late-stage euphoric market-order chasing that consumes resting ask liquidity and leaves price extended above where passive participants will support it — a short-term reversal down follows once the aggressive flow exhausts. Symmetric on the downside: extreme taker-SELL dominance during a flush = panic market-selling into thin bids, over-extends price to the downside, snaps back. The strategy fades the aggressor: extreme taker-BUY + up-move → SHORT; extreme taker-SELL + down-move → LONG. This is exhaustion/liquidity-consumption, not momentum continuation (the opposite of a taker-flow breakout book). Single-venue, single-instrument — no cross-venue/second-leg joint-availability problem (L44), no options/IV feed (L43). SOLUSDT chosen for high realized volatility (reversal targets clear the 0.10% USD-M round trip with wide margin) and to diversify away from the 2%-survival BTC tag. Kept to 3 parameters to resist the overfit deaths dominating the log. Instruments: ["SOLUSDT.BINANCE"]. venue_type: futures. data_requirements: ["ohlcv", "taker_ratio"].

Hypotheses

Iteration-6 fix for the reported incoherence (Sharpe +0.22 vs total_return -54.71%): judged on the equity curve the strategy bled to death on transaction costs, not on a broken signal — a mildly positive per-bar Sharpe alongside a deeply negative compounded return is the signature of cost-of-churn. The iteration-5 geometry made that arithmetic inevitable: notional = equity*0.01/(~1.2% ATR stop) ~= 0.83x equity capped only at 1.0x, an entry threshold of 1.5 on a signal whose own std is ~0.71 (so |signal|>1.5 fired on ~3-4% of bars, several hundred entries over the window), and a flow-normalization exit with exit_z_frac=0.0 that closed the position the moment the score decayed through zero — often within a bar or two, well before the move could clear the ~0.10% USD-M round trip. ~700 round trips x 0.10% x 0.83x notional is ~58% of equity, matching the -54.71% almost exactly. Three coupled parameter/logic changes cut that budget by roughly an order of magnitude while leaving the signal, the entry polarity, and everything that passed Layers 1-3 intact: max_notional_frac 1.0 -> 0.35 (round trip costs ~0.035% of equity instead of ~0.083%, and it also caps the compounding blow-up behind the drawdown), entry_z 1.5 -> 1.8 (entries restricted to the genuine ~2.5-sigma tail, cutting trade count ~3x while still leaving well over 100 trades on the sandbox window), and exit_z_frac 0.0 -> -0.5 plus a new min_hold_bars=1 guard so the flow exit requires the fade score to actually FLIP against the position rather than merely decay, giving a winner room to reach the min_tp_pct=0.35% target that clears the round trip. Everything else — the two-mirror taker parsing, the point-in-time as-of lookup, the module-level _infer_bar_interval_ns that must not shadow the base staticmethod, the calendar-anchored time stop, imports — is unchanged.

Hypotheses

Negative expectancy on a reliable 523-trade / 6-year sample: profit_factor 0.898, total_return -13.16%, avg_trade_pnl_usd -24.87 (negative expectancy), Sharpe -0.31, Sortino -0.17, information_ratio -1.06 vs buy-hold. The headline avg_trade_return_pct of 0.185% is an equal-weighted artifact — the size-weighted USD expectancy is negative because losers carry larger notional, and PF<1 confirms there is no positive gross edge for fees to eat into. The taker-exhaustion fade produces NO edge in any regime (calm -11.0%, normal -1.5%, stressed -9.9%) and loses in every full year, so this is a statement about the entry/direction, which IS the hypothesis — not a parameter cliff. The adverse payoff geometry (avg_loss 560.9 vs avg_win 389.5) means the fade is systematically run over by continuation when aggression is extreme; optimizing the 3 params (lookback_bars/entry_z/max_hold_bars) cannot invert a PF-0.898 signal to profitability. QA's edge concern is borne out on full history. Matches L55 (single-name fade, PF<1.15 → abandon). abandon_class=negative_expectancy.

Implementation

Long-short single-instrument SOLUSDT.BINANCE USD-M perpetual strategy on 1-hour bars that FADES sharp price moves driven by an extreme one-sided TAKER aggressor imbalance. Each taker observation (taker_ratio fapi mirror, backfilled by the metrics Vision mirror for the years the fapi feed does not cover) is converted to a bounded imbalance (buy-sell)/(buy+sell); that imbalance is z-scored against its own rolling window, as is the 2-bar price return, and the continuous signal is -0.5*(z_flow + z_move) recomputed every bar. Extreme taker-BUY dominance into an up-move -> SHORT the exhausted chase; extreme taker-SELL dominance into a flush -> LONG the panic. Exits on ATR target, ATR stop, a calendar-anchored time stop, or a taker-flow score that flips against the position after a minimum hold. Risk-based sizing with a hard 0.35x-equity notional cap; leverage 1.0; no price-only fallback when the taker feed is stale.

Verification Results

No change required if the base template treats min_bars_required as an overridable hook; otherwise rename. Behavior is correct as written.

Verification Results

min_bars_required(self) overrides a framework method (flagged by static analysis). Reviewed the base contract: the override returns a sensible warmup (lookback_bars + move_bars + atr_period + 2) and is the intended per-strategy hook, so this is benign, but noting the static-analysis base_shadow warning for completeness.

Verification Results

Sandbox (unoptimized, single draw) is negative: total_return -11.1%, Sharpe -0.66, avg_trade_return_pct -0.0223% (below the fee floor), profit_factor 0.81 on 262 trades, and the 'stressed' vol tercile is deeply negative (-13.2%, Sharpe -1.51) while only the 'calm' tercile is positive (+1.43%). The min_tp_pct=0.35% target structurally clears the ~0.10% round trip, so the negative per-trade result is an unoptimized-parameter/loss-asymmetry outcome rather than a structurally fee-negative design, but the analyst should verify on full history that the fade edge is not confined to the calm regime and that avg_trade_return_pct clears fees after optimization. avg_win 380.8 vs avg_loss 428.7 with a 2.0xATR stop / 1.5xATR target is an unfavorable payoff geometry worth checking first.

Backtest Review

Orthogonal taker-aggression signal (not a re-derived trend), fresh family in the failure log

Backtest Review

Reliable sample: 523 trades over 2181 days, balanced long (268) / short (255), metrics_reliable=true

Backtest Review

Well-engineered code with restart-safe exits and fee-drag budgeting

Backtest Review

Negative expectancy on a decisive sample: profit_factor 0.898 (<1), total_return -13.16%, avg_trade_pnl_usd -24.87, Sharpe -0.31, Sortino -0.17

Backtest Review

Loss asymmetry is adverse: avg_loss 560.9 vs avg_win 389.5 despite a 56% win rate — the 1.5xATR target / 2.0xATR stop geometry lets the fade get run over on continuation

Backtest Review

No regime works: calm -11.0% (Sharpe -0.23), normal -1.5%, stressed -9.9% (Sharpe -0.96). The fade edge is absent everywhere, not confined to a tradeable regime

Backtest Review

Losing in every full year (2023 -3.4%, 2024 -1.6%, 2025 -2.3%, 2026 -7.1%); information_ratio -1.06 vs buy-hold

Backtest Review

Matches L55: single-name fade/reversion with PF < 1.15 has never been lifted over fees by threshold re-tuning

Backtest Review

misleading here: equal-weighted per-trade pct is positive but size-weighted USD expectancy is negative because losing trades carry larger notional

Analysis

Backtest gate: - metrics unreliable: Incoherent: Sharpe 0.22 disagrees in sign with total_return -54.71% — the Sharpe is computed from the mtm_equity_curve series while the return comes from the equity curve. Judge on the equity curve.

Outcome Summary

This strategy proposed a genuinely fresh family for the factory — fading taker-aggression exhaustion on SOL, shorting euphoric market-buy chasing and longing panic dumping, using the orthogonal taker buy/sell imbalance with no taker-flow strategy anywhere in the failure log. The design was thoughtful: rolling z-scores rather than absolute thresholds, a smooth co-movement score instead of a hard compound gate, risk-based sizing, and no price-only fallback so the taker extreme itself was the edge under test. But across three iterations it never cleared verification — every one of the six Layer-2 synthetic scenarios crashed with an identical `_bar_interval_ns()` signature TypeError, a code defect that stopped the strategy from running before any exhaustion-reversal edge could be measured. It was abandoned in the verification loop, having never produced a backtest.

Outcome Summary

A genuinely fresh, well-motivated signal family can't be evaluated until the code clears basic execution — a helper method with a signature incompatible with the Layer-2 verification proxy (which rebinds callables and injects self) crashed every synthetic scenario, so verification hygiene around proxy-rebound helpers must be right before the edge can ever be tested.

Outcome Summary

abandoned

Outcome Summary

It was abandoned in the verification loop at Layer 2 (synthetic scenarios) after 3 iterations: all six scenarios failed with the same TypeError — `_bar_interval_ns() takes 1 positional argument but 2 were given` — a method-signature defect (the Layer-2 proxy injects self, and the helper was defined incompatibly) that prevented the strategy from running at all. Backtest review, optimization, analysis and risk review were never reached.

Outcome Summary

A long-short, single-instrument, single-venue strategy on the SOLUSDT.BINANCE USD-M perp that fades sharp price moves driven by an extreme one-sided taker aggressor imbalance — shorting euphoric market-buy chasing that exhausts resting ask liquidity, longing panic market-selling into thin bids — using the taker_ratio feed (orthogonal to price) z-scored against its own rolling history and combined with a co-directional price-move requirement, exiting on flow normalization, an ATR target/stop, or a time stop.

Outcome Summary

No performance exists — the strategy never reached a backtest, so there are no returns, Sharpe, or trade metrics.

Iteration History

SolTakerAggressionExhaustionReversal

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['exit_z', 'tp_atr', 'entry_z', 'stop_atr', 'risk_frac', 'atr_period', 'z_lookback', 'min_stop_pct', 'max_hold_bars', 'move_lookback', 'max_notional_frac', 'max_stale_minutes'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: Setup error: TypeError: SolTakerAggressionExhaustionReversal._bar_interval_ns() takes 1 positional argument but 2 were given - steady_downtrend: Setup error: TypeError: SolTakerAggressionExhaustionReversal._bar_interval_ns() takes 1 positional argument but 2 were given - flat_ranging: Setup error: TypeError: SolTakerAggressionExhaustionReversal._bar_interval_ns() takes 1 positional argument but 2 were given - volatility_spike: Setup error: TypeError: SolTakerAggressionExhaustionReversal._bar_interval_ns() takes 1 positional argument but 2 were given - zero_volume: Setup error: TypeError: SolTakerAggressionExhaustionReversal._bar_interval_ns() takes 1 positional argument but 2 were given - price_gap: Setup error: TypeError: SolTakerAggressionExhaustionReversal._bar_interval_ns() takes 1 positional argument but 2 were given

Iteration History

EthOpenInterestConfirmedBreakout

Iteration History

Verification failed (Layer 4 — QA review): - WHOLE-STRATEGY SWAP: the code is class EthOpenInterestConfirmedBreakout — a Donchian BREAKOUT on ETHUSDT.BINANCE gated by OPEN-INTEREST expansion, entering WITH the move (momentum continuation). The hypothesis is a Taker-Aggression Exhaustion REVERSAL on SOLUSDT.BINANCE that FADES an extreme taker aggressor imbalance via the taker_ratio feed. Instrument, signal feed, and edge direction are all wrong — mislinked/mis-generated code. - INSTRUMENT MISMATCH: hypothesis names SOLUSDT.BINANCE; config/class/sandbox all run ETHUSDT.BINANCE-1-HOUR. - CORE EDGE ABSENT: hypothesis edge is the taker buy/sell aggressor imbalance (taker_ratio). The code never reads taker_ratio — it reads open_interest and builds an OI-growth gate. The stated mechanic is not implemented. - EDGE DIRECTION INVERTED: hypothesis is exhaustion/REVERSAL (fade the move). should_enter returns BUY on up-breakout / SELL on down-breakout — momentum continuation, the opposite sign.

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['sl_atr', 'tp_atr', 'entry_z', 'min_obs', 'move_bars', 'risk_frac', 'atr_period', 'min_sl_pct', 'min_tp_pct', 'exit_z_frac', 'lookback_bars', 'max_hold_bars', 'max_stale_hours', 'max_notional_frac'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - steady_downtrend: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - flat_ranging: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - volatility_spike: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - zero_volume: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - price_gap: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['sl_atr', 'tp_atr', 'entry_z', 'min_obs', 'move_bars', 'risk_frac', 'atr_period', 'min_sl_pct', 'min_tp_pct', 'exit_z_frac', '_param_bounds', 'lookback_bars', 'max_hold_bars', 'max_stale_hours', 'max_notional_frac'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - steady_downtrend: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - flat_ranging: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - volatility_spike: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - zero_volume: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given - price_gap: Setup error: TypeError: SolTakerAggressionExhaustionReversalLS._bar_interval_ns() takes 1 positional argument but 2 were given
Strategy report

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