1. Volume Order Blocks Strategy Description
Whenever a fast EMA crosses a slower one, the indicator scans back over the leg that just ended to find the candle that set its extreme, builds an Order Block box from that candle, and tags the box with the volume accumulated over the whole leg leading up to the crossover. Overlapping or invalidated boxes are pruned continuously, and only the surviving ones (up to 15 per side) are drawn, each labelled with its share of the total volume among the boxes currently on screen.
1.1 Indicator Concept
The indicator answers three questions: "Where did the last leg before the current trend actually begin? How much volume traded during that whole leg? And is this zone still relevant, or has price already erased it?" It treats a fast/slow EMA crossover as confirmation that a trend leg has just ended, then looks back to find the exact candle that marked the bottom (for a bullish flip) or the top (for a bearish flip) of that leg — the classic "last candle before the impulsive move" definition of an Order Block.
- The crossover is the trigger, not the zone itself. A fast EMA (length length1, default 5) crossing a slower EMA (always length1 + 13, default 18) marks the moment a down-leg has flipped up (or an up-leg has flipped down). The zone is then built by scanning the candles of the leg that just finished.
- The box is anchored on the leg's extreme candle. For a bullish flip, the indicator finds the candle whose low matches the lowest low of the lookback window — that candle's low and the lower of its open/close become the box's two edges. For a bearish flip, the mirror: the candle whose high matches the window's highest high, using the higher of its open/close as the other edge.
- Volume is accumulated over the whole leg, not just the one candle. The number attached to each box is the sum of every candle's volume from the extreme candle through to the crossover bar — a measure of how much trading happened during the entire move away from that zone, not the volume of a single candle.
- Zones are pruned continuously, not just created once. On every bar, any two same-direction zones that sit too close together (measured in a volatility-based distance) are merged down to the more recent one, and any zone price has already closed back through is dropped entirely — so what remains on screen is a short, de-duplicated list of zones still considered "live".
- Each surviving zone is labelled by its relative volume share. The percentage shown is not a share of total market volume — it is this zone's accumulated volume as a percentage of the combined volume of every other same-direction zone currently surviving on the chart.
In short: an EMA-crossover-triggered Order Block detector with volatility-based de-duplication and invalidation, where each surviving zone is tagged with how much volume built up during the leg that led to it, shown as a share of the other zones currently on screen.
1.2 Indicator Features
-
Display:
- Bullish (demand) zones: a rectangle from the leg's lowest low up to the lower of the extreme candle's open/close, drawn in teal — two solid border lines (2px), a dashed centre line (optional, "Mid Line"), and a 90%-transparent fill between the borders.
- Bearish (supply) zones: the mirror, from the extreme candle's high down to the higher of its open/close, drawn in purple, same line/fill style.
- A volume-share label on the right edge of each zone, reading ⮜ XX.X% (volume) — this zone's share of the combined volume of every other currently-displayed zone on the same side, plus the raw volume figure.
- A trend ribbon (optional, "Trend Shadow"): an invisible fast/slow EMA pair with the space between them filled — teal, intensifying as the fast EMA pulls further above the slow one; purple, intensifying as the slow EMA pulls further above the fast one — both scaled against the gap's own 100-bar range.
- Alerts: The indicator has NO alertcondition() calls.
1.3 How to Use the Indicator
- Treat a teal box as a demand zone and a purple box as a supply zone — the classic order-block idea that price may return to such a zone and react, since it marks where the move away from it originated.
- Use the volume-share label to rank zones against each other, not against the whole market. A zone reading 35% with the others at 10–15% each stood out among the currently-displayed zones on that side — it does not mean 35% of all traded volume.
- Expect the list to stay short and recent. Zones are actively merged when they sit close together and dropped the moment price closes through them, so what you see is usually a handful of the most relevant, still-untested zones, not a long history of every past crossover.
- Adjust "Sensitivity Detection" to control how often new zones appear. A smaller value makes the fast/slow EMA pair cross more often (more, smaller legs, more zones); a larger value makes crossovers rarer (fewer, larger legs). The slow EMA always tracks 13 bars longer than the fast one — you cannot set the two independently.
- Use "Trend Shadow" as a quick-glance momentum gauge between crossovers — a deepening teal ribbon says the fast EMA is pulling away from the slow one on the upside, a deepening purple ribbon the downside, relative to how wide that gap has been over the last 100 bars.
- Remember the zones only redraw on the most recent bar. The detection and clean-up logic runs on every historical bar, but the actual lines/labels are only (re)plotted when the chart reaches its last bar — scrolling back in history will not show you zones "as they looked" on an earlier bar.
1.4 How the Indicator Works
Inputs & their role
- length1 ("Sensitivity Detection: ", integer, default 5): the length of the fast EMA. Example: at 5, the slow EMA is automatically 18; at 10, the slow EMA becomes 23. Smaller values make the pair cross more often, so zones are detected more frequently but each leg behind them is shorter.
- mid_l ("Mid Line", tick box, default on): shows or hides the dashed line through the middle of each zone.
- trend_shadow ("Trend Shadow", tick box, default on): shows or hides the gradient ribbon between the fast and slow EMA. Off = the ribbon's colour is fully transparent; the EMAs themselves are always invisible either way.
- col1 / col2 (two colour swatches, no titles, one row, defaults teal #26ba9f and purple #6626ba): col1 colours every bullish element (zones, mid-lines, fill, the teal side of the trend ribbon); col2 colours every bearish element. Colours only.
Hard-coded constants (NOT inputs)
- The slow EMA length: always length1 + 13 — not independently adjustable.
- Minimum zone height: (highest ATR(200) over the last 200 bars) × 1 — if a zone's natural height (extreme to body edge) is smaller than this, the body edge is pushed out to force at least this height.
- Overlap/merge distance: (highest ATR(200) over the last 200 bars) × 3 — exactly 1.5× the minimum-height value above. Two same-direction zones whose midpoints sit closer together than this are merged into one.
- Maximum zones tracked per side: 15 (oldest is dropped once a 16th is added).
- Indicator-level limits: max_lines_count = 500, max_labels_count = 500, max_bars_back = 5000.
Main logic blocks
⏱️ Flow 0 — Order of work on every candle
- ① update the fast/slow EMA pair and the trend ribbon → ② on a confirmed crossover, scan the just-finished leg and create a new zone → ③ on every bar, prune zones that have merged into a neighbour or been invalidated by price, and cap the list at 15 → ④ only on the chart's last bar, clear and redraw every surviving zone's lines, fill and volume label.
🌊 Flow 1 — The EMA pair and the trend ribbon
- ema1 = EMA(close, length1), ema2 = EMA(close, length1 + 13). cross_up / cross_dn fire when ema1 crosses ema2, gated to confirmed (closed) bars only.
- difff = ema2 − ema1: negative while the fast EMA leads (uptrend), positive while the slow EMA leads (downtrend). The invisible fill between the two EMAs is coloured by where difff sits within its own 100-bar high/low range — fully transparent near a fresh cross (gap near zero), fully coloured at the widest gap seen in the last 100 bars.
📦 Flow 2 — Detecting a new zone at each crossover
- On cross_up (bullish): scan bars 1 to length2 back (skipping the crossover bar itself) for the one whose low equals lowest (the lowest low of that window). Once found at offset i: sum every candle's volume from the current bar back through that candle (i + 1 candles' worth) → the zone's volume figure. The zone's upper edge is the lower of that candle's open/close; its lower edge is lowest itself. If the natural gap between them is under the minimum-height constant, the upper edge is pulled down to enforce it.
- Concrete example: a down-leg's lowest low is 61,200, and the candle that made it closed at 61,340 (below its open 61,410) — upper edge 61,340 (the close, since it's lower than the open), lower edge 61,200, height 140. If the minimum-height constant is 220, the upper edge is pulled down to 61,420 instead (61,200 + 220) so the box is at least that tall.
- On cross_dn (bearish): the mirror — scan for the candle whose high equals highest, volume summed the same way, upper edge = highest, lower edge = the higher of that candle's open/close (pulled up if needed to meet the minimum height).
- If no historical candle in the window exactly matches the window's extreme (for example, if the crossover bar itself happens to be the extreme), the scan finds nothing and no zone is created for that crossover.
🧹 Flow 3 — Pruning: overlap, invalidation, and the 15-zone cap
- On every bar (not only at a crossover), the whole list of currently-tracked zones on each side is re-scanned from oldest to newest. For each zone, two checks run: is it too close to its neighbour, and has price closed through it.
- Overlap: if a zone's midpoint sits within the merge distance of the previous zone's midpoint, the older of the two is blanked out, leaving only the newer one. Example: two bullish zones with midpoints 4 points apart, merge distance 150 — they are within range, so the older one is removed.
- Invalidation: a bullish zone is blanked out the moment close falls below its lower edge (price has fully closed through the demand zone); a bearish zone is blanked out once close closes above its upper edge.
- The oldest-vs-newest comparison: the very first zone checked in the scan (the oldest one in the list) is compared against the last zone in the list rather than a non-existent "one before the first" — so this specific pair is an oldest-vs-newest check, not oldest-vs-nothing, while every other pair in the scan compares a zone to the one immediately before it.
- Once the list holds more than 15 zones (counting any already-blanked ones not yet physically removed), the oldest slot is dropped.
🖊️ Flow 4 — Drawing on the last bar
- Only when the script reaches the chart's current last bar: every previously drawn line/fill/label for a side is deleted, then redrawn fresh from whatever zones currently survive in that side's list.
- For each zone: two solid 2px border lines (upper and lower edge) from the zone's anchor candle to the current bar, an optional dashed line at the midpoint, a 90%-transparent fill between the two borders, and a label at the right edge reading the zone's share of the combined volume of all currently-surviving same-direction zones plus the raw volume number.
Outputs & their role in trading
- Teal / purple zones: demand / supply areas anchored on the candle that started the move away from them.
- The volume label: a relative ranking of how much trading happened on the leg behind each currently-visible zone.
- The trend ribbon: a quick EMA-momentum gauge between crossovers.
- The oldest-vs-newest comparison relies on Pine resolving a negative array index by counting from the end of the array. The clean-up loop reads the "previous" zone as array.get(i − 1), and at i = 0 this becomes array.get(-1). This indicator (and the strategy already derived from it in this project, with a working backtest on record) clearly runs in practice, which is the strongest evidence that Pine treats a negative array index here as "count from the end" rather than raising an error. One concrete consequence of that behaviour: when a side's list holds exactly one zone, that lone zone is compared against itself (index 0 wraps to itself), and since a value is always within merge distance of itself, it is immediately blanked out the same bar it was created — a lone, isolated zone with no neighbour is a harder case to keep on screen than one that already has company. Worth confirming directly on your own chart if an isolated zone ever seems to vanish immediately.
- Blanked (pruned) zones are not physically removed from the list — they are overwritten with an empty placeholder and only leave the list once the 15-zone cap pushes them out from the front. They do not contribute real volume to the label percentages (the summing step skips absent/blank values), but if the drawing step in Flow 4 ever reaches a still-blanked slot, drawing a line from an undefined position is the kind of thing that would normally either fail or silently skip — again, the fact that this script is already running as a working strategy in this project is the practical evidence that this does not cause trouble in normal use.
- The volume figure is a leg total, not a single candle's volume. It sums every candle from the anchor candle through to the crossover bar, so a longer leg (more candles between the extreme and the crossover) naturally accumulates a larger number even if trading activity per candle was unremarkable.
- The percentage is relative, not absolute. It is always measured against the sum of the other zones currently surviving on that side — as older zones get merged or invalidated and drop out of the list, the same zone's displayed percentage can change over time even though its own volume figure never does.
- The slow EMA length is not a free setting — it is permanently Sensitivity Detection + 13, so the two EMAs cannot be tuned independently.
- No alerts. Anything event-based (a new zone appearing, a zone being invalidated) would have to be added by hand.


