---
title: "Aggregated CVD"
description: "One cumulative volume-delta line with volatility bands and sign coloring, on the chart's own market."
order: 81
section: "cookbook"
---

<!-- source: docs/indicators/cookbook/aggregated-cvd.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Aggregated CVD

One cumulative volume-delta line with volatility bands and sign coloring, on the chart's own market.

Cumulative volume delta tracks the running difference between aggressive buying and aggressive selling. On a single venue it tells a partial story, because flow splits across exchanges. The chart serves `trades` on the chart's own market only: it pins another market on a secondary `ohlcv` input alone, and a `trades` input pinned to another venue is refused by name. So the indicator is the single-venue line, framed with volatility bands, on whichever market the chart shows; to read another venue's flow, open the chart on that venue.

## The wrun indicator

```typescript sample=cookbook-aggregated-cvd
param("band_len", 60, { min: 10, max: 500, description: "Bars in the volatility band" });
// The chart's own candles are the grid every other input lines up on.
input("close", ohlcv.close);
input("buy", trades.volume, { side: "BUY", missing: "zero", description: "Aggressive buy volume" });
input("sell", trades.volume, { side: "SELL", missing: "zero", description: "Aggressive sell volume" });
output("cvd", line, lower, {
  width: 2,
  color_by: "sign",
  colors: ["#ff5b7f", "#22d3a5"],
  description: "Cumulative volume delta on the chart's market",
});
output("upper_band", line, lower, { color: "#22d3a5", opacity: 0.35, description: "CVD plus one standard deviation" });
output("lower_band", line, lower, { color: "#ff5b7f", opacity: 0.35, description: "CVD minus one standard deviation" });
output("sign", none, lower);

let stdev = new Stdev(60);
let cvd: f64 = 0.0;

function onStart(): void {
  stdev = new Stdev(i32(p_band_len()));
}

function onBar(): void {
  // A module-level variable is the accumulator: it survives from bar to bar.
  cvd += in_buy() - in_sell();
  const band = stdev.update(cvd);
  const width = isNaN(band) ? 0.0 : band;
  out_cvd(cvd);
  out_upper_band(cvd + width);
  out_lower_band(cvd - width);
  out_sign(cvd >= 0.0 ? 1.0 : 0.0);
}
```

## How it works

**The timeline spine.** `input("close", ohlcv.close)` is never read by the code. It is the first input, and the first input defines the grid every other input lines up on: one row per candle, always. Without it, the timeline would depend on the trade feed resolving, and on a market where it is missing the module would have no bars to compute. Load the chart series first, always.

**Buy and sell volume.** `trades.volume` with `side: "BUY"` and `side: "SELL"` are the two halves of the tape, as two inputs. `missing: "zero"` makes a bar with no trade observation contribute zero rather than carrying the last value forward, which would double-count.

**Cumulative means module-level.** `cvd` is a module-level variable and `cvd += buy - sell` adds each bar's net flow to the running total. That persistence is what turns a per-bar delta into a cumulative line.

**Sign coloring and the band.** `sign` is a data-only `1` or `0`, and the `cvd` line indexes its two-entry palette with it: green when the running total is positive, red when negative. `Stdev` over the running total gives the one-sigma envelope; while it is warming the band width is `0`, so the edges sit on the line instead of vanishing.

## Design notes

- One venue, the chart's own. A `trades` input pinned to another venue is refused by name ("the browser lane serves market pins on secondary ohlcv inputs only"), so a multi-venue sum has no chart form; open the chart on another venue to read that venue's flow.
- The sign tint is `color_by` over a data-only output. To shade between the bands, declare a `range()` between the two band outputs, which the chart draws as a filled band, or a one-bar box between them (the [Anchored VWAP](anchored-vwap.md) recipe shades its band that way).
- No symbol picker drives this Indicator: a `param.symbol` pick re-pins a secondary candle input only, and a `trades` pin to another venue is refused, so the market is always the chart's own.

## Customize it

- **Another market.** Switch the chart: the indicator reads whichever market the chart shows. On a market with no sided trades the two inputs read zero under `missing: "zero"`, so the line stays flat.
- **Band width.** `band_len` sets the volatility lookback. Shorten it for a reactive envelope that hugs the line, lengthen it for a smoother, slower band.
- **Colors.** The palette on `cvd` and the `color` on each band are hex strings on the declarations; recolor there.
- **Alert on the line.** `cvd` is a drawn line, so once the indicator is published and on a chart, the alert dialog offers it with conditions such as **Crosses above** and **Moving up %** ([Alerts](../functions/alerts.md)).

## Run it

1. In the editor's Explorer, press **New indicator** and pick **Blank indicator**: the tab holds the `//@lang=wrun-ts` line alone.
2. Paste the indicator block above under that line and press **Run** on a crypto perp or spot market.
3. At the editor's Console prompt, type `last 20 cvd` to read the running total over the last 20 bars.

## Concepts used

- [Multi-source](../core-concepts/multi-source.md) for which inputs the chart pins to another market
- [Data sources](../core-concepts/data-sources.md) for `trades.volume` with a `side` and the `missing` policies
- [Execution model](../core-concepts/execution-model.md) for the module-level accumulator
- [Volume and VWAP](../functions/volume-indicators.md) for buy and sell volume as inputs
