# Size-band delta

> Split each bar's delta into retail, mid-size and whale flow from the trade stream, and flag stretches where whales and retail trade against each other.

Not every buyer is the same. A thousand small market buys and one block of the same size say different things about who is moving the market. This script classifies every print by its notional and keeps a separate delta for small, mid-size and large traders.

You pick where retail ends and where whale size begins. One pane shows the whale delta as columns with the retail delta as a line on top. A second pane shows cumulative delta per group, restarting every day. When whales have been net buyers over the last few bars while retail sold (or the reverse), a marker appears on the price chart.

```flowscope title="size-band-delta.fs"
script "Size-band delta"

input (
  retailUsd = input.float(10_000.0, title: "Retail below (USD)", min: 100.0)
  whaleUsd = input.float(250_000.0, title: "Whales from (USD)", min: 1_000.0)
  lookback = input.int(20, title: "Comparison window (bars)", min: 3, max: 200)
  minWhaleUsd = input.float(2_000_000.0, title: "Min whale delta in window (USD)", min: 0.0)
)

data (
  chart = subscribe(data.ohlcv)
  tape = subscribe(data.trades)
)

pane (
  barPane = pane(title: "Delta by size (USD)", height: 0.2)
  cumPane = pane(title: "Cumulative delta by size, daily", height: 0.2)
)

plot (
  whaleBars = plot.histogram(title: "Whale delta", on: barPane)
  retailLine = plot.line(title: "Retail delta", color: color.yellow, width: 2, on: barPane)
  whaleCum = plot.line(title: "Whales", color: color.cyan, width: 2, on: cumPane)
  midCum = plot.line(title: "Mid-size", color: color.gray, on: cumPane)
  retailCum = plot.line(title: "Retail", color: color.yellow, on: cumPane)
  whalesBuy = plot.marker(title: "Whales buy, retail sells", color: color.green, shape: shape.up)
  whalesSell = plot.marker(title: "Whales sell, retail buys", color: color.red, shape: shape.down)
)

state (
  retail = 0.0
  mid = 0.0
  whale = 0.0
  retailDay = 0.0
  midDay = 0.0
  whaleDay = 0.0
  whaleWindow = rolling<float>(200)
  retailWindow = rolling<float>(200)
  lastTime: time? = null
)

// Signed notional of each print, sorted into its size group.
on tape.trade(tr) {
  let usd = tr.price * tr.size
  let signed = tr.isBuy ? usd : 0.0 - usd
  if usd < retailUsd {
    retail += signed
  } else if usd < whaleUsd {
    mid += signed
  } else {
    whale += signed
  }
}

on chart.update {
  whaleBars.plot(whale, color: whale >= 0.0 ? color.green : color.red)
  retailLine.plot(retail)
  whaleCum.plot(whaleDay + whale)
  midCum.plot(midDay + mid)
  retailCum.plot(retailDay + retail)
}

on chart.close {
  // Restart the daily lines on the first bar of a new day.
  let t = chart.time ?? time.fromUnixSeconds(0)
  let newDay = time.isNewPeriod(t, lastTime, 1D)
  lastTime = t
  whaleDay = newDay ? whale : whaleDay + whale
  midDay = newDay ? mid : midDay + mid
  retailDay = newDay ? retail : retailDay + retail

  // Who was on which side over the last `lookback` bars.
  whaleWindow.push(whale)
  retailWindow.push(retail)
  let whaleSum = whaleWindow.sumLast(lookback)
  let retailSum = retailWindow.sumLast(lookback)
  let buyStretch = whaleSum >= minWhaleUsd && retailSum < 0.0
  let sellStretch = whaleSum <= 0.0 - minWhaleUsd && retailSum > 0.0
  whalesBuy.plot(buyStretch ? chart.low : null)
  whalesSell.plot(sellStretch ? chart.high : null)

  retail = 0.0
  mid = 0.0
  whale = 0.0
}
```

## How it works

### Classify each print

A print's notional is `tr.price * tr.size`; its sign comes from the aggressor, `tr.isBuy`. An `if` / `else if` chain sorts it into one of three groups. The thresholds are in USD, so the same settings mean the same trader size on every coin.

### Bars and days

The trade handler only adds. `on chart.update` shows the running bar, `on chart.close` banks it: the daily sums restart when `time.isNewPeriod` sees a new day, and the per-bar sums go back to zero. The cumulative lines show the banked day plus the forming bar, so the live bar moves them too.

### Who is on the other side

Two `rolling<float>` windows keep each bar's whale and retail delta. `sumLast(lookback)` adds the newest `lookback` values. When whales bought at least `minWhaleUsd` over that window while retail was net short, the green marker appears under the bar; the red one marks the reverse. The windows hold 200 values, the input's maximum.

### History

On a chart, trades reach back as far as the chart keeps them in memory; earlier bars read zero in every group. For a long history of total delta, use `data.vd` instead, as in [plots and panes](/docs/scripting/examples/plots-and-panes).

## Variations

- **Count instead of size.** Add counters per group to separate one block from a burst of medium prints.
- **Three-way markers.** Mark bars where mid-size traders lead both other groups.
- **Feed a strategy.** Use the whale window sum as a filter in the [CVD breakout strategy](/docs/scripting/examples/cvd-breakout-strategy).

## Related

- [Large prints](/docs/scripting/examples/large-prints) marks individual large trades on the chart.
- [Data and update events](/docs/scripting/guides/data-and-events#trades)
- [`collections` reference](/docs/scripting/reference/catalog/collections), [`time` reference](/docs/scripting/reference/catalog/time)
