1. Statistical Order Blocks Strategy Description
Detects ICT/SMC-style displacement Order Blocks (OBs) while running a lookahead-free, self-backtesting engine inside the indicator itself, stamping live Win Rate, P(Touch), Expected Value, and R:R directly onto the label of every newly-formed OB zone.
1.1 Indicator Concept
This indicator is built on the classic ICT/Smart Money Concepts Order Block (OB) idea: the LAST opposite-colored candle before a sufficiently strong "displacement" move away from it is treated as the origin of unfilled institutional orders - a price zone that tends to "pull" price back for a reaction later.
What sets this indicator apart: it doesn't just draw OB zones - it also runs a self-contained, lookahead-free backtesting engine INSIDE the indicator itself, in parallel. Every confirmed bar, it looks back exactly "Max Bars to Simulate Outcome" bars - far enough that the outcome has definitely already played out - "rediscovers" ONE historical OB opportunity that just became fully resolved (if a pivot happened to sit exactly there), and folds its actual outcome (was it touched? did it win or lose? how many bars did it take?) into a running counter kept separately for the bull and bear side.
- Every NEW OB zone comes stamped with its own "track record": the info label on each OB shows Win Rate, Probability of Touch (P(Touch)), Expected Value (EV in ATR units), R:R, average bars to win/lose, and sample size (n) - all computed from the ENTIRE history of same-side OBs that have actually resolved on THIS chart with THESE current settings, not a generic pre-baked number.
- Every target/stop is ATR-relative, not a fixed pip distance, so the same logic applies more consistently across different pairs/timeframes without much manual retuning.
- OB zone height can be normalized to a fixed ATR multiple instead of the origin candle's actual range - purely for visual consistency across zones of very different raw sizes.
1.2 Indicator Features
- Bullish/Bearish OB Zones: colored boxes matching each direction, left edge pinned at the true origin candle, right edge auto-extending into the future while the zone stays "alive" (not yet mitigated).
- Statistical Label: a single-line string showing WR / (Wins/Losses) / P(Touch) / EV / R:R / AvgW / AvgL / zone size (pips) / sample n - continuously "chases" price, repositioning to 5 bars ahead every new bar instead of staying fixed where the zone formed.
- Mitigation-Driven Lifecycle: an OB zone disappears (or moves to "history") the instant price closes through it - this check runs on EVERY tick, not just confirmed bars, so it reacts almost instantly.
- Historical OBs (optional): after being mitigated, a zone can be kept on the chart as a faded (label-less) ghost instead of being deleted outright - the original tooltip warns this feature "can be slow on very long history".
- Two-tier FIFO cap: the number of active OB zones per side is capped by "Max Active OBs Per Side"; the number of faded historical zones per side is capped at 3× that same number.
- Fully customizable: separate background/border colors per side, label text size, and independent show/hide toggles for each side and each visual layer (active zones / historical zones / labels).
1.3 How to Use the Indicator
- Read the statistical label BEFORE trusting a new OB: a low Win Rate or negative EV for that side on this exact chart/settings is a real warning, not just decoration - it's the result of a live self-backtest running on the data you're actually looking at.
- Understand what "P(Touch)" actually means: it's the probability an OB of this type EVER got revisited by price, not a win probability. A low P(Touch) means most zones of this type never get retested at all - waiting for a "retest entry" on this chart could mean missing most of the opportunities.
- Compute your own TP/SL if you need them for order placement: the indicator already computes tp_price/sl_price for every zone (mid ± ATR×"Take Profit", far edge ± ATR×"Stop Loss") but does NOT plot either level on the chart or in the label - you'd need to recompute them with the same formula if you want to see them visually.
- The displayed R:R is always fixed at "Take Profit"/"Stop Loss" (default 3.0/6.0 = 1:2), identical for EVERY OB regardless of its actual range - because both TP and SL are defined purely as ATR multiples.
- Turn off "Show Historical OBs" on lower timeframes / very long charts if the indicator starts lagging - this is a built-in performance warning from the original tooltip, not a guess.
- Increase "OB Candle Lookback" / "Displacement Multiplier" to only keep OBs with a genuinely strong, decisive displacement (fewer but higher-quality zones); decrease to catch more zones.
- Weigh "Max Bars to Simulate Outcome" carefully: too small and the stats engine gives up early on slow-moving setups (potentially understating the true win rate); too large and new stats take longer to appear (since a sample only finishes resolving that many bars after its OB forms).
1.4 How the Indicator Works
Inputs & Roles
- swing_len ("Swing Detection Length", Integer, default: 9, range 3-30): bars on each side to confirm a pivot. Increase → only catches larger, more significant swing points. Decrease → more pivots, noisier.
- lookback_bars ("OB Candle Lookback", Integer, default: 20, range 5-50): number of bars before a pivot that get scanned to find the OB's origin candle.
- disp_mult ("Displacement Multiplier (ATR)", Float, default: 1.5): the displacement from the OB candle out to the pivot must be ≥ this many ATRs to qualify. Example: ATR=0.0050, disp_mult=1.5 → the move must be ≥ 0.0075 to pass.
- normalize_zones ("Normalize All Zone Heights", Boolean, default: true) / norm_atr_mult ("Zone Height (ATR Multiplier)", Float, default: 0.75): when ON, EVERY OB zone is forced to the same height = current ATR × norm_atr_mult (ignoring the origin candle's real range), keeping the zone's original midpoint.
- tp_atr_mult ("Take Profit (ATR Multiplier)", Float, default: 3.0): TP target = zone midpoint ± ATR×tp_atr_mult.
- sl_atr_mult ("Stop Loss (ATR Multiplier)", Float, default: 6.0): SL = the zone's far edge ± ATR×sl_atr_mult. With the 3.0/6.0 defaults, R:R is always fixed at 1:2 for every zone.
- max_sim_bars ("Max Bars to Simulate Outcome", Integer, default: 100, range 20-300): simultaneously (a) the maximum number of bars the stats engine waits for a historical OB to resolve before giving up (not counted as win or loss), AND (b) the fixed lag between when an OB forms and when its outcome gets folded into the statistics.
- max_obs_per_side ("Max Active OBs Per Side", Integer, default: 7): maximum number of active zones per side; the OLDEST is FIFO-retired once the limit is exceeded, regardless of whether it's still price-valid.
- zone_extend ("Zone Extension (bars)", Integer, default: 200): number of bars the box extends into the future while still active.
- show_bull / bull_bg / bull_border, show_bear / bear_bg / bear_border: independent toggle and background/border colors for each side.
- show_hist ("Show Historical OBs", Boolean, default: true): keeps a mitigated zone as a faded ghost instead of deleting it outright.
- show_labels ("Show Statistical Labels", Boolean, default: true) / lbl_txt_size ("Label Text Size", default: "Large"): toggle and text size for the statistical label.
Main Logic Blocks
🎯 Flow 1: Historical Statistics Replay - Bullish Side
- On every confirmed bar: check whether a confirmed pivot low sat exactly max_sim_bars bars back (ta.pivotlow(...)[check_offset]). If so: scan forward from that pivot (using the same swing_len/lookback_bars/disp_mult logic as live OB detection) to find a qualifying bearish candle as the "historical OB candle".
- If found: hist_bull_total += 1, computes tp_h/sl_h from that moment's ATR and the CURRENT tp_atr_mult/sl_atr_mult. Then walks FORWARD (from the pivot toward now), one bar at a time: the first bar price touches the zone → hist_bull_touches += 1; once touched, whichever bar hits tp_h first → counted as a win (accumulating bars into hist_bull_win_bars), whichever hits sl_h first → counted as a loss. If max_sim_bars bars pass without either → neither win nor loss is recorded (it still counts toward n=total).
- Example: max_sim_bars=100. At the current bar, check whether bar (current-100) had a pivot low - yes → find a bearish OB candle at (current-112). tp_h=mid+ATR×3.0, sl_h=low-ATR×6.0. Walk from (current-99) to (current-1): whichever bar touches the zone first → touches+=1; whichever bar reaches tp_h/sl_h first afterward → added to wins/losses respectively.
🎯 Flow 2: Historical Statistics Replay - Bearish Side
- Fully symmetric to Flow 1, using a pivot high, finding a bullish candle as the "historical OB candle", tp_h BELOW the midpoint, sl_h ABOVE the top - accumulating into hist_bear_*.
🧮 Flow 3: Computing Aggregate Statistics
- compute_stats() runs for both sides right after Flows 1 and 2 just updated their counters: Win Rate = wins/(wins+losses), P(Touch) = touches/total, EV = win_fraction×tp_atr_mult - loss_fraction×sl_atr_mult (defaults to a 50% assumption when no sample has resolved yet, so EV isn't NA from the very start), R:R = tp_atr_mult/sl_atr_mult (fixed, independent of the statistical sample).
🖊️ Flow 4: Detecting & Drawing a Live Bearish OB
- When a NEW pivot high just confirmed (no offset, real time): scans from swing_len+1 to swing_len+lookback_bars bars BEFORE the pivot, looking for the FIRST (closest to the pivot) bullish candle whose displacement (from that candle's high down to the pivot's low) ≥ ATR×disp_mult.
- If found: applies normalize_zone() (using the CURRENT ATR, not the ATR at the time the OB candle formed), computes tp_price/sl_price specific to this zone (stored on the struct but NOT drawn on the chart), draws the bearish box from the origin candle to bar_index+zone_extend, with a statistical label (if enabled) using the freshly computed bear_wr/bear_pt/... from Flow 3. Pushes into the bear_obs array.
- Example: a pivot high confirms, scanning backward finds a bullish candle at i=14 with high=1.2650, low=1.2620. normalize_zones=true, current ATR=0.0050, norm_atr_mult=0.75 → half_h=0.0050×0.75/2=0.001875, mid=1.2635 → norm_top=1.265375, norm_bot=1.261625 (a fixed ~0.00375 zone height regardless of the origin candle's actual 0.0030 range). tp_price=1.2635-0.0050×3.0=1.2485, sl_price=1.265375+0.0050×6.0=1.295375 (both values are stored internally only, never shown on the chart).
🖊️ Flow 5: Detecting & Drawing a Live Bullish OB
- Fully symmetric to Flow 4, using a pivot low, finding a bearish candle as the OB origin, tp_price ABOVE the midpoint, sl_price BELOW the zone's bottom. Pushes into the bull_obs array.
🗑️ Flow 6: Mitigation Check - runs on every tick
- Unlike the flows above (confirmed-bar only), this one runs on EVERY tick. A bullish zone is mitigated when close < ob.bot; a bearish zone when close > ob.top.
- If show_hist is ON: the zone moves into the historical array (hist_bull_obs/hist_bear_obs) - its right edge is frozen at the current bar, auto-extend is cancelled, its color switches to a faded version (95% transparent background, 90% transparent border), and its label's TEXT IS CLEARED (set to empty, the label object itself is NOT deleted). If show_hist is OFF: the box and label are deleted outright, simply removed from the active array.
- Example: a bullish zone with ob.bot=1.2616. Price closes at 1.2610 (<1.2616) → mitigated. If show_hist is ON: the box freezes in place, switches to a faded color, its label goes blank but stays alive; if OFF: both box and label are deleted entirely.
📦 Flow 7: FIFO Cap on Active Zones
- If bull_obs.size() > max_obs_per_side (or the same for bear_obs): deletes the OLDEST zone by creation order (regardless of whether it's still price-valid) - fully independent of the mitigation logic in Flow 6.
🔄 Flow 8: Per-Bar Visual Refresh (still-active zones)
- For every zone NOT yet mitigated: extend the box's right edge to bar_index+zone_extend, AND if labels are shown, move the label to coordinates (bar_index+5, zone midpoint) - the label continuously "chases" the current bar, always floating 5 bars ahead of price, while the box's left edge stays pinned at the origin candle.
📦 Flow 9: FIFO Cap on Historical (Faded) Zones
- If the number of zones in hist_bull_obs/hist_bear_obs exceeds max_obs_per_side × 3: deletes the oldest one - bounding the number of accumulated boxes on very long charts, matching the performance warning in show_hist's tooltip.
Outputs & Usage Roles
- Active OB zones: a dynamic support/resistance zone anchored at its true origin candle, auto-extending into the future until mitigated.
- Historical (faded) OB zones, optional: a visual trail of past "expired" zones, useful for eyeballing zone density without cluttering the chart with full labels.
- Statistical label: a continuously-updated, lookahead-free track record, freshly printed onto EVERY new zone as it forms - read it as "how has this exact detection logic performed on this chart/settings so far", not a guarantee for this specific new zone.
- Stored (undisplayed) tp_price/sl_price: pre-computed ATR-based target/stop levels for each specific zone, ready to be wired into real order-placement logic during Phase B, even though the current indicator never plots them.


