# Plots and panes

> Lay out a price trend on the chart and CVD, open-interest change and volume delta in three stacked sub-panes from one script.

This script builds a compact order-flow layout. On the price pane you see a fast and a slow EMA with the space between them shaded. Below the chart, three panes show cumulative volume delta that restarts every day, the bar-by-bar change in open interest, and the volume delta of each bar.

It is a good template whenever you want several related series in one indicator: one script, one set of inputs, and every pane removed together when you remove the script.

```flowscope title="plots-and-panes.fs"
script "Flow layout"

input (
  fastLen = input.int(21, title: "Fast EMA", min: 2, max: 200)
  slowLen = input.int(55, title: "Slow EMA", min: 3, max: 400)
  dailyReset = input.bool(true, title: "Restart CVD every day")
)

data (
  chart = subscribe(data.ohlcv)
  oi = subscribe(data.oi)
  vd = subscribe(data.vd)
)

pane (
  cvdPane = pane(title: "CVD", height: 0.2)
  oiPane = pane(title: "OI change", height: 0.15)
  vdPane = pane(title: "Volume delta", height: 0.15)
)

plot (
  fast = plot.line(title: "Fast EMA", color: color.blue, width: 2)
  slow = plot.line(title: "Slow EMA", color: color.gray, width: 2)
  cvdLine = plot.line(title: "CVD", color: color.cyan, width: 2, on: cvdPane)
  oiBars = plot.histogram(title: "OI change", on: oiPane)
  vdBars = plot.histogram(title: "Delta", on: vdPane)
)

fill trendFill = fill.between(fast, slow, color: color.withAlpha(color.blue, 40))

state previousTime: time? = null

// Price pane: the trend lines follow the forming candle.
on chart.update {
  let f = ta.ema(chart.close, fastLen)
  let s = ta.ema(chart.close, slowLen)
  let up = (f ?? 0.0) >= (s ?? 0.0)
  fast.plot(f, color: up ? color.green : color.red)
  slow.plot(s)
}

// CVD pane: sum each bar's delta, restarting on a new day.
on vd.close {
  let t = vd.time ?? time.fromUnixSeconds(0)
  let newDay = dailyReset && time.isNewPeriod(t, previousTime, 1D)
  cvdLine.plot(ta.cum(vd.close, reset: newDay))
  previousTime = t
}

// OI pane: change against the previous confirmed period.
on oi.close {
  let change = ta.change(oi.close)
  let shade = (change ?? 0.0) >= 0.0 ? color.green : color.red
  oiBars.plot(change, color: color.withAlpha(shade, 200))
}

// Delta pane: the close of the volume-delta bar is the bar's delta.
on vd.update {
  let delta = vd.close
  vdBars.plot(delta, color: (delta ?? 0.0) >= 0.0 ? color.green : color.red)
}
```

## How it works

### Declare every surface up front

Panes and plots are declarations, not statements. The grouped `pane ( ... )` block creates three panes, and each plot names its target with `on:`. A plot without `on:` lands on the price pane.

`height` is a share of the chart's height. Three panes at 0.2, 0.15 and 0.15 leave half of the space for price. The order of the declarations is the order of the panes from top to bottom; in the chart you can drag the dividers, and the script remembers the sizes.

### One handler per source

Each subscription has its own handlers. `ta.change(oi.close)` in `on oi.close` compares the confirmed open interest with the one before it; if either side is missing the result is `null`, and the histogram shows a gap rather than a misleading zero.

Keeping sources in separate handlers means you never have to think about which subscription's bar is confirmed at a given moment. If you need to combine two sources in one calculation, read [data and update events](/docs/scripting/guides/data-and-events) first.

### Accumulate with a reset

`ta.cum(value, reset:)` keeps a running sum and starts again from the current value when `reset` is true. `time.isNewPeriod` tells when a bar opens a new day; the previous bar's time lives in `state` because the handler needs it on the next call.

### Colour per point

Every `plot(...)` call accepts an optional `color:`. The fast EMA switches colour by its position against the slow EMA, and the histograms colour each bar by sign. The fill between the two EMAs is declared once with `fill.between` and needs no handler code.

## Adapting it

- **Liquidations instead of OI.** Subscribe to `data.stat` and plot `stats.sellLiq` as a positive and `stats.buyLiq` as a negative histogram; see [data and update events](/docs/scripting/guides/data-and-events#statistics-liquidations-and-funding).
- **Weekly CVD.** Replace `1D` with `1W` in `time.isNewPeriod`.
- **Hide a series without removing it.** Add an `input.bool` and plot `show ? value : null`. The plot stays declared, so the layout does not change.

## Related

- [Inputs and visuals](/docs/scripting/guides/inputs-and-visuals)
- [Custom axes and charts](/docs/scripting/guides/custom-axes-and-charts)
- [`plot` reference](/docs/scripting/reference/catalog/plot), [`data` reference](/docs/scripting/reference/catalog/data)
