FlowscopeDocs

Aggregated spot bands

Sum the order books of several spot exchanges and draw where bids or asks dominate as a coloured band between two distances from the mid, on any chart of the coin.

Perpetual futures move the price, but spot books show where buyers and sellers with real coins wait. This script sums the order books of the spot exchanges you pick (Binance and Coinbase by default) into one book and reads its balance within a few percent of the mid. When bids gain the upper hand, a band is drawn below the price between the inner and the outer edge; when asks do, above it. The colour is the tier the reading reached, three per side.

It runs on any chart of the coin, including a Binance futures chart: the books come from the spot markets, whatever the chart shows. It ships with Flowscope as a community script. Classic mode keeps the settings, defaults and colours of the order-flow tool traders know it from; Adaptive mode, the default, fixes what makes the classic rule hard to use on the full book (below).

aggregated-spot-bands.fs
script "Aggregated Spot Bands"

// Where spot buyers and sellers stack their orders, on any chart of the coin.
//
// The order books of the picked spot exchanges are summed into one book
// (levels added up in shared price buckets). Within the outer edge of the
// mid price the script reads
//
//   ratio = (bids - asks) / (bids + asks)
//
// by notional, from -1 (all asks) to +1 (all bids). Cumulative depth counts
// all liquidity from the mid out to the outer edge; Differential only the
// liquidity between the inner and outer edges. Book depth (levels) can
// limit the count to the nearest N price levels of each side, as a data
// plan with a level limit sees the book.
//
// Classic mode compares the ratio itself with the Lvl thresholds, as the
// original indicator does. Spot books lean to one side for hours (asks
// usually outweigh bids within a few percent), so fixed thresholds either
// never fire on the full book or fire all the time. Adaptive mode (the
// default) removes that standing lean: it scores each bar's ratio against
// the last `Baseline` bars (z = (ratio - mean) / standard deviation) and
// draws a band only when the book turns unusually bid- or ask-heavy.
//
// A bullish reading draws a band below the price, between the inner and the
// outer edge (mid * (1 - inner %) to mid * (1 - outer %)), where the bid
// support sits; a bearish one draws it above the price, where the ask wall
// is. The band follows the mid of each bar's book, so its edges move with
// the price. The colour is the tier the reading reached (Lvl 1, 2 or 3 of
// its side); below Lvl 1 there is no band. On the bar where the reading
// changes sign the band is left out, so it does not flicker.
//
// The legend reads "Spot Bands | N ex | ratio" (Adaptive: "| z score" too).

