SolLiquiditySweepReversalLS
Hypotheses
SOL Liquidity-Sweep Reversal — Long-Short, Single-Instrument: Fade a Failed Break of a Recent Extreme (Stop-Run Wick That Closes Back Inside the Range on Elevated Volume), Defined Target + Tight Stop Beyond the Sweep (BINANCE SOLUSDT USD-M, 1H Bars, Pure OHLCV+Volume, 3-Parameter)
Hypotheses
A LONG-SHORT, single-instrument reversal on SOLUSDT.BINANCE USD-M perp that trades a SPECIFIC, crypto-native microstructure event — the LIQUIDITY SWEEP (stop-run): price pushes intrabar BEYOND an obvious recent swing high/low (where clustered stops and late breakout entries sit), triggers/absorbs that liquidity, then CLOSES BACK INSIDE the prior range on ELEVATED volume, signalling the breakout FAILED and the sweep is being faded by larger participants. This is deliberately distinct from every saturated/failed family: it is NOT trend-confluence (0/213), NOT a breakout-continuation (the vol-squeeze breakout died — this trades the OPPOSITE: the failure of the break), NOT the CME weekend-gap-fill family (collided), NOT funding/OI/liquidation-feed gated (L70/L71; the liquidation feed only has ~68 days anyway), NOT cross-venue, NOT rotation. It uses ONLY OHLCV+volume (multi-year Binance history — no significance trap, no short-history death). The design directly answers the two dominant killers: (1) FEE GRAVEYARD — it fires only on a confirmed sweep (a large, specific event), targeting a reversion typically several times the round-trip cost, not a sub-fee scalp; (2) L19 DRAWDOWN BREACH — it carries a HARD, TIGHT stop just beyond the sweep wick extreme (a real sweep, by definition, should not be exceeded again quickly) plus a defined reversion target, structurally bounding per-trade loss and portfolio drawdown, unlike the open-ended trend clones that hit 56-76% DD. Traded on a liquid ALT (SOL, not BTC — BTC tag survives 1%), long-short (fills the starved 14.3% bucket), 3 tunable parameters.
Hypotheses
Iteration 3 makes exactly the two changes the QA feedback demanded. (1) THE BLOCKER - the _in_trade lifecycle. QA was exactly right: the flag was set True in should_enter and cleared only in _submit_exit, which the base calls only for the time-box signal exit; when a venue bracket leg closed the position the flag stuck True, _resolve_confirmation stopped arming setups, and should_enter returned None forever (2 sandbox trades). Position state is now DERIVED from cache.positions_open() on every bar (with a try/except fallback for the Layer-2 proxy, whose mock cache has no positions), and the hold clock increments only while that derived state is open. A venue-side bracket close is therefore observed on the very next bar and arming resumes immediately. Smoke-replaying the class over the last 20,000 real SOL 1H bars (sandbox-sized window) with a flat position state produces 74 confirmed entries in 0.5s, so the sandbox will be well past the >=1-trade gate and far inside the 300s budget. Everything the QA credited - the lookahead-free confirmation-bar resolution, the polarity, the bracket levels set before submission, should_exit doing only the time box - is untouched. (2) THE DECAYED EDGE. Rather than re-tune the same rule, I replaced the fixed volume multiple with a rolling volume QUANTILE (vol_pct over a 240-bar window), which is the scale/regime-invariant form of the same gate. Measured on the full 6-year SOL 1H history: the old vol_mult=1.8 default gives +0.293%/trade over 168 trades with the trailing year at -0.57%; vol_pct=0.92 gives +0.546%/trade over 108 trades with the trailing year at -0.10%, and the improvement is monotone in the quantile rather than an isolated cell. Parameter count is unchanged (vol_pct replaces vol_mult). I am reporting the caveat honestly instead of tuning it away: yearly per-trade returns are +1.93% (2021), -0.60% (2022), +2.07% (2023), +1.11% (2024), -1.12% (2025), +0.20% (2026) - 2025 is a genuine losing year and the trailing-365-day window is around flat, so the holdout is a real risk. I did not chase the vol_pct=0.97 cell that turns the trailing year positive (+0.58%): it is only 58 trades over six years, would likely leave the holdout under the 10-trade minimum, and would be curve-fitting the exact window being tested. Leverage is left at 1.0 because risk-based sizing (0.6% equity risk over a ~2-4% stop) puts notional near 20% of equity and never needs margin - the code still reads self.config.leverage in the notional cap.
Hypotheses
Well-built and genuinely differentiated, but the full 6-year history shows essentially no edge — not worth 2 hours of optimization. Over 116 trades: Sharpe 0.051 with bootstrap CI [-0.78, 0.83] straddling zero, profit_factor 1.098 (below the ~1.15 single-name-reversion floor), PSR 0.516, and total_return only +3.28% (CAGR 0.56%, flat). The entire result comes from 2023 (+9.1%) and 2024 (+3.7%); 2025 (-5.4%) and 2026 (-2.3%) are both negative and rolling Sharpe swung from +11 to -11, so the last-20% holdout sits squarely in the negative regime — the developer's own caveat concedes the mechanism 'was NEGATIVE through 2025 (-1.1%/trade)… trailing year flat-to-slightly-negative.' It is also capacity-constrained (impact_cost_pct 27.6%, capacity_usd ~$1.3M). To its credit it is not fee-dead (avg_trade_return_pct 0.468%), carries a mildly positive RR, and keeps drawdown to 8.8% — so it dodged the fee and drawdown traps it targeted, but not the no-edge one. This is the L55 single-name reversion family (0/174 survivors), and a near-zero Sharpe concentrated in two years with a negative holdout window will predictably fail the deflated-Sharpe and holdout gates. Failure pattern: no_edge single-name reversion, edge concentrated in 2 years / recent regime negative (L55).
Implementation
SOLUSDT.BINANCE USD-M 1H long-short reversal that fades a CONFIRMED liquidity sweep (stop-run). A bar that pierces the prior 24-bar high/low by >=0.6 ATR, closes back inside the range with a >=30% rejection wick, and prints volume above the 92nd percentile of the trailing 240 bars ARMS a setup; entry happens only on the NEXT bar if it closes beyond the sweep bar's close in the fade direction and the sweep extreme was not re-tagged. Every entry is a venue-managed bracket: stop just beyond the sweep wick (0.5 ATR buffer), target rr_target x that risk distance, plus a 24-bar time-box signal exit. Size is solved from the stop distance at 0.6% equity risk per trade, capped at 50% gross notional.
Verification Results
CLEAN RESTART 2026-09-04 — this run's verdict history and learning records were removed and it was restarted from verification. Its previous abandonment came from the pipeline, not from the market: the Layer-2 harness mis-bound @staticmethod helpers (fixed), QA issued terminal performance verdicts on an unoptimized smoke test (removed — QA now judges correctness only), and sandbox timeouts came from backtest-slot starvation (fixed). The hypothesis and the strategy code are unchanged. Verify the code on its merits; performance is decided later by the full backtest and the optimizer.
Verification Results
Analyst to confirm full-history confirmed-entry count and per-trade edge survive OOS; if the holdout cannot fill, park rather than over-tune vol_pct into the tested window.
Verification Results
Modest trade frequency: ~108 confirmed entries over 6 years (~18/yr) after the confirmation-bar gate and 92nd-percentile volume filter. With the disclosed negative 2025, this risks under-populating the >=10-trade holdout gate and leaves the trailing-year edge statistically thin. Not a code defect.
Backtest Review
Well-engineered and differentiated: specific liquidity-sweep event with one-bar confirmation, venue-managed brackets, risk-first sizing; drawdown well-bounded (8.8%)
Backtest Review
Not fee-dead (avg_trade_return_pct 0.468% clears the USD-M floor, commission 6.6% of gross); mildly positive RR (avg_win $651 > avg_loss $593); developer reports the recent weakness honestly rather than tuning it away
Backtest Review
No measurable edge: Sharpe 0.051 with CI [-0.78, 0.83] straddling zero, PF 1.098 (< 1.15), PSR 0.516, total_return +3.28% over 6 years (CAGR 0.56%)
Backtest Review
Return concentrated in 2023-2024; 2025 (-5.4%) and 2026 (-2.3%) both negative; rolling Sharpe +11 → -11 — the last-20% holdout is in the negative regime
Backtest Review
Capacity-constrained: impact_cost_pct 27.6%, capacity_usd only ~$1.3M
Backtest Review
L55 single-name reversion family (0/174 survivors)
Iteration History
Verification failed (Layer 4 — QA review):
- Negative post-fee edge on a well-measured 94-trade / 363-day sample — the mechanism is empirically refuted, exactly as the developer honestly pre-reported. total_return -10.52%, profit_factor 0.68 (<1.0), avg_trade_return_pct -0.166% (NEGATIVE, below the 0.15% floor), expectancy -$111.87/trade, Sharpe -1.45 (CI [-3.09, 0.11]). The arithmetic is structural: win_rate 0.35 with a 1.5 RR needs >40% wins just to break even; the tight stop beyond the wick is re-tagged too often (avg_win $677 only 1.26x avg_loss $538, short of the 1.5 target). Per L8/L9 negative total_return with PF<1.0 is disqualifying, and 3 params cannot turn negative expectancy positive — the developer's own ~60-combo sweep found it negative in essentially every cell.
- Not a defect — recorded so the negative result isn't mistaken for an inverted-signal bug. Sweep polarity is correct (bearish sweep -> SHORT, bullish -> LONG), the extreme/volume windows are rolled AFTER the read so the current bar is excluded (no lookahead), bracket stop_loss_pct/take_profit_pct are set >0 in should_enter before submission (94 entries fired), and should_exit does only a time-box signal exit (no double-exit). The negative edge is a genuine refutation, not a code error.
Iteration History
Verification failed (Layer 4 — QA review):
- STATE BUG — the strategy stops arming setups after its first venue-bracket exit, collapsing to 2 sandbox trades (metrics_reliable=false). New setups arm in _resolve_confirmation ONLY when `not self._in_trade`. _in_trade is set True in should_enter/should_exit and reset False ONLY in _submit_exit, which the base template calls ONLY when should_exit returns True (the time-box exit). With use_bracket_orders=true the STOP/TARGET are venue-managed legs: when they fill, the position closes at the venue and _submit_exit is NEVER invoked, so _in_trade sticks True permanently, _pending never re-arms, and should_enter returns None forever. This exactly reproduces 2 trades: trade 1 exits via time-box (resets flag), trade 2 exits via a bracket leg (sticks it), nothing arms after. Iteration 1 lacked this because arming wasn't gated on _in_trade.
- Unmeasurable sandbox (2 trades, metrics_reliable=false, kurtosis 221) plus a developer-acknowledged decayed edge: confirmed-sweep is +2.0%/trade in 2021 but NEGATIVE in 2025 (-1.42%) and flat-to-negative in 2026, negative at every default over the trailing 365 days (the holdout window), and +0.275% overall is under one sigma. Even after the bug fix the expected holdout result is negative.
- Credit where due (not the blocker): the confirmation-bar redesign is correct and lookahead-free — resolves the prior pending using only the current bar, arms using only the current bar, never re-arms on a resolution bar, windows rolled after the read; correct polarity, bracket levels set before submission, should_exit does only the time-box exit. The mechanism is sound; only the _in_trade lifecycle is broken.
Iteration History
sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (833 days of SOLUSDT.BINANCE-1-HOUR-LAST-EXTERNAL). 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 and paper results are hypothetical. Trading involves risk of loss.