Skip to content

View original

AdaFourHourDonchianHighPersistenceBreakoutLong

Hypotheses

ADA 4H Donchian-High Persistence Breakout Long (Multi-Bar Channel-Top Knocking Pattern)

Hypotheses

A long-only, single-instrument, single-timeframe strategy on ADAUSDT.BINANCE 4H bars (audit_stale RESOLVED). Implements a NEW structural mechanism combining three analyst-validated successful pattern attributes: (a) sequence-based (per Two-Bar Sequential HH success), (b) channel-breakout (per Volume-Confirmed Donchian success), (c) range-statistics (per Channel-Width Expansion success). The mechanism: detect when price has been 'knocking on' the 20-bar Donchian high — i.e., closing within 0.3×ATR of the Donchian high for 3+ consecutive bars — and then breaks above it. This 'persistence' setup is structurally distinct from a single-bar breakout because the multi-bar proximity establishes that buyers have been continuously testing the channel top, building pressure. When the actual breakout finally occurs, it represents accumulated buying pressure resolving upward. Critically distinct from every existing ADA 4H mechanism: (a) NOT Two-Bar HH Breakout (uses PROXIMITY-to-channel-high not raw bar-high sequence); (b) NOT Donchian Channel-Width Expansion (uses bar-close proximity not width metric); (c) NOT N-Bar Sequential HL Breakout (uses HIGH-side proximity not LOW-side rising support); (d) NOT Inside-Bar Cluster (uses channel-top knocking not contracting ranges); (e) NOT Midpoint Reclaim (uses TOP not midpoint); (f) NOT statistical signals. SEQUENCE-BASED + RANGE-STATISTICS + CHANNEL-BREAKOUT confluence per analyst's complete successful-pattern profile.

Hypotheses

Iteration 3 after the clean restart: the code is unchanged in mechanism (persistence-knocking + Donchian breakout + range statistics via ATR proximity, exactly as the hypothesis specifies), but must not resubmit a byte-identical hash. Two minimal, targeted edits: (1) performance — the ATR true-range computation is now fully vectorized with numpy instead of a per-bar Python loop, and the working window is bounded to exactly what the model needs (max(trend, donchian+persist+1, atr*4)+5 ~ 61 bars) instead of a fixed 300-bar rebuild, cutting per-bar work substantially for the 300s Layer-3 wall-clock; the persistence check is likewise a vectorized slice comparison rather than a loop. (2) entry frequency — proximity_atr default relaxed from 0.3 to 0.5 ATR, since requiring three consecutive closes inside 0.3 ATR of the channel top AND a breakout close AND a bullish body AND an uptrend is a four-way conjunction whose rarest term is the proximity band; 0.5 ATR keeps the 'knocking' semantics intact while materially raising the chance of >=1 trade in the sandbox window (the optimizer can search around it). Sizing guard tightened to reject sub-1-unit sizes (ADA size precision is whole units, so int() truncation to 0 would silently no-trade). Everything that passed earlier layers — imports, class structure, continuous non-frozen signal, leverage genuinely consumed in position_size (0.60 x 2.0 = 1.2x equity notional) — is untouched.

Hypotheses

data_wall: Not worth 2 hours of optimization: 16 trades over 6.1 years is an unmeasurable sample. The result rests on a single outlier win ($24k largest_win, return_skew 7.41 / kurtosis 82.99, driven by the 2025-03 +23% spike); strip that trade and the edge is gone. Sharpe 0.26 with CI [-0.71, 0.75] straddles zero and information_ratio is -0.62 vs simply holding ADA, so there is no measurable risk-adjusted edge to tune toward — a 3-window walk-forward would fit ~5 trades per window and curve-fit noise. The mechanism is inherently low-frequency single-name, so iteration on thresholds cannot raise the sample without changing the premise. QA's edge_concern (edge carried by a handful of trades, 'normal' vol tercile negative) is confirmed on full history. abandon_class=unmeasurable.

Implementation

