Pine Script request.footprint(): Building a Volume Profile and Delta Filter

Since January 2026, Pine Script can read a bar's volume footprint directly with request.footprint(). That gives each bar its own buy volume, sell volume, delta, Point of Control and Value Area. By default the Value Area covers 70% of the bar's volume and an imbalance needs a 300% gap between adjacent rows (>TradingView Pine Script docs). This guide shows what the function returns and how to turn it into a simple delta filter.

Pine ScriptBy , Senior AnalystPublished Updated 6 min read

What request.footprint() actually returns

TradingView added the function in its January 2026 release, along with two new types, footprint and volume_row (>Pine Script release notes). It builds on Pine Script v6, which TradingView launched on December 10, 2024 (>TradingView blog).

The documented signature is:

request.footprint(ticks_per_row, va_percent, imbalance_percent) → series footprint

Here is what each parameter does, per the official docs:

Parameter Type What it controls Default
ticks_per_row simple int, required Row height, as a multiple of syminfo.mintick. 100 means each row spans 100 ticks. none
va_percent simple float, optional Share of the bar's volume used for the Value Area 70
imbalance_percent simple float, optional How much bigger buy (or sell) volume must be than the opposing volume in the adjacent row to flag an imbalance 300

The call returns the ID of a footprint object for the current bar, or na when no footprint data exists for that bar.

Three limits matter before you write a line of code:

The functions you get

TradingView's docs and release notes show these built-ins. You can call them as functions (footprint.delta(fp)) or as methods (fp.delta()).

On a footprint Returns
total_volume(), buy_volume(), sell_volume() Float volume totals for the bar
delta() Buy volume minus sell volume for the bar
poc(), vah(), val() The volume_row for the Point of Control, Value Area High and Value Area Low
rows() An array of every volume_row, sorted from lowest price to highest
get_row_by_price() The row at a given price
On a volume_row Returns
up_price(), down_price() The row's upper and lower price bounds
total_volume(), buy_volume(), sell_volume(), delta() Volume figures for that row
has_buy_imbalance(), has_sell_imbalance() Booleans that depend on your imbalance_percent

The docs define the POC as "the row with the highest total volume." The Value Area is the band of rows holding your va_percent share of volume.

Building the volume profile and delta filter

The script below is our own example. It uses only functions and patterns shown in TradingView's documentation. It plots each bar's Value Area and POC, then shades bars where delta is strong in one direction and price closed on the same side of the POC.

//@version=6
indicator("Footprint delta filter", overlay = true)

int   ticksInput = input.int(100, "Ticks per footprint row", minval = 1)
int   vaInput    = input.int(70, "Value Area percentage", minval = 1)
float minShare   = input.float(0.2, "Min delta as share of volume", minval = 0, maxval = 1)

// One footprint request per script. Returns na when no data is available.
footprint fp = request.footprint(ticksInput, vaInput)
bool hasFp = not na(fp)

// footprint.*() calls do not accept na, so guard every call.
float totalVol = hasFp ? fp.total_volume() : na
float deltaVol = hasFp ? fp.delta() : na
float vaHigh   = hasFp ? fp.vah().up_price() : na
float vaLow    = hasFp ? fp.val().down_price() : na
float pocMid   = hasFp ? (fp.poc().up_price() + fp.poc().down_price()) / 2 : na

float deltaShare = hasFp and totalVol > 0 ? deltaVol / totalVol : na

bool buyersInControl  = deltaShare >= minShare and close > pocMid
bool sellersInControl = deltaShare <= -minShare and close < pocMid

plot(vaHigh, "VA high", color.purple, 1, plot.style_stepline)
plot(vaLow,  "VA low",  color.purple, 1, plot.style_stepline)
plot(pocMid, "POC mid", color.orange, 2, plot.style_stepline)
bgcolor(buyersInControl ? color.new(color.green, 85) : sellersInControl ? color.new(color.red, 85) : na)

