1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
201 lines of Pine Script v6 from Stacked_Buy_Sell_Volume_Columns.pine, published under GPL-3.0. Keep the author's credit and licence lines when you reuse it.
Stacked_Buy_Sell_Volume_Columns.pine
//@version=6
indicator(
title = "Stacked Buy/Sell Volume Columns",
shorttitle = "Stacked Vol Delta",
overlay = false,
max_bars_back = 500
)
//──────────────────────────────────────────────────────────────────────────────
// WHAT THIS IS AND ISN'T
//
// Real order-flow data -- bid/ask depth, trade-by-trade aggressor side,
// footprint charts -- comes from the exchange's tick/quote feed. Pine
// Script has NO access to that at all, on any TradingView plan. This is
// the closest legitimate approximation available in Pine, and the same
// method already used for the CVD module in Regime_Engine_TCO_Gatekeeper
// .pine in this repo: pull the actual lower-timeframe sub-bars inside
// each chart bar via request.security_lower_tf(), classify each one
// up/down by its own close-vs-open, and sum the volume on each side. A
// large passive limit order sitting at the ask still gets counted as
// "buy volume" if price ticks up through it -- this is a directional-
// print proxy, not true bid/ask delta -- but it is a materially better
// approximation than a single-bar close-position heuristic (Chaikin-
// style), since it is built from many smaller directional prints
// instead of one bar's shape.
//
// What's new here vs. that CVD module: this plots each bar's OWN
// buy/sell split as a stacked column (the "stacking" the user asked
// for), not a running cumulative total -- see the "STACKED COLUMNS"
// section below for how that's actually drawn in Pine, which has no
// native stacked-column primitive.
//──────────────────────────────────────────────────────────────────────────────
//──────────────────────────────────────────────────────────────────────────────
// INPUTS — INTRABAR RESOLUTION
//──────────────────────────────────────────────────────────────────────────────
groupTF = "1. Intrabar Resolution"
lowerTFModeInput = input.string(
defval = "Auto",
title = "Lower-Timeframe Mode",
options = ["Auto", "Manual"],
group = groupTF,
tooltip = "Auto picks a sub-timeframe from the chart's own timeframe, targeting roughly 5-15 sub-bars per chart bar -- enough directional prints to be meaningful without hitting the sub-bar-count limits TradingView enforces per request.security_lower_tf() call. Switch to Manual to pick your own."
)
lowerTFManualInput = input.timeframe(
defval = "1",
title = "Manual Lower Timeframe",
group = groupTF,
tooltip = "Only used when Mode is Manual. Must be strictly lower than the chart's own timeframe, or TradingView raises a runtime error."
)
//──────────────────────────────────────────────────────────────────────────────
// INPUTS — VISUALS
//──────────────────────────────────────────────────────────────────────────────
groupVisual = "2. Visuals"
buyColorInput = input.color(
defval = color.lime,
title = "Buy Volume Color (Top Segment)",
group = groupVisual
)
sellColorInput = input.color(
defval = color.red,
title = "Sell Volume Color (Bottom Segment)",
group = groupVisual
)
showNetDeltaLineInput = input.bool(
defval = true,
title = "Show Net Delta Line",
group = groupVisual,
tooltip = "Plots buyVolume - sellVolume for the bar as a line over the stacked columns -- a quick read of which side dominated that bar, without having to compare the two segment heights by eye."
)
netDeltaColorInput = input.color(
defval = color.white,
title = "Net Delta Line Color",
group = groupVisual
)
showDashboardInput = input.bool(
defval = true,
title = "Show Dashboard",
group = groupVisual
)
//──────────────────────────────────────────────────────────────────────────────
// AUTO LOWER-TIMEFRAME SELECTION
//
// Written as if/else-if, not a chained multi-line ternary -- a nested
// ternary spread across several lines like this has previously hit
// Pine's line-continuation parser limit (CE10013) elsewhere in this
// repo (Regime_Engine_TCO_Gatekeeper.pine); if/else-if has no such
// limit and is the established fix.
//──────────────────────────────────────────────────────────────────────────────
f_autoLowerTF() =>
float chartSeconds = timeframe.in_seconds()
string result = "240"
if chartSeconds <= 60
result := "1S"
else if chartSeconds <= 300
result := "1"
else if chartSeconds <= 900
result := "5"
else if chartSeconds <= 3600
result := "15"
else if chartSeconds <= 14400
result := "60"
result
string lowerTF = lowerTFModeInput == "Auto" ? f_autoLowerTF() : lowerTFManualInput
//──────────────────────────────────────────────────────────────────────────────
// INTRABAR BUY/SELL VOLUME SPLIT
//
// Guarded with `if array.size(...) > 0` before the for..in loop -- an
// empty array (e.g. right at the start of history, before the lower-tf
// feed has any bars yet, or a symbol with a data gap) would otherwise
// make Pine's `for i = 0 to array.size(x) - 1` infer a DECREMENTING
// step from `-1 < 0` and run once with i = 0 instead of skipping,
// throwing an array.get() out-of-bounds error -- the same for-loop
// step-direction gotcha already fixed in several other scripts in this
// repo (Dynamic_Markov_Capacity_PDH_PDL_..., STRONG_NQ_ORB_BREAKOUTS,
// Execution_Signals_1m_13m, NQ_Hourly_Retracement_Levels).
//──────────────────────────────────────────────────────────────────────────────
[lowerClose, lowerOpen, lowerVolume] = request.security_lower_tf(
syminfo.tickerid,
lowerTF,
[close, open, volume]
)
float buyVolume = 0.0
float sellVolume = 0.0
if array.size(lowerClose) > 0
for i = 0 to array.size(lowerClose) - 1
float subClose = array.get(lowerClose, i)
float subOpen = array.get(lowerOpen, i)
float subVolume = array.get(lowerVolume, i)
if subClose > subOpen
buyVolume += subVolume
else if subClose < subOpen
sellVolume += subVolume
else
// Sub-bar closed flat -- can't classify direction, so split
// the volume evenly rather than assigning it to either side.
buyVolume += subVolume * 0.5
sellVolume += subVolume * 0.5
float netDelta = buyVolume - sellVolume
float totalVolume = buyVolume + sellVolume
//──────────────────────────────────────────────────────────────────────────────
// STACKED COLUMNS
//
// plot(style = plot.style_columns) always draws from 0 -- Pine has no
// native "stack segment B on top of segment A" primitive. The standard
// workaround, and what actually makes this a STACKED column rather than
// two overlapping ones: plot the FULL total (buy+sell) as one column in
// the buy color first, then plot JUST the sell portion (0 to sellVolume)
// as a second, later column in the sell color at the same x. Later
// plots render on top in Pine, so the shorter sell column overdraws the
// bottom of the taller total column, leaving the buy-colored portion of
// the total column showing through above it. Net visible result: one
// column, bottom segment sell-colored, top segment buy-colored, total
// height = total volume for the bar.
//──────────────────────────────────────────────────────────────────────────────
plot(totalVolume, title = "Buy Volume (Top Segment)", style = plot.style_columns, color = buyColorInput)
plot(sellVolume, title = "Sell Volume (Bottom Segment)", style = plot.style_columns, color = sellColorInput)
plot(showNetDeltaLineInput ? netDelta : na, title = "Net Delta", color = netDeltaColorInput, linewidth = 2)
hline(0, title = "Zero Line", color = color.new(color.gray, 50))
//──────────────────────────────────────────────────────────────────────────────
// DASHBOARD
//──────────────────────────────────────────────────────────────────────────────
var table statsTable = table.new(
position.top_right,
2,
4,
border_width = 1
)
if barstate.islast and showDashboardInput
table.cell(statsTable, 0, 0, "Stacked Vol Delta", text_color = color.white, bgcolor = color.rgb(20, 50, 90))
table.cell(statsTable, 1, 0, lowerTF + " sub-bars", text_color = color.white, bgcolor = color.rgb(20, 50, 90))
table.cell(statsTable, 0, 1, "Buy Volume")
table.cell(statsTable, 1, 1, str.tostring(buyVolume, format.volume), text_color = buyColorInput)
table.cell(statsTable, 0, 2, "Sell Volume")
table.cell(statsTable, 1, 2, str.tostring(sellVolume, format.volume), text_color = sellColorInput)
table.cell(statsTable, 0, 3, "Net Delta")
table.cell(statsTable, 1, 3, str.tostring(netDelta, format.volume), text_color = netDelta >= 0 ? buyColorInput : sellColorInput)
Overview
The split is estimated from lower-timeframe sub-bars, each classed as up or down by its own close versus open, so it approximates order flow rather than showing true bid and ask data. The lower timeframe can be chosen automatically or set manually and must be below the chart timeframe. An optional net delta line, adjustable colours and a dashboard are included.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Lower-Timeframe Mode
string
Auto
lowerTFModeInput
Manual Lower Timeframe
timeframe
1
lowerTFManualInput
Buy Volume Color (Top Segment)
color
color.lime
buyColorInput
Sell Volume Color (Bottom Segment)
color
color.red
sellColorInput
Show Net Delta Line
bool
true
showNetDeltaLineInput
Net Delta Line Color
color
color.white
netDeltaColorInput
Show Dashboard
bool
true
showDashboardInput
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
Calculates the standard Money Flow Index (MFI) oscillator alongside an exponential moving average signal line in a separate pane. The script visually highlights trend conditions through dynamic ribbon filling, optional difference histogram bars, and overbought
This indicator plots a Supertrend-based trend-following setup in a separate pane and skips conditions it treats as a sideways market. A volatility range is built from an ATR value, with RMA, SMA, EMA, or WMA smoothing, or from a fixed point distance. Optional
Range Volume Change compares volume inside a chosen session window with the same window on the previous day and plots that change as a percentage. It accumulates volume during the window and, when enabled, volume outside it, drawing growing columns in a separa
This indicator builds a footprint of estimated buy and sell volume across price rows and plots it in a separate pane. It fills rows from bar low to high, stores volume per row, and draws buy and sell volume boxes, volume-profile boxes, row labels, and cumulati
This indicator measures directional momentum by evaluating candlestick body proportions and volume over a user-defined lookback window. It computes the ratio of candle body to total range, weights the cumulative value by volume and absolute body size, and dete
This indicator builds an aggregate oscillator in a separate pane. It scales open, high, low, close, and volume into a 0 to 1 range, then averages scaled readings from MACD, a MACD histogram, RSI, stochastic, CCI, momentum, OBV, a directional index, volume-weig