TradingView Just Gave Pine Script Traders X-Ray Vision Into Order Flow
In early March 2026, TradingView quietly shipped one of the most significant Pine Script updates in years: request.footprint(). This single function gives you programmatic access to volume footprint data — buy volume, sell volume, delta, Point of Control, Value Area, and per-row imbalances — directly inside your custom indicators.
Before this update, analyzing order flow on TradingView meant either eyeballing the built-in footprint chart overlay or dropping down to lower timeframes and doing math yourself. Now you can build fully custom footprint indicators that fire alerts, paint levels, and integrate with your existing strategy logic.
I build Pine Script indicators for my own USDJPY momentum strategy, so I tested request.footprint() the day it dropped. Here is everything you need to know — from the basic API to three progressively more useful indicators you can copy-paste and run today.
request.footprint() requires a TradingView Premium or Ultimate plan. Free and Essential users cannot access footprint data in Pine Script.
---
What request.footprint() Actually Does
The function requests volume footprint information for the current bar and returns a footprint object. That object contains:
| Data | How to Access | What It Tells You |
|---|---|---|
| Total buy volume | fp.buy_volume() | Aggressive buying (trades at ask or above) |
| Total sell volume | fp.sell_volume() | Aggressive selling (trades at bid or below) |
| Volume delta | fp.delta() | Buy − Sell difference (net aggression) |
| Point of Control (POC) | fp.poc() | Price row with highest total volume |
| Value Area High (VAH) | fp.vah() | Upper boundary of the 70% volume zone |
| Value Area Low (VAL) | fp.val() | Lower boundary of the 70% volume zone |
| All rows | fp.rows() | Array of every price row for granular analysis |
| Row at specific price | fp.get_row_by_price(price) | Volume data at any price level you choose |
volume_row object with its own functions: up_price(), down_price(), buy_volume(), sell_volume(), delta(), total_volume(), buy_imbalance(), sell_imbalance(), and total_imbalance().
---
Function Signature and Parameters
footprint fp = request.footprint(ticks_per_row, va_percent, imbalance_percent)
Three parameters control how the footprint is constructed:
- ticks_per_row (required, simple int) — The price range of each row in ticks. One tick equals
syminfo.mintick. Smaller values = more rows, finer granularity. For forex pairs like USDJPY (mintick = 0.001), I use 100 ticks (= 0.1 price units). For BTC (mintick = 0.01), try 1000 ticks (= $10 per row). - va_percent (optional, default 70) — The percentage of total volume that defines the Value Area. The industry standard is 70%. Bump to 80% if you want a wider "fair value" zone.
- imbalance_percent (optional, default 300) — The threshold for flagging buy/sell imbalances between adjacent rows. At 300%, a row's buy volume must exceed 3× the sell volume of the row below it to qualify as a buy imbalance.
- Only one
request.footprint()call per script - It counts toward the
request.*()call limit - Returns
naon bars with no footprint data (always check before accessing)
Indicator #1: POC and Value Area Levels
This is the "hello world" of footprint indicators. It plots the Point of Control and Value Area boundaries as horizontal levels on your chart — essentially recreating the key lines from a volume profile, but now you can layer them with your own logic.
//@version=6
indicator("Footprint POC & Value Area", 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)
// Extract key levels when footprint data exists
float pocHigh = not na(fp) ? fp.poc().up_price() : na
float pocLow = not na(fp) ? fp.poc().down_price() : na
float vahPrice = not na(fp) ? fp.vah().up_price() : na
float valPrice = not na(fp) ? fp.val().down_price() : na
// Plot POC as a thick line — this is where most volume traded
plot(pocHigh, "POC High", color.new(color.orange, 0), 3, plot.style_stepline)
plot(pocLow, "POC Low", color.new(color.orange, 0), 3, plot.style_stepline)
// Value Area boundaries — the 70% volume zone
plot(vahPrice, "VAH", color.new(color.blue, 30), 2, plot.style_stepline, linestyle = line.style_dotted)
plot(valPrice, "VAL", color.new(color.blue, 30), 2, plot.style_stepline, linestyle = line.style_dotted)
// Fill between POC lines to highlight the control zone
p1 = plot(pocHigh, display = display.none)
p2 = plot(pocLow, display = display.none)
fill(p1, p2, color.new(color.orange, 85), title = "POC Zone")
How to use it
The POC is where the market found agreement — the price that attracted the most volume. When price moves away from the POC and then returns, it often acts as a magnet (mean-reversion). The Value Area (VAH to VAL) defines "fair value" for the bar. Price breaking out of the Value Area signals potential directional moves.
For my USDJPY setup, I watch whether price opens above or below the previous session's POC. An open above with positive delta confirms bullish momentum; below with negative delta confirms bearish.
---
Indicator #2: Volume Delta Histogram
Delta (buy volume minus sell volume) is arguably the most useful single metric from footprint data. This indicator plots delta as a histogram below the chart, colored green when buyers dominate and red when sellers dominate.
//@version=6
indicator("Footprint Delta Histogram", overlay = false)
int ticksInput = input.int(100, "Ticks per row", minval = 1)
float warnThreshold = input.float(1.5, "Delta warning multiplier", minval = 1.0)
footprint fp = request.footprint(ticksInput)
float delta = not na(fp) ? fp.delta() : na
float buyVol = not na(fp) ? fp.buy_volume() : na
float sellVol = not na(fp) ? fp.sell_volume() : na
// Color based on delta direction
color deltaColor = delta >= 0 ? color.new(color.green, 20) : color.new(color.red, 20)
// Highlight extreme delta bars (>1.5x average)
float avgDelta = ta.sma(math.abs(delta), 20)
bool isExtreme = math.abs(delta) > avgDelta * warnThreshold
color barColor = isExtreme ? (delta >= 0 ? color.lime : color.fuchsia) : deltaColor
plot(delta, "Volume Delta", barColor, 2, plot.style_histogram)
plot(0, "Zero Line", color.gray, 1)
// Show buy/sell breakdown in data window
plot(buyVol, "Buy Volume", display = display.data_window)
plot(sellVol, "Sell Volume", display = display.data_window)
// Fire alert on extreme delta
alertcondition(isExtreme and delta > 0, "Extreme Buying", "Extreme buy delta detected")
alertcondition(isExtreme and delta < 0, "Extreme Selling", "Extreme sell delta detected")
Why delta matters
A bar can close green (price up) while delta is negative — meaning sellers were actually more aggressive, and the price rise happened on weak buying. This divergence between price and delta is one of the strongest reversal signals in order flow analysis.
The extreme-delta alert (warnThreshold at 1.5× the 20-bar average) catches moments of unusual aggression. In trending markets, extreme delta in the trend direction confirms continuation. Against the trend, it warns of exhaustion.
---
Indicator #3: Imbalance Scanner
This is where request.footprint() gets genuinely powerful. Volume imbalances — where buyers dramatically outnumber sellers (or vice versa) at specific price rows — mark where institutional participants stepped in aggressively. These levels often become future support/resistance.
//@version=6
indicator("Footprint Imbalance Scanner", overlay = true, max_labels_count = 200)
int ticksInput = input.int(100, "Ticks per row", minval = 1)
float imbInput = input.float(300, "Imbalance threshold %", minval = 100)
int minImbalances = input.int(3, "Min stacked imbalances", minval = 1)
footprint fp = request.footprint(ticksInput, 70, imbInput)
if not na(fp)
array<volume_row> rows = fp.rows()
int buyImbCount = 0
int sellImbCount = 0
float firstBuyImbPrice = na
float firstSellImbPrice = na
for row in rows
if row.buy_imbalance()
buyImbCount += 1
if na(firstBuyImbPrice)
firstBuyImbPrice := row.down_price()
if row.sell_imbalance()
sellImbCount += 1
if na(firstSellImbPrice)
firstSellImbPrice := row.up_price()
// Flag bars with stacked buy imbalances (strong demand zone)
if buyImbCount >= minImbalances
label.new(bar_index, low,
text = str.tostring(buyImbCount) + " buy imb",
color = color.new(color.green, 20),
textcolor = color.white,
style = label.style_label_up,
size = size.small)
// Flag bars with stacked sell imbalances (strong supply zone)
if sellImbCount >= minImbalances
label.new(bar_index, high,
text = str.tostring(sellImbCount) + " sell imb",
color = color.new(color.red, 20),
textcolor = color.white,
style = label.style_label_down,
size = size.small)
Stacked imbalances explained
A single imbalance row means buyers overwhelmed sellers at one price level. Multiple consecutive ("stacked") imbalances mean an entire price zone saw aggressive one-sided activity — typically institutional absorption or a large market order sweeping through the book.
The minImbalances input (default 3) filters out noise. With 3+ stacked imbalances, you are seeing a genuine supply or demand zone. These levels frequently hold when price retests them.
---
Choosing the Right Ticks Per Row
The ticks_per_row parameter is the single most important setting, and getting it wrong will either give you too few rows (losing granularity) or too many (noise overload).
Here is my starting-point cheat sheet:
| Asset | syminfo.mintick | Suggested ticks_per_row | Price per row |
|---|---|---|---|
| USDJPY | 0.001 | 100 | ¥0.10 |
| EURUSD | 0.00001 | 100 | 0.00100 |
| BTCUSD | 0.01 | 1000–5000 | $10–$50 |
| ETHUSD | 0.01 | 500–1000 | $5–$10 |
| SPY | 0.01 | 50–100 | $0.50–$1.00 |
| AAPL | 0.01 | 25–50 | $0.25–$0.50 |
Use array.size(fp.rows()) to check how many rows each bar produces, then adjust accordingly.
---
Like what you're reading? Try it yourself — this link supports ChartedTrader at no cost to you.
Try TradingView Premium for footprint access →Combining Footprint Data with Your Existing Strategy
The real power of request.footprint() is not building standalone footprint indicators — it is adding order flow confirmation to strategies you already run.
Here is a pattern I use: my USDJPY momentum strategy generates signals based on a 60-day rate-of-change. Before this update, I would just take the signal. Now I can add a delta filter:
// Pseudocode — integrate with your strategy
bool momentumLong = ta.roc(close, 60) > 0
bool footprintConfirms = not na(fp) and fp.delta() > 0
bool confirmedEntry = momentumLong and footprintConfirms
This simple addition filters out momentum signals where sellers are actually dominating order flow — the kind of signal that looks good on a chart but fails in live execution because the momentum is "empty" (driven by thin ask-side liquidity, not genuine buying pressure).
Other combinations worth exploring:
- Breakout confirmation: Price breaks above resistance + POC moves to the breakout zone (volume accepting the new price)
- Reversal warning: Strong price trend + delta divergence (price up, delta down for 3+ bars)
- Support/resistance validation: Price approaches a level + check
fp.get_row_by_price(level)volume — high volume at the level means it is been tested; low volume means it is untested and more likely to break
Practical Tips from Real Usage
After a day of testing request.footprint() on USDJPY and BTC, here are the things the documentation does not tell you:
if not na(fp)
Footprint data is not available for every bar, especially on higher timeframes or less liquid instruments. If you skip the na check, your script will throw errors or produce misleading plots with na gaps.
On USDJPY (one of the most liquid forex pairs), the footprint data is rich and meaningful. On a low-cap altcoin with $50K daily volume, the delta will be noisy and imbalances will fire on trivial volume. Stick to liquid instruments.
3. Only onerequest.footprint() call per script
You cannot request footprint data at multiple tick granularities in the same script. If you want both a fine-grained (50 ticks) and coarse (500 ticks) view, you will need two separate indicators.
4. It counts toward request limitsIf your script already uses request.security() calls near the limit, adding request.footprint() might push you over. Plan your request budget accordingly.
On a daily chart, using 100 ticks for BTCUSD creates thousands of rows per bar. That is technically correct but practically useless — most rows will have negligible volume. Scale ticks_per_row with your timeframe.
---
What About the Built-In Footprint Chart?
TradingView already has a visual footprint chart (Chart Settings → Footprint). So why bother writing Pine Script indicators?
Three reasons:
1. Alerts. The built-in footprint chart cannot fire alerts. A Pine Script indicator can alert you when delta exceeds a threshold, when stacked imbalances appear, or when POC shifts above/below a key level.
2. Integration. You cannot combine built-in footprint visuals with your existing strategy logic. Pine Script lets you use footprint data as a filter, confirmation, or entry trigger within any strategy.
3. Custom calculations. The built-in chart shows raw data. Pine Script lets you compute derived metrics — delta divergence over N bars, cumulative delta, custom imbalance ratios, value area migration between sessions.
---
Frequently Asked Questions
Do I need a Premium plan for request.footprint()?
Yes. request.footprint() requires a TradingView Premium or Ultimate plan. Essential and free plans cannot use this function.
Can I use request.footprint() in a strategy (not just an indicator)?
The function is available in both indicator() and strategy() scripts. You can use footprint data to filter strategy entries and exits.
Does request.footprint() work on all assets?
It works on any asset that has volume data on TradingView. Forex, crypto, stocks, futures — all supported. However, the quality of the footprint data directly correlates with the asset's liquidity. Thin markets produce noisy footprints.
What Pine Script version do I need?
request.footprint() requires Pine Script v6. If your existing scripts are on v5, you will need to migrate. We wrote a Pine Script v5 to v6 migration checklist that covers the key breaking changes.
Can I request footprint data for a different timeframe?
No. request.footprint() retrieves data for the current chart's timeframe only. Unlike request.security(), you cannot pass a timeframe parameter. If you need footprint data on the 1H chart, you need to be viewing the 1H chart.
---
What to Build Next
Now that you have the building blocks, here are ideas worth pursuing:
- Cumulative Delta indicator: Sum
fp.delta()across bars to track whether net buying or selling pressure is building over a session - POC migration tracker: Plot how the POC price moves bar-to-bar — a rising POC confirms an uptrend, a falling POC confirms a downtrend
- Session Value Area comparison: Compare today's Value Area with yesterday's to identify whether value is migrating higher or lower
- Imbalance heatmap: Color-code bars based on the ratio of buy imbalances to sell imbalances across all rows
For a deeper dive into Pine Script indicator building, check out our RSI divergence indicator tutorial and the TradingView Strategy Tester backtest guide.
---
*request.footprint() shipped in early March 2026 as part of Pine Script v6. This tutorial is current as of the initial release. TradingView may add additional footprint functions in future updates — check the official release notes for the latest.*
---
*This article contains affiliate links. If you sign up through our links, we may earn a commission at no extra cost to you. This helps support our independent research and content.*