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
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
tradesinput 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_byover a data-only output. To shade between the bands, declare arange()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.symbolpick re-pins a secondary candle input only, and atradespin 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_lensets the volatility lookback. Shorten it for a reactive envelope that hugs the line, lengthen it for a smoother, slower band. - Colors. The palette on
cvdand thecoloron each band are hex strings on the declarations; recolor there. - Alert on the line.
cvdis 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
- In the editor's Explorer, press New indicator and pick Blank indicator: the tab holds the
//@lang=wrun-tsline alone. - Paste the indicator block above under that line and press Run on a crypto perp or spot market.
- At the editor's Console prompt, type
last 20 cvdto read the running total over the last 20 bars.
Concepts used
- Multi-source for which inputs the chart pins to another market
- Data sources for
trades.volumewith asideand themissingpolicies - Execution model for the module-level accumulator
- Volume and VWAP for buy and sell volume as inputs