input (
  input.tab(title: "Settings")
  input.section(title: "Spot Exchanges")
  spotExchanges = input.exchanges(["binance", "coinbase"], title: "Spot Exchanges", minSelected: 1, filter: ["binance", "coinbase", "bybit", "okx", "kraken"], description: "Spot order books to aggregate. Depth is summed over the picked venues. Default = Binance + Coinbase. A change restarts the script.")
  input.section(title: "Signal")
  mode = input.select("Adaptive", title: "Mode", options: ["Adaptive", "Classic"], description: "Adaptive (default): a band shows when the book is unusually bid- or ask-heavy compared with its own recent bars, so the standing ask lean of spot books does not paint a band all the time. Classic: the fixed ratio thresholds of the original indicator; Depth Mode Auto and Book depth set to 250 or 4096 limit how much of the book is read.")
  baselineMinutes = input.int(60, title: "Baseline (minutes)", min: 1, max: 1440, description: "Adaptive mode: how long a stretch the ratio is compared with, in minutes, on every chart interval (60 = the last hour: 60 bars on 1m, 360 bars on 10s, 12 on 5m). Shorter reacts faster and fires more often; longer only flags what is unusual over a longer stretch.")
  persistBars = input.int(1, title: "Persistence (bars)", min: 1, max: 10, description: "How many closed bars in a row a side must reach Lvl 1 before its band shows. 1 (default) = at once. 2 or more hides single-bar flashes, often orders that were pulled again (spoofs), at the cost of a later band.")
  input.section(title: "Band Settings")
  showBand = input.bool(true, title: "Show Band", description: "Show or hide the liquidity imbalance band on the chart.")
  drawAt = input.select("Near price", title: "Draw band", options: ["Near price", "Book edges"], description: "Near price (default): the band hugs the candles, from half an ATR beyond the bar's low (bids) or high (asks), 3 ATR or 0.15 % of the price tall, so it stays in view at any zoom. Book edges: between the Inner and Outer Edge off the mid, where the measured orders rest, as the original draws it; 1-2.5 % away it is only in view when the chart is zoomed out.")
  depthMode = input.select("Auto", title: "Depth Mode", options: ["Cumulative", "Differential", "Auto"], description: "Cumulative: all liquidity from mid price out to the Outer Edge. Differential: only the liquidity between the Inner and Outer edges, so the busy book near the mid is ignored and orders stacked further out count. Auto (default): Differential in Adaptive mode, Cumulative in Classic mode (as the original).")
  innerEdge = input.float(1.0, title: "Inner Edge (%)", min: 0.0, max: 50.0, description: "Price distance from mid (%) where the band starts. In Differential mode liquidity tighter than this is excluded. In Cumulative mode the band still starts here visually but the depth measurement begins at mid.")
  outerEdge = input.float(2.5, title: "Outer Edge (%)", min: 0.1, max: 50.0, description: "Price distance from mid (%) where the band ends. This is how far into the order book the indicator looks. Wider values capture deeper walls but react slower to changes near the top of book.")
  bookLevels = input.int(0, title: "Book depth (levels)", min: 0, max: 100000, description: "How many price levels of each side count, nearest the best bid and ask first. 0 (default) = the full book up to the Outer Edge. A level is one price bucket of the summed book ($1 on BTC), so 250 levels only reach about 0.3 % from the mid and the Outer Edge is never reached.")
  activation = input.float(0.0, title: "Activation (ratio)", min: 0.0, max: 1.0, description: "Classic mode: minimum imbalance ratio before any band color shows. Ratio = (bids - asks) / (bids + asks), 0.0 to 1.0. 0.0 (default) = any imbalance shows color. 0.10 = one side must hold at least 10% more liquidity.")

  input.tab(title: "Thresholds")
  input.section(title: "Adaptive Thresholds", description: "Adaptive mode: how far the ratio must stand from its baseline average, in standard deviations (σ), for each tier. 2σ lies well outside the ratio's usual swings over the baseline.")
  bullZ1 = input.float(2.0, title: "Bull Lvl 1 (σ)", min: 0.0, max: 20.0, description: "Bids gained this many standard deviations over the baseline: mild bullish band.")
  bearZ1 = input.float(2.0, title: "Bear Lvl 1 (σ)", min: 0.0, max: 20.0, sameLine: true, description: "Asks gained this many standard deviations over the baseline: mild bearish band.")
  bullZ2 = input.float(2.5, title: "Bull Lvl 2 (σ)", min: 0.0, max: 20.0, description: "Moderate bullish band.")
  bearZ2 = input.float(2.5, title: "Bear Lvl 2 (σ)", min: 0.0, max: 20.0, sameLine: true, description: "Moderate bearish band.")
  bullZ3 = input.float(3.0, title: "Bull Lvl 3 (σ)", min: 0.0, max: 20.0, description: "Strong bullish band.")
  bearZ3 = input.float(3.0, title: "Bear Lvl 3 (σ)", min: 0.0, max: 20.0, sameLine: true, description: "Strong bearish band.")
  input.section(title: "Bullish Thresholds (Classic)")
  bull1 = input.float(0.30, title: "Bull Lvl 1", min: 0.0, max: 1.0, description: "Ratio at which the Lvl 1 (mild) bullish color appears. 0.30 = bids exceed asks by 30% of total liquidity.")
  bull1Color = input.color(#0C5C17, title: "Bull Lvl 1 color", sameLine: true)
  bull2 = input.float(0.46, title: "Bull Lvl 2", min: 0.0, max: 1.0, description: "Ratio for Lvl 2 (moderate) bullish color. 0.46 = bids outweigh asks by 46% of total depth.")
  bull2Color = input.color(#08A212, title: "Bull Lvl 2 color", sameLine: true)
  bull3 = input.float(0.68, title: "Bull Lvl 3", min: 0.0, max: 1.0, description: "Ratio for Lvl 3 (strong) bullish color. 0.68 = bids dominate asks by 68% of total depth.")
  bull3Color = input.color(#00E1FF, title: "Bull Lvl 3 color", sameLine: true)
  input.section(title: "Bearish Thresholds (Classic)")
  bear1 = input.float(0.25, title: "Bear Lvl 1", min: 0.0, max: 1.0, description: "Ratio at which the Lvl 1 (mild) bearish color appears. 0.25 = asks exceed bids by 25% of total liquidity.")
  bear1Color = input.color(#670E12, title: "Bear Lvl 1 color", sameLine: true)
  bear2 = input.float(0.50, title: "Bear Lvl 2", min: 0.0, max: 1.0, description: "Ratio for Lvl 2 (moderate) bearish color. 0.50 = asks outweigh bids by 50% of total depth.")
  bear2Color = input.color(#AF07AB, title: "Bear Lvl 2 color", sameLine: true)
  bear3 = input.float(0.68, title: "Bear Lvl 3", min: 0.0, max: 1.0, description: "Ratio for Lvl 3 (strong) bearish color. 0.68 = asks dominate bids by 68% of total depth.")
  bear3Color = input.color(#00E1FF, title: "Bear Lvl 3 color", sameLine: true)

  input.tab(title: "About")
  input.group(title: "Overview")
  input.section(title: "What this indicator does", description: "Aggregates order book depth across the picked spot exchanges. Computes a bid/ask imbalance ratio within a configurable price range and renders it as a colored band. Band appears below price when bids dominate, above price when asks dominate.")
  input.section(title: "Why spot books", description: "Spot orders represent unleveraged capital. A $5M spot bid requires $5M in capital vs $500K on 10x futures. Spot book data is less open to spoofing and shows genuine positioning. The tradeoff is lower notional depth than futures.")
  input.section(title: "Why multi-exchange", description: "Any single venue can have thin, spoofed, or market-maker-dominated books. Aggregating across exchanges gives a more representative view of total market liquidity. The books are lined up on the first exchange's mid before they are summed, since USD and USDT prices differ by up to about $100 on BTC.")
  input.group(title: "Modes")
  input.section(title: "Adaptive (default)", description: "Spot books lean to one side for hours: within 2.5 % of the mid, asks usually outweigh bids, so the raw ratio sits around -0.1 to -0.2 for a whole day. Adaptive mode scores each bar against its own last Baseline bars: z = (ratio - average) / standard deviation. A band shows only when bids (green, below) or asks (red, above) gain unusually fast, 2σ or more. That removes the permanent red on top.")
  input.section(title: "Classic", description: "The original rule: the ratio itself against fixed thresholds (Bull 0.30, Bear 0.25). With the full book the ratio rarely reaches them; with a level-limited book (Book depth 250) it reaches them in short spikes. Pick it to compare with the original indicator.")
  input.section(title: "Measured", description: "On one day of BTC (Binance + Coinbase spot, 1m, about 1,000 bars), Adaptive with Differential depth showed a band on 9 % of bars: 22 bullish bands, 73 % followed by a higher price 15 minutes later (77 % after 30 min); 14 bearish bands, 64 % followed by a lower price (71 % after 30 min). Classic with the full book showed none; with 250 levels, three bearish spikes. One day is a small sample: treat it as a sanity check, not a promise.")
  input.group(title: "Book depth limits")
  input.section(title: "Book depth (levels)", description: "Book depth limits how many price levels of each side are read. On BTC a level is $1, so 250 levels cover only about ±0.3 % around the mid and the 1-2.5 % band edges are never reached: the ratio describes the top of the book and jumps with every order there. 4,096 levels cover the whole 2.5 % range: on BTC the same as the full book. Flowscope reads the full book by default (0); set 250 or 4096 to compare limited depths.")
  input.group(title: "Band Mechanics")
  input.section(title: "Inner / Outer Edge", description: "Inner Edge (%) and Outer Edge (%) define the price range monitored. With Inner 1% / Outer 2.5%, the indicator reads depth between 1% and 2.5% from mid price. The imbalance in this zone sets band color and intensity.")
  input.section(title: "Imbalance ratio", description: "Computed as (bids - asks) / (bids + asks) across all picked exchanges. Range: -1.0 (all asks) to +1.0 (all bids). The legend shows the live ratio, and in Adaptive mode its z score.")
  input.section(title: "Band positioning", description: "Bullish reading: band renders below price, bearish above. Near price (default) draws it right beyond the candle (from 0.5 ATR, 3 ATR or 0.15 % tall), so it is readable at a normal zoom. Book edges draws it between the Inner and Outer Edge, where the measured bids or asks rest, as the original does.")
  input.section(title: "Flip suppression", description: "On the bar where the reading changes sign (bull to bear or vice versa), the band goes transparent. This prevents flicker from rapid oscillation. The band resumes on the next bar with the new direction confirmed.")
  input.group(title: "Depth Modes")
  input.section(title: "Cumulative", description: "Counts all liquidity from mid price to the Outer Edge. Gives total depth within the range. The original indicator's default; Auto uses it in Classic mode.")
  input.section(title: "Differential", description: "Counts only liquidity between Inner and Outer Edge, excluding everything closer to mid. Shows walls stacked 1-2.5 % away without the noise near the mid. Auto uses it in Adaptive mode, where it gave the clearest signals.")
  input.group(title: "Threshold System")
  input.section(title: "3-tier structure", description: "Bull and bear sides each have 3 levels (Lvl 1/2/3): in standard deviations in Adaptive mode (2, 2.5, 3), in ratio in Classic mode (0.30/0.46/0.68 and 0.25/0.50/0.68). As the reading crosses each level, the band moves to that tier color.")
  input.section(title: "Activation threshold", description: "Classic mode: minimum absolute ratio required before any color is shown. Default 0.0 (any imbalance triggers).")
  input.group(title: "Exchange Notes")
  input.section(title: "Default selection", description: "Binance + Coinbase are picked by default. Binance has the deepest spot book for most pairs. Coinbase is the primary USD venue and captures US institutional flow. Bybit, OKX and Kraken add coverage.")

  input.tab(title: "Tips & FAQ")
  input.group(title: "Setup & Interpretation")
  input.section(title: "Starting config", description: "Defaults: Binance + Coinbase, Adaptive, Baseline 60, Depth Mode Auto (Differential), Inner 1% / Outer 2.5%, full book. Green below price = bids building unusually fast. Red above price = asks building unusually fast.")
  input.section(title: "Highest-value signals", description: "Lvl 2-3 at a structurally important level (prior swing, VWAP, round number) means the order book is actively stacking at that price. Watch whether price respects it.")
  input.section(title: "Confluence", description: "Combines well with volume profile and liquidation maps. A Lvl 3 bid band at a high-volume node with no nearby liquidation risk is a high-confidence support signal.")
  input.group(title: "Signal Quality")
  input.section(title: "Tier intensity", description: "Lvl 1: a clear change in the book. Lvl 2: strong stacking, likely intentional. Lvl 3: an extreme one-sided change. Higher tiers carry more weight.")
  input.section(title: "Persistence", description: "A band held across several bars means orders are maintained or refreshed. A single-bar flash that fades at once suggests the orders were pulled: potential spoof. Persistence (bars) 2 shows a band only once its side held for two closed bars in a row.")
  input.section(title: "Divergence", description: "Price making new highs while ask pressure (red band) builds above = book warning of resistance ahead. Price making new lows while bid support (green band) strengthens below = bullish divergence.")
  input.section(title: "Why no band most of the time", description: "In Adaptive mode the band is the exception: about one bar in ten on BTC 1m. No band means the book is no more one-sided than it was over the last Baseline bars.")
  input.group(title: "Tuning")
  input.section(title: "Too noisy", description: "Adaptive: raise the Lvl 1 thresholds to 2.5σ, or lengthen the Baseline. Classic: raise Activation to 0.10-0.15 or the Lvl 1 thresholds.")
  input.section(title: "Too quiet", description: "Adaptive: lower Lvl 1 to 1.5σ or shorten the Baseline to 30. Classic: lower Lvl 1 (e.g. 0.15), or use Adaptive: on the full book the Classic ratio rarely reaches 0.25.")
  input.section(title: "Classic always red", description: "Spot books are usually ask-heavy 1-2.5 % from the mid, so Classic with Differential depth shows red on top on about four bars in ten. Use Adaptive, or Classic with Cumulative depth.")
  input.section(title: "Timeframe", description: "The Baseline counts bars: 60 is an hour on 1m and five hours on 5m. Keep it at about an hour to a few hours of bars. The measurements above are from 1m bars.")
  input.group(title: "Spot vs Futures")
  input.section(title: "Why spot only", description: "Spot orders require full capital commitment with no leverage. Spot book data is a cleaner directional signal with less noise from speculative positioning. The indicator excludes futures on purpose.")
  input.section(title: "Capital comparison", description: "A $10M futures bid at 10x leverage represents $1M in actual capital. A $5M spot bid represents $5M. Spot walls across multiple exchanges often carry more directional conviction.")
  input.group(title: "Troubleshooting")
  input.section(title: "Band not showing", description: "Check in order: (1) Show Band enabled, (2) the pair trades on the picked exchanges, (3) Adaptive: the Baseline needs that many bars of book history before the first band, (4) Classic: Activation and the Lvl 1 thresholds not above the live ratio. The title shows the live ratio.")
)

data (
  chart = subscribe(data.ohlcv)
  books = subscribe(data.book, exchange: spotExchanges)
)

plot (
  bullInner = plot.line(title: "Bull inner edge", color: #00000000, width: 1, showLabel: false, showValue: false)
  bullOuter = plot.line(title: "Bull outer edge", color: #00000000, width: 1, showLabel: false, showValue: false)
  bearInner = plot.line(title: "Bear inner edge", color: #00000000, width: 1, showLabel: false, showValue: false)
  bearOuter = plot.line(title: "Bear outer edge", color: #00000000, width: 1, showLabel: false, showValue: false)
)

fill (
  bullBand = fill.between(bullInner, bullOuter)
  bearBand = fill.between(bearInner, bearOuter)
)

// The tier colour of an absolute reading, null below Lvl 1.
fn tierColor(r: float, t1: float, t2: float, t3: float, c1: color, c2: color, c3: color) -> color? {
  if r >= t3 {
    return c3
  }
  if r >= t2 {
    return c2
  }
  if r >= t1 {
    return c1
  }
  return null
}

on chart.update {
  let adaptive = mode == "Adaptive"
  let differential = depthMode == "Differential" || depthMode == "Auto" && adaptive
  let inner = differential ? innerEdge : 0.0
  let ratio = books.imbalance(outerEdge, innerPct: inner, levels: bookLevels)
  let previous = books.imbalance(outerEdge, innerPct: inner, barsAgo: 1, levels: bookLevels)
  // Adaptive: this bar's ratio against the bars of the last
  // `baselineMinutes`, and the previous bar's likewise (the window of the
  // previous ratios). In time, so seconds charts read like the minute one.
  let seconds = time.durationSeconds(time.bars(1))
  let bars = baselineMinutes * 60 / (seconds > 0 ? seconds : 60)
  let baseline = bars < 10 ? 10 : bars > 2000 ? 2000 : bars
  let z = ta.zscore(ratio, baseline)
  let zBefore = ta.zscore(previous, baseline)
  let mid = books.mid()
  let shown = ratio != null && math.isFinite(ratio)
  let reading = shown ? str.format("{0:.2}", ratio) : "n/a"
  let score = adaptive && z != null ? str.format(" | z {0:.1}", z) : ""
  script.setTitle(str.format("Spot Bands | {0} ex | {1}{2}", spotExchanges.len, reading, score))

  let r = shown ? ratio ?? 0.0 : 0.0
  let level = adaptive ? z ?? 0.0 : r
  let before = adaptive ? zBefore : previous
  let b = before ?? 0.0
  // Flip suppression: no band on the bar the reading changes sign.
  let flipped = before != null && (level > 0.0 && b < 0.0 || level < 0.0 && b > 0.0)
  // Tier thresholds: standard deviations (Adaptive) or ratios (Classic).
  let up1 = adaptive ? bullZ1 : bull1
  let up2 = adaptive ? bullZ2 : bull2
  let up3 = adaptive ? bullZ3 : bull3
  let down1 = adaptive ? bearZ1 : bear1
  let down2 = adaptive ? bearZ2 : bear2
  let down3 = adaptive ? bearZ3 : bear3
  let bull = level > 0.0 ? tierColor(level, up1, up2, up3, bull1Color, bull2Color, bull3Color) : null
  let bear = level < 0.0 ? tierColor(-level, down1, down2, down3, bear1Color, bear2Color, bear3Color) : null
  let ready = adaptive ? z != null : math.abs(r) >= activation
  // Persistence: the side reached Lvl 1 on each of the last `persistBars` bars.
  let bullHeld = ta.sum(ready && shown && bull != null ? 1.0 : 0.0, persistBars) ?? 0.0
  let bearHeld = ta.sum(ready && shown && bear != null ? 1.0 : 0.0, persistBars) ?? 0.0
  let active = showBand && shown && mid != null && !flipped && ready
  let m = mid ?? 0.0
  // Where the band is drawn: hugging the candle, or at the book edges.
  let near = drawAt == "Near price"
  let atr = chart.atr(14) ?? 0.0
  // Near price: half an ATR off the candle, as tall as 3 ATR or 0.15 % of
  // the price, whichever is more, so it reads at any zoom.
  let byAtr = 3.0 * atr
  let byPrice = chart.close * 0.0015
  let tall = byAtr > byPrice ? byAtr : byPrice
  let lowEdge = near ? chart.low - 0.5 * atr : m * (1.0 - innerEdge / 100.0)
  let lowFar = near ? lowEdge - tall : m * (1.0 - outerEdge / 100.0)
  let highEdge = near ? chart.high + 0.5 * atr : m * (1.0 + innerEdge / 100.0)
  let highFar = near ? highEdge + tall : m * (1.0 + outerEdge / 100.0)

  if active && bull != null && bullHeld >= persistBars {
    bullInner.plot(lowEdge)
    bullOuter.plot(lowFar)
    bullBand.fill(color: color.withAlpha(bull ?? #00000000, 240))
  } else {
    bullInner.plot(null)
    bullOuter.plot(null)
  }
  if active && bear != null && bearHeld >= persistBars {
    bearInner.plot(highEdge)
    bearOuter.plot(highFar)
    bearBand.fill(color: color.withAlpha(bear ?? #00000000, 240))
  } else {
    bearInner.plot(null)
    bearOuter.plot(null)
  }
}

How it works

One book from several exchanges

subscribe(data.book, exchange: spotExchanges) takes the list the input.exchanges picker returns. Several exchanges’ books are summed: each is shifted onto the first exchange’s mid (Coinbase quotes BTC in USD, Binance in USDT, and the two sit up to about $100 apart), then their levels are added up in price buckets of the widest exchange’s bin, so the queries (mid, imbalance) read one combined book. Without symbol:, each exchange’s market of the chart’s coin is read: BTCUSDT on Binance, BTC-USD on Coinbase.

The ratio

books.imbalance(outerEdge, innerPct: inner, levels: bookLevels) is

Text
ratio = (bids - asks) / (bids + asks)

by notional within the outer edge of the mid, from -1 (all asks) to +1 (all bids). Cumulative depth counts everything from the mid out (inner is 0); Differential depth passes the inner edge, so only the liquidity between the edges counts. Depth Mode Auto picks Differential in Adaptive mode and Cumulative in Classic mode.

Book depth limits

levels: limits the count to the nearest price levels of each side, best bid and best ask first. 0, the default, reads the full book. On BTC a level of the summed book is $1, so

Book depthReachesWhat the ratio describes
0 (full book)the Outer Edge (2.5 %)the whole band
4,096about ±5 %the whole band: the same as the full book
250about ±0.3 %only the top of the book; the 1-2.5 % edges are never reached, and the ratio jumps with each order near the mid

On one day of BTC the 250-level ratio spiked past the bearish threshold on three bars and the full-book ratio never reached a threshold.

Classic and Adaptive

Classic compares the ratio with fixed thresholds (Bull Lvl 1 0.30, Bear Lvl 1 0.25). Spot books lean to one side for hours, though: on the day we measured, asks outweighed bids within 2.5 % of the mid on almost every bar (the full-book ratio sat between -0.2 and +0.1), and between 1 % and 2.5 % the ratio was below -0.25 on four bars in ten. Fixed thresholds either never fire or paint red above the price all day.

Adaptive removes that standing lean. It scores each bar’s ratio against the last Baseline bars,

Text
z = (ratio - average of the last N ratios) / their standard deviation

with ta.zscore(ratio, baseline) over the bars of the last baselineMinutes (60: an hour on every interval), and draws a band when bids (z ≥ 2) or asks (z ≤ -2) gained unusually fast. Lvl 2 and 3 are 2.5σ and 3σ.

ModeBand
Adaptivez ≥ Bull Lvl 1 (σ): below the price; z ≤ -Bear Lvl 1 (σ): above it
Classicratio ≥ Bull Lvl 1: below the price; ratio ≤ -Bear Lvl 1: above it; never below Activation

For the original’s behaviour pick Classic, leave Depth Mode on Auto (Cumulative) and set Book depth to 250 (Free) or 4096 (Pro).

What we measured

One day of production data (Oct 15-16, 2026, ~17.5 hours, 1,046 one-minute bars of BTC; Binance + Coinbase spot books from the hub’s minute heatmap tiles, prices from Binance BTCUSDT perpetual). A signal is the first bar of a band; “hit” means the price moved the band’s way (up after green, down after red) by the stated time. Over all bars the price was higher 15 minutes later 53 % of the time.

RuleBars with a bandBull signals, hit 15 / 30 / 60 minBear signals, hit 15 / 30 / 60 min
Classic, full book, Cumulative0 %nonenone
Classic, 250 levels0.3 %none3: 33 / 33 / 67 %
Classic, full book, Differential42 %655: 49 / 52 / 59 %
Adaptive, Cumulative, 60 bars, 2σ7.7 %27: 70 / 67 / 56 %19: 47 / 37 / 42 %
Adaptive, Differential, 60 bars, 2σ (default)9.0 %22: 73 / 77 / 73 %14: 64 / 71 / 71 %
Adaptive, Differential, 120 bars, 2σ5.7 %12: 58 / 100 / 75 %5: 80 / 40 / 60 %
Adaptive, 250 levels, 60 bars, 2σ5.6 %22: 59 / 59 / 64 %22: 36 / 36 / 41 %

Adaptive with Differential depth was the only rule whose bands on both sides pointed the right way more often than not, and it stayed so with a 30-bar baseline and with thresholds from 1.5σ to 2.5σ. Longer baselines kept the bullish bands but made the bearish ones unreliable, so the default is 60 bars on 1m, an hour; the baseline is set in minutes, so 10s charts compare with the same hour (360 bars). Requiring two bars in a row, hysteresis, or a filter on where the price stood in its recent range did not clearly improve it, so they are left out. One day is a small sample (a few dozen signals, overlapping moves, one market regime): read the table as evidence that Adaptive does not paint noise, not as a hit rate to trade on.

The band

ReadingBandColour
bullishmid × (1 − inner %) down to mid × (1 − outer %)Bull Lvl 1, 2 or 3
bearishmid × (1 + inner %) up to mid × (1 + outer %)Bear Lvl 1, 2 or 3

Below Lvl 1 of its side there is no band. The edges follow the mid of each bar’s book, so the band moves with the price. On the bar where the reading changes sign (the ratio in Classic, z in Adaptive, compared with the previous bar’s) the band is left out: it does not flicker. The previous bar’s z is ta.zscore of books.imbalance(..., barsAgo: 1), the same window one bar earlier. The band is a fill.between of two invisible edge lines, coloured per bar with fill(color:).

Legend

script.setTitle writes Spot Bands | N ex | ratio into the legend: how many exchanges are summed and the newest ratio, and in Adaptive mode | z with its score.

Settings with tabs

input.tab splits the settings into Settings, Thresholds, About and Tips & FAQ. Every input carries its explanation as description:; the About and Tips tabs are input.group headings with input.section(title:, description:) rows, which the settings show as text.

Adapting it

  • Futures books. Pick perpetual exchanges instead (binancef, bybitf, okxf) by changing the picker’s filter: and default.
  • A ratio pane. Plot the ratio as a histogram on a pane of its own to see the imbalance over time.
  • Alerts. Trigger an alert when the reading crosses Lvl 3 on either side.
  • A z-score pane. Plot z as a histogram with lines at ±2 to see how close the book came to a band.