
Volume footprint data reveals what aggregate price and volume charts cannot: the exact distribution of buying and selling activity at each price level within a bar. Professional traders have relied on footprint charts for years to identify institutional order flow, spot absorption patterns, and locate the price levels where the heaviest trading occurs. In January 2026, TradingView brought this capability directly into Pine Script with the request.footprint() function.1
This is the biggest feature addition to Pine Script since the v6 launch. It introduces two entirely new data types, a dedicated request function, and over a dozen built-in functions for analyzing volume at the row level.1 This guide covers everything you need to know to start building footprint-based indicators and strategies.
A volume footprint breaks down the total volume of each bar into individual price rows. Instead of seeing a single volume number for an entire candlestick, you see how much volume traded at each specific price level within that bar. Each row covers a fixed price range measured in ticks, and each row tracks buy volume and sell volume separately.
This granular view reveals several important characteristics that a standard volume bar cannot show:
Before this update, Pine Script developers who wanted footprint-style analysis had to estimate it using request.security_lower_tf() to fetch intrabar data and manually aggregate volume into price buckets.4 That approach was slow, limited in precision, and consumed multiple request calls. The new request.footprint() function replaces all of that with a single call that returns pre-calculated, accurate footprint data directly from TradingView's data feeds.3
The function signature is straightforward:
request.footprint(ticks_per_row, va_percent, imbalance_percent) → footprint
It takes three parameters and returns a footprint object (or na if no footprint data is available for the current bar).3
This required parameter sets the price range of each footprint row, expressed in ticks. A tick is the smallest price increment for the symbol, defined by syminfo.mintick.3 If a symbol has a minimum tick size of 0.01 and you set ticks_per_row to 100, each row covers a price range of 1.00.
Smaller values create more rows with finer granularity but require more data processing. Larger values create fewer, broader rows that give a higher-level view of volume distribution. The right value depends on the instrument and the timeframe you are analyzing.
This optional parameter sets the percentage of total volume used to calculate the Value Area. The default is 70, which is the industry standard.3 The Value Area represents the price range where 70% (or your specified percentage) of the bar's total volume traded. The rows at the boundaries of this range are the Value Area High (VAH) and Value Area Low (VAL).
Increasing this value to 80 or 90 widens the Value Area to encompass more volume. Decreasing it narrows the area to focus on the most concentrated zone of trading activity.
This optional parameter sets the percentage difference threshold for detecting volume imbalances between adjacent rows. The default is 300, meaning that the buy volume of a row must be at least three times (300%) the sell volume of the row below it to register as a buy imbalance.3 Similarly, a sell imbalance occurs when a row's sell volume exceeds the buy volume of the row above it by the specified percentage.
Volume imbalances are significant because they often indicate aggressive directional activity. Clusters of buy imbalances suggest strong demand, while clusters of sell imbalances suggest heavy selling pressure.
Only programmers with a TradingView Premium or Ultimate plan can use request.footprint().1 Scripts that include this function call will not compile for users on lower-tier plans. Additionally, scripts can only include one unique request.footprint() call. Attempting to use multiple calls with different arguments will cause a compilation error.3
The request.footprint() function returns an ID of the footprint type. This is a reference type, similar to how line.new() returns a line ID.3 You cannot inspect the footprint object directly. Instead, you pass its ID to the built-in footprint.*() functions to extract data.
There are two categories of footprint functions: those that return aggregate values for the entire footprint and those that return volume_row IDs for specific rows.
These functions return numeric values summarizing the entire footprint:
A positive delta indicates that the bar had more buying activity than selling, while a negative delta indicates the opposite. These aggregate values give you a quick read on directional pressure without examining individual rows.
These functions return the ID of a volume_row object for a specific row of interest:
The footprint.rows(id) function creates an array containing all volume_row IDs from the footprint. The array is sorted in ascending order by price level, with the first element representing the lowest row and the last element representing the highest row.3 You can loop through this array to analyze every row in the footprint.
A volume_row object contains the data for a single row within a footprint. You get volume_row IDs from the footprint functions described above. Once you have a volume_row ID, you can call the built-in volume_row.*() functions to extract detailed information about that row.
The difference between up_price and down_price equals the ticks_per_row value multiplied by syminfo.mintick. These boundaries define the exact price range that the row covers.
These per-row values let you see exactly where buying or selling pressure was concentrated within a bar. A row with high total volume and a strongly positive delta indicates aggressive buying at that specific price level.
The imbalance threshold is set by the imbalance_percent argument in the original request.footprint() call. Changing that argument directly affects which rows register as having imbalances.
All footprint.*() and volume_row.*() functions can be called using either function syntax or method syntax.3 Both approaches produce identical results. Method syntax is often cleaner when chaining multiple calls on the same object.
// Function syntax
float pocPrice = volume_row.up_price(footprint.poc(reqFootprint))
// Method syntax (equivalent)
float pocPrice = reqFootprint.poc().up_price()
Throughout this guide and in the examples below, we use method syntax for readability.
The following example from the Pine Script release notes demonstrates a complete footprint indicator.1 It requests footprint data, retrieves the POC and Value Area levels, and displays a summary label on each bar showing key volume metrics.
//@version=6
indicator("Footprint requests demo", overlay = true, behind_chart = false, max_labels_count = 50)
//@variable The number of ticks to use as the price interval for each footprint row.
int numTicksInput = input.int(100, "Ticks per footprint row", minval = 1)
//@variable The percentage of each footprint's total volume to use for calculating the Value Area (VA).
int vaInput = input.int(70, "Value Area percentage", minval = 1)
//@variable References a `footprint` object for the current bar, or holds `na` if no footprint data is available.
footprint reqFootprint = request.footprint(numTicksInput, vaInput)
// Only process visible bars that have footprint data.
if time >= chart.left_visible_bar_time and time <= chart.right_visible_bar_time and not na(reqFootprint)
// Retrieve aggregate volume values.
float buyVol = reqFootprint.buy_volume()
float sellVol = reqFootprint.sell_volume()
float delta = reqFootprint.delta()
// Retrieve key row IDs.
volume_row poc = reqFootprint.poc()
volume_row vah = reqFootprint.vah()
volume_row val = reqFootprint.val()
// Build a summary string.
string infoText = str.format(
"Buy: {0}\nSell: {1}\nDelta: {2}\nPOC: {3} - {4}\nVAH: {5}\nVAL: {6}",
str.tostring(buyVol, format.volume),
str.tostring(sellVol, format.volume),
str.tostring(delta, format.volume),
str.tostring(poc.down_price(), format.mintick),
str.tostring(poc.up_price(), format.mintick),
str.tostring(vah.up_price(), format.mintick),
str.tostring(val.down_price(), format.mintick)
)
// Display a label with the summary.
color labelColor = delta >= 0 ? color.teal : color.maroon
label.new(bar_index, high, infoText, style = label.style_label_down,
color = labelColor, textcolor = color.white, size = size.small)
This indicator creates a label on each visible bar showing the total buy volume, sell volume, volume delta, the POC price range, the VAH level, and the VAL level. Labels are colored teal for bars with positive delta (net buying) and maroon for bars with negative delta (net selling).
The following example demonstrates how to iterate through all rows in a footprint to display detailed per-row information. It draws boxes at each row's price range with buy/sell volume, delta, and imbalance data.
//@version=6
indicator("Footprint row data demo", max_boxes_count = 500)
//@variable The size of each footprint row, expressed in ticks.
int numTicksInput = input.int(100, "Ticks per footprint row", 1)
//@variable The percentage difference for detecting volume imbalances.
int imbalanceInput = input.int(300, "Imbalance percentage", 1)
//@variable References a footprint object for the current bar.
footprint reqFootprint = request.footprint(numTicksInput, imbalance_percent = imbalanceInput)
if time >= chart.left_visible_bar_time and time <= chart.right_visible_bar_time and not na(reqFootprint)
// Get all rows and key reference rows.
array<volume_row> volumeRowsArray = reqFootprint.rows()
volume_row poc = reqFootprint.poc()
volume_row vah = reqFootprint.vah()
volume_row val = reqFootprint.val()
// Loop through every row in the footprint.
for row in volumeRowsArray
float upPrice = row.up_price()
float dnPrice = row.down_price()
float buyVol = row.buy_volume()
float sellVol = row.sell_volume()
float delta = row.delta()
bool buyImbalance = row.has_buy_imbalance()
bool sellImbalance = row.has_sell_imbalance()
// Format the row data as text.
string boxText = str.format(
"B: {0} | S: {1} | D: {2} | I: {3}",
str.tostring(buyVol, format.volume),
str.tostring(sellVol, format.volume),
str.tostring(delta, format.volume),
buyImbalance and sellImbalance ? "Both" : buyImbalance ? "Buy" : sellImbalance ? "Sell" : "None"
)
// Color based on delta relative to POC volume.
color deltaColor = delta >= 0 ? color.green : color.red
color boxColor = color.from_gradient(
row.total_volume() / poc.total_volume(), 0, 1,
color.new(deltaColor, 100), color.new(deltaColor, 70)
)
// Draw the box.
box rowBox = box.new(bar_index, upPrice, bar_index + 1, dnPrice, #787b8650, 1,
text = boxText, text_color = #787b86, text_halign = text.align_left, bgcolor = boxColor)
// Highlight POC and VA boundary rows.
if upPrice == vah.up_price() or upPrice == val.up_price()
rowBox.set_text_color(color.purple)
rowBox.set_text_formatting(text.format_bold)
if upPrice == poc.up_price()
rowBox.set_text_color(chart.fg_color)
rowBox.set_text_formatting(text.format_bold)
This example draws a box for every row in the footprint. Each box shows the buy volume, sell volume, volume delta, and imbalance status. The boxes use gradient colors based on delta direction and volume intensity relative to the POC row. The POC row text is displayed in the chart's foreground color, and Value Area boundary rows appear in purple.
Footprint data opens up several trading strategies that were not practical in Pine Script before this update.
The Point of Control represents the price where the most volume traded within a bar. When price returns to a previous bar's POC level, it often acts as support or resistance because that price level attracted significant trading interest. You can track POC levels across bars to identify high-volume zones where price is likely to react.
The Value Area High and Value Area Low define the range where 70% of volume occurred. When price opens inside the previous bar's Value Area, many traders expect it to continue trading within that range. When price opens outside the Value Area, it suggests a potential directional move. This is the basis of the "Value Area rule" used in futures and equity index trading.
Comparing the overall volume delta with price movement reveals divergences. If price makes a new high but the volume delta is negative (more selling than buying), it suggests the up move lacks conviction. Conversely, a new low with positive delta suggests buyers are stepping in. You can build indicators that flag these divergences automatically using footprint.delta().
Multiple consecutive rows with buy imbalances (where buy volume overwhelms the sell volume of adjacent rows by the imbalance threshold) indicate aggressive buying across a range of prices. These "stacked imbalances" often mark institutional entry zones. You can scan for them by looping through footprint rows and checking volume_row.has_buy_imbalance() on consecutive rows.
There are several constraints to keep in mind when working with footprint requests:
Footprint analysis gives you deep insight into where volume is concentrated and how buying and selling pressure is distributed across price levels. Once you have built a footprint-based strategy that identifies high-probability setups using POC levels, Value Area boundaries, or volume delta signals, the next step is to automate execution.
TradersPost connects your TradingView strategies to supported brokers.5 Set up webhook alerts in TradingView to fire when your footprint indicator detects a trading signal, and TradersPost will route orders to your brokerage account automatically. This eliminates the delay between signal and execution, ensuring you act on volume profile setups the moment they appear.
Visit TradersPost to connect your TradingView strategies to live and paper trading accounts across stocks, options, futures, and crypto.
1 Pine Script Release Notes — TradingView
2 Pine Script v5 to v6 Migration Guide — TradingView
3 Pine Script v6 Language Reference — TradingView
4 Pine Script User Manual — TradingView
5 TradersPost — Automated Trading Platform