alertcondition(buyersInControl, "Buyers in control", "Footprint delta filter: buyers in control")
alertcondition(sellersInControl, "Sellers in control", "Footprint delta filter: sellers in control")

How it works, line by line:

  1. One request. request.footprint(ticksInput, vaInput) fetches the bar's footprint. The imbalance setting keeps its default of 300.
  2. Guard against na. TradingView's own examples wrap every footprint.*() call in a ternary, because the id parameter does not accept na. We do the same.
  3. Profile levels. vah().up_price() and val().down_price() give the outer edges of the Value Area. The POC midpoint is the average of its row's upper and lower bounds.
  4. Delta share. Raw delta grows with volume, so a 5,000-contract delta means little on a 500,000-contract bar. Dividing by total volume makes bars comparable. At the default 0.2, a bar with 10,000 total volume needs a delta of at least 2,000 to qualify (10,000 × 0.2 = 2,000).
  5. The filter. A bar passes when delta points one way and the close sits on the same side of the POC. That pairs "who was aggressive" with "where the volume actually traded."

The 0.2 threshold and the POC rule are our own choices. Tune them for your market and timeframe. Row size matters too: a larger ticks_per_row means fewer, taller rows and a coarser profile.

Test before you trust it. Paste the script into the Pine Editor on a Premium or Ultimate account and check it compiles on your symbol. We have not backtested this filter, and this article publishes no performance numbers for it.

Going further: rows, imbalances and higher timeframes

Loop through fp.rows() with a for...in loop to read every row. The official "Retrieving footprint row data" example does exactly this. It reads each row's buy volume, sell volume, delta and imbalance flags, then draws them as boxes. Counting rows where has_buy_imbalance() is true is a natural next filter.

You can also pull footprints from another timeframe. The docs explain that passing an expression built on request.footprint() into request.security() or request.security_lower_tf() calculates the footprint on that dataset. Their example requests the last confirmed higher-timeframe footprint with request.footprint(ticksInput)[1] and barmerge.lookahead_on. That is how you would get a prior-day POC and Value Area on an intraday chart. See the >request.footprint() section of the docs for the full code.

How to use it on the Loomi.Ai Desk

The Loomi.Ai Strategy Lab lists 16 named systems, each with "a public certificate you can inspect." Every card states the market and timeframe it was tested on, such as Coil on NQ 60-minute or Anchor on ES intraday. Anchor "Fades stretched moves back to the volume-weighted anchor price."

A footprint filter fits beside those systems as a second opinion. Here is a simple routine:

The Lab strategies are closed-source. The filter runs as your own separate indicator on your own charts. Loomi.Ai membership starts at $8/mo intro, then $20/mo. TradingView strategy access is sold separately: own a strategy for $99.99 one time (the Pine script and the Python version), or add Loomi Full Auto for $79 per month, which includes up to 5 active strategies; every strategy you run after that is a $19.99 per month add-on.

Put the research to work on your Desk

Explore the tools behind the research, compare the available access, or open your Desk if you already have an account.

Explore the tools → · Compare access → · Member sign in →

FAQ

Which TradingView plan do I need for request.footprint()? Premium or Ultimate. TradingView's docs say accounts on lower-tier plans cannot use scripts that request footprint data.

Why does request.footprint() return na? It returns na when no footprint data is available for that bar. Always check not na(fp) before calling any footprint.*() function.

Can I call request.footprint() twice with different row sizes? No. A script that makes more than one footprint request raises a runtime error. Pick one row size per script.

What is the difference between volume delta and a volume profile? Delta is buy volume minus sell volume, giving one number per bar or row. A volume profile shows how volume is spread across price. request.footprint() gives you both: delta() for the imbalance, and rows(), poc(), vah() and val() for the profile.

Loomi.Ai provides educational market research, not financial advice. Backtests are hypothetical, and no performance is claimed for the example script in this article.

Sources