Aggregated CVD

View as MarkdownOpen the editor

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

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);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

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 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).

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