Skip to main content

CVD Indicator (Cumulative Volume Delta)

This example demonstrates a CVD (Cumulative Volume Delta) indicator that has been verified to match Bookmap's closed-source CVD indicator exactly.

What is CVD?โ€‹

Cumulative Volume Delta tracks the running difference between buying and selling volume:

CVD = รŽยฃ (Buy Volume - Sell Volume)

Where:

  • Buy Volume: Trades where the buyer was the aggressor (lifted the ask)
  • Sell Volume: Trades where the seller was the aggressor (hit the bid)

CVD reveals the underlying buying/selling pressure that price alone doesn't show.

The Core Formulaโ€‹

if (tradeInfo.isBidAggressor) {
cvdValue += size; // Aggressive buy รขโ€ โ€™ positive delta
} else {
cvdValue -= size; // Aggressive sell รขโ€ โ€™ negative delta
}
isBidAggressorMarket ActionCVD Impact
trueBuyer lifted the ask+ size
falseSeller hit the bid- size

Complete Implementationโ€‹

package com.bookmap.addons.orderflow;

import velox.api.layer1.annotations.*;
import velox.api.layer1.data.InstrumentInfo;
import velox.api.layer1.data.TradeInfo;
import velox.api.layer1.messages.indicators.Layer1ApiUserMessageModifyIndicator.GraphType;
import velox.api.layer1.simplified.*;
import velox.gui.StrategyPanel;

import javax.swing.*;
import java.awt.*;

@Layer1SimpleAttachable
@Layer1StrategyName("CVD Indicator")
@Layer1ApiVersion(Layer1ApiVersionValue.VERSION2)
public class CvdIndicator implements CustomModule, TradeDataListener, CustomSettingsPanelProvider {

private IndicatorModifiable cvdIndicator;
private double cvdValue = 0;
private Color indicatorColor = Color.GREEN;
private int lineWidth = 2;

@Override
public void initialize(String alias, InstrumentInfo info, Api api, InitialState initialState) {
cvdIndicator = api.registerIndicatorModifiable("CVD", GraphType.BOTTOM);
cvdIndicator.setColor(indicatorColor);
cvdIndicator.setWidth(lineWidth);
}

@Override
public void onTrade(double price, int size, TradeInfo tradeInfo) {
if (tradeInfo.isBidAggressor) {
cvdValue += size; // Buying volume (bid aggressor)
} else {
cvdValue -= size; // Selling volume (ask aggressor)
}
cvdIndicator.addPoint(cvdValue);
}

@Override
public StrategyPanel[] getCustomSettingsPanels() {
StrategyPanel panel = new StrategyPanel("CVD Settings");

JButton colorButton = new JButton("Change Color");
colorButton.addActionListener(e -> {
Color newColor = JColorChooser.showDialog(null, "Choose CVD Line Color", indicatorColor);
if (newColor != null) {
indicatorColor = newColor;
cvdIndicator.setColor(indicatorColor);
}
});
panel.add(colorButton);

JSpinner widthSpinner = new JSpinner(new SpinnerNumberModel(lineWidth, 1, 5, 1));
widthSpinner.addChangeListener(e -> {
lineWidth = (int) widthSpinner.getValue();
cvdIndicator.setWidth(lineWidth);
});
panel.add(new JLabel("Line Width:"));
panel.add(widthSpinner);

return new StrategyPanel[] { panel };
}

@Override
public void stop() {
// Cleanup if necessary
}
}

Key Implementation Detailsโ€‹

1. Indicator Registrationโ€‹

cvdIndicator = api.registerIndicatorModifiable("CVD", GraphType.BOTTOM);
  • IndicatorModifiable: Allows dynamic color/width changes after registration
  • GraphType.BOTTOM: Places indicator in separate panel below price chart

2. No Price Conversion Neededโ€‹

Unlike price-based indicators, CVD only uses:

  • size - Trade volume (already in correct units)
  • tradeInfo.isBidAggressor - Aggressor side classification

No pips conversion required.

3. Zero-Size Tradesโ€‹

Zero-size trades (MBO updates) naturally contribute nothing:

cvdValue += 0;  // No impact

No explicit filtering needed, though you may add it for efficiency:

if (size == 0) return;

4. Settings Panelโ€‹

CustomSettingsPanelProvider enables runtime customization:

  • Color picker via JColorChooser
  • Line width via JSpinner

Verificationโ€‹

This implementation has been verified against Bookmap's native CVD indicator:

IndicatorValue
Custom (green)-38.0
Bookmap native-38

Result: Exact match confirmed.

Use Casesโ€‹

Trading Applicationsโ€‹

PatternInterpretation
Price up, CVD upConfirmed bullish (buying pressure)
Price up, CVD downDivergence - potential reversal
Price down, CVD downConfirmed bearish (selling pressure)
Price down, CVD upDivergence - potential reversal

Extensionsโ€‹

This foundation can be extended for:

  1. Session CVD - Reset at session boundaries
  2. Delta Bars - Per-bar delta instead of cumulative
  3. Delta Divergence Alerts - Automatic divergence detection
  4. Multi-timeframe Delta - Compare delta across timeframes

Session-Based CVD Variantโ€‹

To reset CVD at session open:

private boolean isNewSession = false;

@Override
public void onTrade(double price, int size, TradeInfo tradeInfo) {
if (isNewSession) {
cvdValue = 0;
isNewSession = false;
}

if (tradeInfo.isBidAggressor) {
cvdValue += size;
} else {
cvdValue -= size;
}
cvdIndicator.addPoint(cvdValue);
}

Combine with HistoricalModeListener or time-based checks to detect session boundaries.

Data Source Compatibilityโ€‹

Data SourceCVD Accuracy
Rithmic (live)รขล“โ€ฆ Exact
Rithmic (recorded)รขล“โ€ฆ Exact
dxFeed Historicalรขล“โ€ฆ Accurate (sizes and aggressor flags preserved)

Unlike volume profile, CVD is not affected by price aggregation since it only depends on size and aggressor side, both of which are preserved in aggregated data.

See Alsoโ€‹