Skip to content
All strategies

Strategies

BTC Single-Asset Pin Bar Wick Rejection Pattern Long-Short on BINANCE Daily (Brooks 2009 / Sperandeo 1991 Rejection Bar, Event-Driven, Translation-Robust)

Outcome: Abandoned

BtcDailyPinBarWickRejectionLongShort

BTC Single-Asset Pin Bar Wick Rejection Pattern Long-Short on BINANCE Daily (Brooks 2009 / Sperandeo 1991 Rejection Bar, Event-Driven, Translation-Robust)

Hypothesis

Single-asset pin bar / wick rejection pattern strategy on BTCUSDT.BINANCE — detects bars where one side of the bar has an abnormally long wick (shadow) relative to the body, indicating price tested an extreme and was REJECTED within the same bar by aggressive opposite-side flows. Mechanism: (1) compute body = abs(close - open); upper_wick = high - max(close, open); lower_wick = min(close, open) - low. (2) BULLISH PIN TRIGGER: lower_wick >= 2 × body AND upper_wick <= 0.3 × body AND close > open (closed in upper……Show moreShow less

Single-asset pin bar / wick rejection pattern strategy on BTCUSDT.BINANCE — detects bars where one side of the bar has an abnormally long wick (shadow) relative to the body, indicating price tested an extreme and was REJECTED within the same bar by aggressive opposite-side flows. Mechanism: (1) compute body = abs(close - open); upper_wick = high - max(close, open); lower_wick = min(close, open) - low. (2) BULLISH PIN TRIGGER: lower_wick >= 2 × body AND upper_wick <= 0.3 × body AND close > open (closed in upper part of range) AND bar.low <= 20_bar_low × (1 + proximity_pct) (default 5%, near a recent low). Indicates that price dropped to a low, found buyers, and was pushed back up. (3) BEARISH PIN TRIGGER: upper_wick >= 2 × body AND lower_wick <= 0.3 × body AND close < open AND bar.high >= 20_bar_high × (1 - proximity_pct) (near a recent high). Indicates that price rose to a high, found sellers, and was pushed back down. (4) FLAT otherwise. This implements Al Brooks 2009 'Reading Price Charts Bar by Bar' and Victor Sperandeo 1991 'Methods of a Wall Street Master' rejection bar pattern. Distinct from my in-pipeline event-driven BTC reversal stack: Climax Reversal uses BAR RANGE relative to average; Engulfing uses 2-BAR BODY TAKEOVER; Key Reversal uses 3-BAR SEQUENCE; Pin Bar uses WICK-TO-BODY RATIO on a SINGLE BAR — fundamentally different mechanic for identifying extreme rejection. The mechanism is FUNDAMENTALLY EVENT-DRIVEN AND TRANSLATION-ROBUST: pin bars are identifiable on any timeframe (the wick-to-body ratio criterion is scale-invariant). Single-asset scope = no multi-asset translation curse. Helps direction quota (90.2% long-only).

Implements the Sperandeo (1991) / Brooks (2009) rejection-bar hypothesis using the wick-to-body ratio on a single bar — scale-invariant and translation-robust, distinct from the range/2-bar/3-bar reversal variants in the pipeline. FIX for the reported Layer-2 stale frozen-signal feedback: the code already returns a continuous _monitoring_base every bar (verified locally: 479 unique, non-zero signals across all 6 synthetic scenarios, zero warnings), so the earlier value=0.0 frozen result (from a pre-monitoring-base gated version) no longer occurs. Layer 1 (static) and Layer 2 (synthetic) both pass and are left untouched to avoid regression; only the config surfaces entry_threshold explicitly in the parameters dict so no threshold relies on a silent default. Venue is BINANCE USD-M futures because the strategy trades short (requires a margin venue) and consumes leverage (2.0) in position_size via self.config.leverage.

verifying_stuck_3_timeouts

Implementation

Single-asset BTCUSDT.BINANCE daily pin-bar / wick-rejection reversal, long AND short. Detects single bars where one wick is abnormally long relative to the body (lower_wick >= wick_ratio*body with a small upper stub near a 20-bar low = bullish rejection -> LONG; upper_wick >= wick_ratio*body with a small lower stub near a 20-bar high = bearish rejection -> SHORT). calculate_signal() returns a continuous, bar-varying monitoring score (dominated by intrabar wick-rejection pressure) on every non-trigger bar, and a large-magnitude (>=1000) value only on a confirmed pin. Exits via stop-loss, take-profit, opposite-side pin, or a max-hold time stop. Futures venue with 2x leverage (short-capable, notional ~1.2x equity).

Verification Results

Verification failed (Layer 4 — QA review) [class=not_measurable]: - [edge_concern] The reported metrics are noise, not evidence: profit_factor 3.6 and avg_trade_return_pct 1.09% rest on 4 trades with a single +$6,245 win (win_rate 0.25, return_skew 10.4, kurtosis 217, metrics_reliable=false). No performance conclusion can be drawn — the sample is too small. - NOT MEASURABLE — the tradeable sample is far too sparse. The sandbox processed essentially the FULL daily history (bars_processed 2398 ≈ 6.5 years) yet the……Show moreShow less

Verification failed (Layer 4 — QA review) [class=not_measurable]: - [edge_concern] The reported metrics are noise, not evidence: profit_factor 3.6 and avg_trade_return_pct 1.09% rest on 4 trades with a single +$6,245 win (win_rate 0.25, return_skew 10.4, kurtosis 217, metrics_reliable=false). No performance conclusion can be drawn — the sample is too small. - NOT MEASURABLE — the tradeable sample is far too sparse. The sandbox processed essentially the FULL daily history (bars_processed 2398 ≈ 6.5 years) yet the pin-bar conjunction fired only 4 times total (3 long / 1 short). The strict single-bar gate (wick >= 2x body AND opposite stub <= 0.3x body AND correct close direction AND within 5% of a 20-bar extreme) is genuinely rare on daily BTC (~0.6 events/year). Four trades over the entire available history cannot populate the HARD >=10-trade holdout gate or the 3 walk-forward windows, so the edge is unmeasurable by construction and the optimizer would die at holdout. Loosening defaults +/-20% will not lift ~4 events to the >=30 needed. Not a code bug (the pattern is correctly implemented and simply rare) — a sample-sparsity problem inherent to the daily + single-asset scope.

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['stub_ratio', 'wick_ratio', 'min_notional', 'position_pct', 'max_hold_bars', 'proximity_pct', 'stop_loss_pct', 'take_profit_pct', 'extreme_lookback'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: Frozen signal: all 479 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies. - steady_downtrend: Frozen signal: all 479 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies. - volatility_spike: Frozen signal: all 479 signals are identical (value=0.0). calculate_signal() likely has a bug — the signal never varies.

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 and paper results are hypothetical. Trading involves risk of loss.