Skip to content

Free TradingView indicator

Volume Delta (Approximation)

Approximate buy/sell volume delta per bar from where the candle closes in its range, plus a resettable cumulative delta.

Volumev1.0TradingView (Pine Script v6) Non-repainting signals Alerts

What it does

Real volume delta is the difference between volume traded at the ask (aggressive buyers) and volume traded at the bid (aggressive sellers). Measuring it needs tick-by-tick trade data with the aggressor side, which a normal OHLCV chart does not have.

This indicator is an approximation. It estimates buy and sell volume for each bar from one piece of information: where the close sits inside the bar's high-low range. It is useful as a rough picture of whether bars are closing with buyers or sellers in control, but it is not order-flow data and should not be read as such.

How it works

  1. Close location = (close − low) ÷ (high − low), a value from 0 (close at the low) to 1 (close at the high).
  2. Estimated buy volume = volume × close location. Estimated sell volume = volume − buy volume.
  3. Bar delta = buy volume − sell volume. A bar that closes at its high gets a delta equal to its full volume; one that closes in the middle gets zero.
  4. Bars with no range (high = low) are split 50/50 by default. With that option off, the whole bar is assigned by comparing the close with the previous close.
  5. Cumulative delta = running total of bar delta, restarted at each session, week or month if selected.
  6. A signal line (EMA of cumulative delta) is drawn in cumulative mode, and crosses are marked on closed bars.

How to use it

Use it to compare effort and result. If price makes a new high but the approximate cumulative delta for the session does not, closes have been weaker during the push. If price is flat while cumulative delta climbs, bars are repeatedly closing near their highs.

Practical ideas:

  • Session reset on intraday charts gives a fresh read each day.
  • Large single-bar deltas next to support or resistance can show which side closed the bar decisively.
  • Treat signal-line crosses as context for another method, not as entries by themselves.

Settings

InputDefaultWhat it changes
DisplayCumulative deltaLine of the running total or columns per bar.
Reset cumulative deltaSessionRestart point of the running total.
Split zero-range bars 50/50onTreatment of bars with no range.
Signal EMA length21Smoothness of the signal line.
Show signal-line crossesonDots at crosses.

Limitations

  • It is an estimate, not real delta. A bar that rallies hard and then closes mid-range shows zero delta, even if aggressive buying dominated most of it. Results can differ a lot from true bid/ask delta.
  • On spot forex, TradingView volume is generally tick volume from one feed, which adds a second layer of approximation.
  • Results depend on the chart timeframe: the same hour split into M5 bars and into one H1 bar can give different deltas.
  • The signal EMA does not restart with the cumulative total, so it can lag sharply right after a reset.
  • The script stops with an error on symbols with no volume data.

Pine Script source

In TradingView open Pine Editor, paste the code, click Save and then Add to chart.

FXP_VolumeDeltaApprox.pine

64 lines

// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/volume-delta-approx/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
// NOTE: this is an APPROXIMATION. It does not read real bid/ask trades. It splits each bar's total
// volume into "buy" and "sell" parts based on where the close sits inside the bar's high-low range.
//@version=6
indicator("FXP Volume Delta (Approximation)", shorttitle = "FXP Delta Approx", overlay = false, format = format.volume)

// ── Inputs ─────────────────────────────────────────────────────────────
grpD      = "Delta"
dispMode  = input.string("Cumulative delta", "Display", options = ["Cumulative delta", "Bar delta"], group = grpD)
resetSel  = input.string("Session", "Reset cumulative delta", options = ["Never", "Session", "Week", "Month"], group = grpD)
dojiSplit = input.bool(true, "Split zero-range bars 50/50", group = grpD, tooltip = "When high equals low the close position is undefined. On: half buy, half sell. Off: the whole bar is assigned by close versus previous close.")

grpS      = "Signal"
sigLen    = input.int(21, "Signal EMA length (cumulative mode)", minval = 1, group = grpS)
showSig   = input.bool(true, "Show signal-line crosses", group = grpS)

grpVis    = "Display"
buyCol    = input.color(color.teal, "Buying color", group = grpVis)
sellCol   = input.color(color.red, "Selling color", group = grpVis)

// ── Approximate buy / sell split ──────────────────────────────────────
float vol   = nz(volume)
float barRange = high - low
float buyFrac = barRange > 0 ? (close - low) / barRange : dojiSplit ? 0.5 : close > close[1] ? 1.0 : close < close[1] ? 0.0 : 0.5
float buyVol  = vol * buyFrac
float sellVol = vol - buyVol
float delta   = buyVol - sellVol

// ── Cumulative delta with optional reset ───────────────────────────────
bool chgD = timeframe.change("D")
bool chgW = timeframe.change("W")
bool chgM = timeframe.change("M")
bool doReset = resetSel == "Session" ? chgD : resetSel == "Week" ? chgW : resetSel == "Month" ? chgM : false

var float cumDelta = 0.0
cumDelta := doReset ? delta : cumDelta + delta
float cdSignal = ta.ema(cumDelta, sigLen)

bool isCum   = dispMode == "Cumulative delta"
bool rawUp   = ta.crossover(cumDelta, cdSignal)
bool rawDn   = ta.crossunder(cumDelta, cdSignal)
bool crossUp = isCum and rawUp and barstate.isconfirmed and not doReset
bool crossDn = isCum and rawDn and barstate.isconfirmed and not doReset

float cumVol = ta.cum(vol)
if barstate.islast and cumVol == 0
    runtime.error("This symbol provides no volume data, so delta cannot be approximated.")

// ── Plots ──────────────────────────────────────────────────────────────
plot(isCum ? na : delta, "Bar delta (approx.)", style = plot.style_columns, color = delta >= 0 ? buyCol : sellCol)
plot(isCum ? cumDelta : na, "Cumulative delta (approx.)", color = cumDelta >= cdSignal ? buyCol : sellCol, linewidth = 2, style = plot.style_linebr)
plot(isCum and not doReset ? cdSignal : na, "Signal EMA", color = color.new(color.gray, 10), style = plot.style_linebr)
hline(0, "Zero", color = color.new(color.gray, 60), linestyle = hline.style_dotted)
plot(buyVol, "Approx. buy volume", display = display.data_window)
plot(sellVol, "Approx. sell volume", display = display.data_window)

plotshape(showSig and crossUp ? cumDelta : na, "Cum. delta above signal", shape.circle, location.absolute, buyCol, size = size.tiny)
plotshape(showSig and crossDn ? cumDelta : na, "Cum. delta below signal", shape.circle, location.absolute, sellCol, size = size.tiny)

// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(crossUp, "Cumulative delta crossed above signal", "FXP Delta Approx: approximate cumulative delta crossed above its EMA on {{ticker}} {{interval}}")
alertcondition(crossDn, "Cumulative delta crossed below signal", "FXP Delta Approx: approximate cumulative delta crossed below its EMA on {{ticker}} {{interval}}")

Key inputs

Key inputs of Volume Delta (Approximation)
InputDefaultDescription
DisplayCumulative deltaShow the running cumulative delta or the per-bar delta histogram.
Reset cumulative deltaSessionWhen the running total restarts: Never, Session, Week or Month.
Split zero-range bars 50/50trueHow bars with high equal to low are treated.
Signal EMA length (cumulative mode)21EMA of the cumulative delta used for cross signals.

Educational tools, not advice. Indicators and Expert Advisors describe price; they do not predict it, and past behaviour does not guarantee future results. Test everything on a demo account first and never risk money you cannot afford to lose. See our disclaimer.

Keep exploring

Related tradingview indicators