Long-only ADAUSDT 4H Binance USD-M futures breakout strategy (leverage 2.0). Detects multi-bar 'knocking on the channel top' persistence: the last 3 closes before the current bar each sit within 0.5x ATR below the 20-bar Donchian high (computed excluding the current bar), and then the current bar closes above that Donchian high with a bullish body while price is above the 50-bar SMA. calculate_signal() returns a continuous per-bar score (tanh of ATR-scaled breakout distance + SMA trend position + per-bar momentum) that varies on every bar; the binary setup gate drives entries. Exits on 4% stop-loss, 8% take-profit, a trend break (close below the SMA), or a 12-bar time stop.

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

Let the optimizer explore proximity_atr in [0.2, 0.6]; confirm the wider band does not dilute the 'building pressure' semantics on full-history results.

Verification Results

Hypothesis states the knocking band is 0.3x ATR; config/default proximity_atr is 0.5 (developer-documented relaxation to raise trigger frequency). Structure (three consecutive closes within an ATR-scaled band below the Donchian high) is intact, so this is a magnitude change within tolerance, not a mechanism mismatch. Noted for the optimizer to search around.

Verification Results

OHLCV-only Donchian-high breakout family has historically weak survival (L81 flags Donchian/50-day-high breakout re-parameterizations as a low-survivor class); the hypothesis argues this 'persistence knocking' variant is structurally distinct, which is plausible but unproven. Sandbox is thin/lumpy: 16 trades, return_skew 7.57 and kurtosis 85.9 (one 24k win vs 5.9k largest loss), Sharpe CI straddles zero (-0.70..0.79), information_ratio -0.63 vs buy-hold, and the 'normal' vol tercile is negative (-9.77%, Sharpe -0.65) while gains concentrate in calm/stressed. Analyst should confirm on full history that the edge is not carried by a handful of trades and survives the 0.5-ATR proximity relaxation.

Backtest Review

Strategy is faithfully built to its hypothesis: 16 entries, all long, on the persistence-then-breakout trigger; avg_trade_return_pct 1.31% clears the Binance USD-M 0.15% fee floor; low max_drawdown (5.8%) and PF 2.34.

Backtest Review

Trades appear to implement the described knocking-on-channel-top breakout mechanism (16 signals from 14,386 evaluated bars, long-only, in-uptrend).

Backtest Review

16 trades over 6.1 years is an unmeasurable sample for a 5-parameter walk-forward optimization — each of the 3 WF windows would fit ~5 trades, guaranteeing the optimizer curve-fits noise (L10/L27 low-trade-count trap).

Backtest Review

The headline is carried by ONE trade: largest_win $24,138 (the 2025-03 +23.3% daily spike) dwarfs everything; return_skew 7.41, kurtosis 82.99. Remove that trade and the edge collapses.

Backtest Review

Sharpe 0.26 with a CI of [-0.71, 0.75] straddling zero — statistically indistinguishable from no edge. information_ratio -0.62 vs buy-hold (underperforms simply holding ADA risk-adjusted).

Backtest Review

Regime attribution: the 'normal' vol tercile is negative (-5.2%, Sharpe -0.27); gains concentrate in a handful of calm/stressed prints, consistent with a lucky-tail rather than a repeatable edge.

Backtest Review

Mechanism is inherently single-name, low-frequency (4H, multi-bar persistence gate), so iteration on thresholds cannot lift the sample without changing the premise. QA's edge_concern is borne out on full history.

Analysis

Backtest gate: - metrics unreliable: 1 trades / 2406 data days is too little evidence (need >=5 trades over >=10 days) — increase signal frequency or widen the window

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['atr_period', 'min_notional', 'persist_bars', 'position_pct', 'trend_period', 'max_hold_bars', 'proximity_atr', 'stop_loss_pct', 'donchian_period', 'take_profit_pct'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: Frozen signal: all 446 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies. - steady_downtrend: Frozen signal: all 446 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies. - flat_ranging: Frozen signal: all 446 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies. - volatility_spike: Frozen signal: all 446 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies. - zero_volume: Frozen signal: all 446 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies. - price_gap: Frozen signal: all 446 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies.

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.