
The January 2026 Pine Script update introduced two new namespaces for working with volume footprint data: footprint.* and volume_row.*1. These functions provide access to aggregated volume metrics at the bar level and granular volume data at the individual price row level. Together they form the complete API for building custom footprint indicators and strategies in Pine Script.
This article is a function-by-function reference for both namespaces. For an overview of footprint concepts and how to set up a footprint request, see our companion guide on Pine Script footprint requests. This reference assumes you are familiar with the request.footprint() function and the footprint and volume_row data types.
Before using any footprint or volume_row function, you need a footprint object. The only way to create one is by calling request.footprint().
request.footprint(ticks_per_row, va_percent, imbalance_percent) → footprint2
ticks_per_row (simple int) - Required. The price range of each footprint row, expressed in ticks. A tick equals the symbol's minimum price increment (syminfo.mintick). Smaller values create more rows with finer granularity; larger values create fewer, broader rows2.va_percent (simple int/float) - Optional. The percentage of total volume used to calculate the Value Area. Default is 70, which is the industry standard2. The Value Area is the price range containing this percentage of the bar's total volume.imbalance_percent (simple int/float) - Optional. The percentage threshold for detecting volume imbalances between adjacent rows. Default is 300, meaning a row's buy volume must exceed 300% of the sell volume of the row below it to qualify as a buy imbalance2.Returns a footprint object ID, or na if no footprint data is available for the current bar2. Scripts can use this ID with the footprint.*() functions described below.
request.footprint() call2request.*() call limit2//@version=6
indicator("Footprint Reference", overlay = true)
int ticksInput = input.int(100, "Ticks per row", minval = 1)
int vaInput = input.int(70, "Value Area %", minval = 1)
footprint fp = request.footprint(ticksInput, vaInput)
All subsequent examples in this reference use the fp variable from this setup.
The footprint namespace contains eight functions that operate on a footprint object2. These functions retrieve bar-level aggregated data and references to specific rows within the footprint.
footprint.buy_volume(id) → series float
Calculates the total buy volume for the entire volume footprint represented by the footprint object2. Buy volume is the volume traded at the ask price or above, representing aggressive buying activity.
id (footprint) - The footprint object ID from request.footprint()na if the footprint ID is nafloat buyVol = footprint.buy_volume(fp)
// Alternative method syntax:
float buyVol2 = fp.buy_volume()
footprint.sell_volume(id) → series float
Calculates the total sell volume for the entire volume footprint2. Sell volume is the volume traded at the bid price or below, representing aggressive selling activity.
id (footprint) - The footprint object IDfloat sellVol = footprint.sell_volume(fp)
// Alternative method syntax:
float sellVol2 = fp.sell_volume()
footprint.delta(id) → series float
Calculates the overall volume delta for the footprint2. The delta represents the difference between the total buy volume and total sell volume. A positive value indicates that buying activity exceeded selling activity for the bar. A negative value indicates the opposite.
id (footprint) - The footprint object IDbuy_volume - sell_volumefloat barDelta = footprint.delta(fp)
// Equivalent to: footprint.buy_volume(fp) - footprint.sell_volume(fp)
color deltaColor = barDelta >= 0 ? color.green : color.red
footprint.poc(id) → volume_row
Finds the Point of Control (POC) row for the volume footprint, then returns the ID of a volume_row object containing the data for that row2. The POC is the footprint row with the highest total volume (buy volume plus sell volume). It represents the price level where the most trading activity occurred during the bar.
id (footprint) - The footprint object IDvolume_row object ID for the POC row, or na if the footprint is navolume_row pocRow = footprint.poc(fp)
// Use volume_row functions to extract data from the POC
float pocUpperPrice = pocRow.up_price()
float pocLowerPrice = pocRow.down_price()
float pocVolume = pocRow.total_volume()
The POC is one of the most important levels in volume profile analysis. Many traders treat the POC as a dynamic support/resistance level because it marks the price where the highest concentration of trading occurred.
footprint.vah(id) → volume_row
Finds the Value Area High (VAH) row for the volume footprint and returns a volume_row ID for that row2. The VAH row is the highest row within the footprint's Value Area. The Value Area is the contiguous range of price rows centered on the POC that together account for the percentage of total volume specified by the va_percent parameter in the request.footprint() call (default 70%).
id (footprint) - The footprint object IDvolume_row object ID for the VAH rowvolume_row vahRow = footprint.vah(fp)
float vahUpperPrice = vahRow.up_price()
footprint.val(id) → volume_row
Finds the Value Area Low (VAL) row for the volume footprint and returns a volume_row ID for that row2. The VAL row is the lowest row within the footprint's Value Area.
id (footprint) - The footprint object IDvolume_row object ID for the VAL rowvolume_row valRow = footprint.val(fp)
float valLowerPrice = valRow.down_price()
Together, footprint.vah() and footprint.val() define the boundaries of the Value Area. The price range between the VAL's lower price and the VAH's upper price represents where the majority of volume (typically 70%) traded during the bar. Many volume profile strategies use these levels for mean-reversion entries: price outside the Value Area is expected to return inside, while price within the Value Area is expected to gravitate toward the POC.
footprint.rows(id) → array<volume_row>
Creates an array containing all volume_row IDs from the footprint object2. Each volume_row in the array contains data for one row in the footprint. The first element represents the lowest row (lowest price level), and the last element represents the highest row (highest price level).
id (footprint) - The footprint object IDarray<volume_row> containing all rows, ordered from lowest to highest pricearray<volume_row> allRows = footprint.rows(fp)
// Iterate through all rows
for row in allRows
float rowDelta = row.delta()
float rowVolume = row.total_volume()
// Process each row...
This function is essential for custom analysis that goes beyond the standard POC and Value Area metrics. Use it to scan all rows for specific patterns, calculate custom volume distributions, or build visual footprint displays.
footprint.get_row_by_price(id, price) → volume_row
Searches the volume footprint to find the row whose price range includes the specified price level2. If the price falls within one of the footprint's rows, the function returns the volume_row ID for that row. If the price is outside all rows, it returns na.
id (footprint) - The footprint object ID; price (series int/float) - The price level to search forvolume_row object ID if the price falls within a row, or na otherwise// Find the footprint row at the current close price
volume_row rowAtClose = footprint.get_row_by_price(fp, close)
if not na(rowAtClose)
float volumeAtClose = rowAtClose.total_volume()
float deltaAtClose = rowAtClose.delta()
This function is useful for analyzing what happened at a specific price level without iterating through the entire footprint. Common use cases include checking the volume delta at the bar's close, examining activity at a known support or resistance level, or comparing volume at the high versus the low of the bar.
The volume_row namespace contains nine functions that operate on individual volume_row objects2. These functions extract specific data from a single row within the footprint, including price boundaries, categorized volume, delta, and imbalance status.
volume_row.up_price(id) → series float
Retrieves the upper price level of the volume footprint row. Each row covers a fixed price range determined by the ticks_per_row parameter. The upper price is the top boundary of that range.
id (volume_row) - The volume_row object IDvolume_row pocRow = footprint.poc(fp)
float pocHighPrice = pocRow.up_price()
volume_row.down_price(id) → series float
Retrieves the lower price level of the volume footprint row. This is the bottom boundary of the row's price range.
id (volume_row) - The volume_row object IDvolume_row pocRow = footprint.poc(fp)
float pocLowPrice = pocRow.down_price()
// The midpoint of a row
float pocMid = (pocRow.up_price() + pocRow.down_price()) / 2.0
volume_row.buy_volume(id) → series float
Calculates the total buy volume for the specific footprint row. This is the volume that traded at the ask or above within this row's price range.
id (volume_row) - The volume_row object IDvolume_row pocRow = footprint.poc(fp)
float pocBuys = pocRow.buy_volume()
volume_row.sell_volume(id) → series float
Calculates the total sell volume for the specific footprint row. This is the volume that traded at the bid or below within this row's price range.
id (volume_row) - The volume_row object IDvolume_row pocRow = footprint.poc(fp)
float pocSells = pocRow.sell_volume()
volume_row.total_volume(id) → series float
Calculates the sum of buy volume and sell volume for the footprint row. This is the total amount of volume that traded within this row's price range, regardless of direction.
id (volume_row) - The volume_row object IDbuy_volume + sell_volume for the rowvolume_row pocRow = footprint.poc(fp)
float pocTotal = pocRow.total_volume()
// Equivalent to: pocRow.buy_volume() + pocRow.sell_volume()
volume_row.delta(id) → series float
Calculates the volume delta for the specific footprint row. The value represents the difference between the row's buy volume and sell volume. A positive value indicates that buyers were more aggressive at this price level. A negative value indicates that sellers were more aggressive.
id (volume_row) - The volume_row object IDbuy_volume - sell_volume for the rowvolume_row pocRow = footprint.poc(fp)
float pocDelta = pocRow.delta()
// Positive = buyers dominated at POC, Negative = sellers dominated
Row-level delta is one of the most informative metrics in footprint analysis. While the overall bar delta tells you whether the bar was net buying or selling, row-level delta reveals where that buying or selling was concentrated. A bar with a positive overall delta but negative delta at the POC suggests that buying activity occurred at the extremes rather than at the most active price level.
volume_row.has_buy_imbalance(id) → series bool
Checks whether the footprint row has a buy imbalance, based on the imbalance_percent argument of the request.footprint() call that produced the footprint object2. A buy imbalance occurs when the row's buy volume exceeds the sell volume of the row directly below it in the footprint by the specified percentage.
id (volume_row) - The volume_row object IDtrue if the row has a buy imbalance, false otherwisevolume_row pocRow = footprint.poc(fp)
bool pocHasBuyImbalance = pocRow.has_buy_imbalance()
With the default imbalance_percent of 300, a buy imbalance means the row's buy volume is at least three times the sell volume of the row below. This indicates exceptionally strong buying pressure at this price level relative to the selling pressure at the adjacent lower level. Clusters of consecutive rows with buy imbalances often signal institutional buying.
volume_row.has_sell_imbalance(id) → series bool
Checks whether the footprint row has a sell imbalance2. A sell imbalance occurs when the row's sell volume exceeds the buy volume of the row directly above it in the footprint by the imbalance_percent threshold.
id (volume_row) - The volume_row object IDtrue if the row has a sell imbalance, false otherwisevolume_row pocRow = footprint.poc(fp)
bool pocHasSellImbalance = pocRow.has_sell_imbalance()
Sell imbalances are the mirror image of buy imbalances. With the default 300% threshold, a sell imbalance means the row's sell volume is at least three times the buy volume of the row above. Clusters of sell imbalances often signal institutional selling or aggressive distribution.
The following script demonstrates how to use multiple footprint and volume_row functions together to build a practical footprint analysis indicator. It retrieves the POC, VAH, and VAL levels, calculates the overall delta, and iterates through all rows to count imbalances.
//@version=6
indicator("Footprint Analysis", overlay = true, max_labels_count = 50)
// Inputs
int ticksInput = input.int(100, "Ticks per row", minval = 1)
int vaInput = input.int(70, "Value Area %", minval = 1)
int imbalanceInput = input.int(300, "Imbalance %", minval = 100)
// Request footprint data
footprint fp = request.footprint(ticksInput, vaInput, imbalanceInput)
// Only process on visible, confirmed bars with available data
if not na(fp) and barstate.isconfirmed
// Bar-level metrics
float buyVolume = fp.buy_volume()
float sellVolume = fp.sell_volume()
float deltaVolume = fp.delta()
// Key rows
volume_row pocRow = fp.poc()
volume_row vahRow = fp.vah()
volume_row valRow = fp.val()
// Price levels from volume_row objects
float pocUpper = pocRow.up_price()
float pocLower = pocRow.down_price()
float vahUpper = vahRow.up_price()
float valLower = valRow.down_price()
// Count imbalances across all rows
array<volume_row> allRows = fp.rows()
int buyImbalances = 0
int sellImbalances = 0
for row in allRows
if row.has_buy_imbalance()
buyImbalances += 1
if row.has_sell_imbalance()
sellImbalances += 1
// Check volume at the close price
volume_row closeRow = fp.get_row_by_price(close)
string closeRowInfo = na(closeRow)
? "N/A"
: str.format("Vol: {0}, Delta: {1}",
closeRow.total_volume(), closeRow.delta())
// Build display string
string info = str.format(
"Buy Vol: {0}\nSell Vol: {1}\nDelta: {2}\n" +
"POC: {3}-{4}\nVA: {5}-{6}\n" +
"Buy Imb: {7} | Sell Imb: {8}\n" +
"Close Row: {9}",
buyVolume, sellVolume, deltaVolume,
pocLower, pocUpper, valLower, vahUpper,
buyImbalances, sellImbalances, closeRowInfo
)
// Display label
color labelColor = deltaVolume >= 0 ? color.green : color.red
label.new(bar_index, high, info,
yloc = yloc.abovebar, color = labelColor,
textcolor = color.white, size = size.small)
This example combines nearly every function from both namespaces. The script retrieves bar-level aggregates using footprint.buy_volume(), footprint.sell_volume(), and footprint.delta(). It gets key price levels using footprint.poc(), footprint.vah(), and footprint.val(). It iterates through all rows using footprint.rows() to count imbalances. And it looks up a specific price level using footprint.get_row_by_price().
The following table summarizes every function in both namespaces with their signatures and return types.
footprint.buy_volume(id) → series float - Total buy volume for the barfootprint.sell_volume(id) → series float - Total sell volume for the barfootprint.delta(id) → series float - Buy volume minus sell volume for the barfootprint.poc(id) → volume_row - Point of Control row (highest total volume)footprint.vah(id) → volume_row - Value Area High boundary rowfootprint.val(id) → volume_row - Value Area Low boundary rowfootprint.rows(id) → array<volume_row> - All rows from lowest to highest pricefootprint.get_row_by_price(id, price) → volume_row - Row containing the specified pricevolume_row.up_price(id) → series float - Upper price boundary of the rowvolume_row.down_price(id) → series float - Lower price boundary of the rowvolume_row.buy_volume(id) → series float - Buy volume for the rowvolume_row.sell_volume(id) → series float - Sell volume for the rowvolume_row.total_volume(id) → series float - Total volume (buy + sell) for the rowvolume_row.delta(id) → series float - Buy minus sell volume for the rowvolume_row.has_buy_imbalance(id) → series bool - True if row has a buy imbalancevolume_row.has_sell_imbalance(id) → series bool - True if row has a sell imbalanceThe footprint API opens up a new category of trading strategies that were previously impossible in Pine Script. Delta divergence strategies can compare the bar's price direction to its volume delta. POC proximity strategies can generate signals when price approaches or departs from high-volume levels. Imbalance scanning strategies can identify bars with unusually strong buying or selling pressure.
Once you have built a footprint-based strategy that generates reliable signals, TradersPost can connect those signals to automated order execution3. Your strategy's alert messages can include footprint-derived information such as the POC price, the delta direction, or the imbalance count, giving the execution platform rich context about each signal. To learn how to connect your Pine Script strategies to live trading, see our guide on automating TradingView strategies.
1 Pine Script Release Notes
2 Pine Script v6 Language Reference
3 TradersPost - Automated Trading Platform