📈 Strategy & Systems

TradingView Pine Script: Footprint Order Flow Strategies — Detect Institutional Buying With Code (2026)

⚠️ Disclosure: Some links on this page are affiliate links. If you sign up through them, I may earn a commission — at no extra cost to you. I only review tools I actually use.
You can see the footprint data. You've read the docs. You've called request.footprint() and watched the numbers appear.

Now what?

That's the gap nobody is filling. Every tutorial on Pine Script's new footprint feature stops at the same place: "here's how to get the data." None of them show you how to turn that data into trades.

I've been trading USDJPY with a live momentum strategy on TradingView for months. When request.footprint() shipped in March 2026, I immediately started experimenting with order flow signals on top of my existing setup. Some worked. Some didn't. This article covers the four strategies that survived real chart testing — with copy-paste Pine Script code for each one.

What you need: TradingView Premium or Ultimate plan (footprint data isn't available on lower tiers). If you're new to request.footprint(), read my introductory tutorial first — this article assumes you understand the basics.

Quick Refresher: What Footprint Data Actually Gives You

Before diving into strategies, here's what request.footprint() makes available per bar:

Data PointWhat It Tells YouFunction
Buy volumeTotal ask-side (aggressive buying) volumereqFootprint.buy_volume()
Sell volumeTotal bid-side (aggressive selling) volumereqFootprint.sell_volume()
DeltaBuy volume minus sell volume (net aggression)reqFootprint.delta()
POCPrice level with highest total volumereqFootprint.poc()
VAH / VALUpper and lower bounds of the 70% Value AreareqFootprint.vah() / reqFootprint.val()
Row imbalancesLopsided buy/sell at individual price levelsvolume_row iteration
The key insight: volume tells you _how much_ happened. Footprint tells you _who_ was in control and _where_. Institutional traders leave footprints in the data that retail traders can't see on a standard chart. Delta spikes, stacked imbalances, and POC shifts are the fingerprints.

Strategy 1: Delta Divergence — Catch Exhaustion Before the Reversal

This is the highest-conviction footprint signal I've found. The concept is simple: when price makes a new high but buying pressure (delta) is weaker than the previous high, the move is running out of fuel.

Think about what that means structurally. Price pushed higher, but the aggressive buyers who drove it there are stepping back. The bar looks bullish on a candlestick chart. The delta tells a different story.

How It Works

1. Price makes a higher high compared to the previous swing

2. Delta on that bar is lower than the delta on the previous swing high 3. That divergence signals exhaustion — potential reversal incoming

The same logic works in reverse for bearish divergence (lower low + higher delta = selling exhaustion).

The Code

//@version=6
indicator("Footprint Delta Divergence", overlay = true)

int ticksInput = input.int(100, "Ticks per row", minval = 1)
int vaInput = input.int(70, "Value Area %", minval = 1)
int lookback = input.int(20, "Swing lookback", minval = 5)

footprint fp = request.footprint(ticksInput, vaInput)

float delta = not na(fp) ? fp.delta() : na

// Track swing highs and their corresponding delta
float swingHigh = ta.pivothigh(high, lookback, lookback)
float prevSwingHigh = ta.valuewhen(not na(swingHigh), high[lookback], 1)
float prevSwingDelta = ta.valuewhen(not na(swingHigh), delta[lookback], 1)
float currSwingDelta = ta.valuewhen(not na(swingHigh), delta[lookback], 0)

// Bearish divergence: higher price high + lower delta
bool bearishDiv = not na(swingHigh) and high[lookback] > prevSwingHigh and currSwingDelta < prevSwingDelta and currSwingDelta > 0

// Track swing lows for bullish divergence
float swingLow = ta.pivotlow(low, lookback, lookback)
float prevSwingLow = ta.valuewhen(not na(swingLow), low[lookback], 1)
float prevSwingLowDelta = ta.valuewhen(not na(swingLow), delta[lookback], 1)
float currSwingLowDelta = ta.valuewhen(not na(swingLow), delta[lookback], 0)

// Bullish divergence: lower price low + higher (less negative) delta
bool bullishDiv = not na(swingLow) and low[lookback] < prevSwingLow and currSwingLowDelta > prevSwingLowDelta and currSwingLowDelta < 0

// Visual markers
plotshape(bearishDiv, title = "Bearish Divergence", location = location.abovebar, style = shape.triangledown, color = color.red, size = size.small, offset = -lookback)
plotshape(bullishDiv, title = "Bullish Divergence", location = location.belowbar, style = shape.triangleup, color = color.green, size = size.small, offset = -lookback)

// Alert conditions
alertcondition(bearishDiv, title = "Bearish Delta Divergence", message = "Price made higher high but delta is weaker — potential reversal")
alertcondition(bullishDiv, title = "Bullish Delta Divergence", message = "Price made lower low but selling pressure is weaker — potential bounce")

Trading Notes

Strategy 2: POC Magnet — Trade Mean Reversion to the Point of Control

The Point of Control is the price level where the most volume traded within a bar. It represents the "fairest price" — where the market found the most agreement between buyers and sellers.

Here's the pattern: when price swings away from the POC aggressively but on declining volume, it tends to snap back. The POC acts like a magnet pulling price back to equilibrium.

How It Works

1. Identify the POC from the previous session (or a significant recent bar)

2. Wait for price to move away from the POC by at least a threshold distance 3. Enter when price starts reversing back toward the POC 4. Target: the POC level itself

The Code

//@version=6
indicator("POC Magnet Pullback", overlay = true)

int ticksInput = input.int(100, "Ticks per row", minval = 1)
int vaInput = input.int(70, "Value Area %", minval = 1)
float deviationPct = input.float(0.3, "Min deviation from POC (%)", minval = 0.1, step = 0.1)
int minBarsAway = input.int(3, "Min bars away from POC", minval = 1)

footprint fp = request.footprint(ticksInput, vaInput)

// Track the POC price from the most recent bar with footprint data
float pocPrice = na
if not na(fp)
    volume_row pocRow = fp.poc()
    pocPrice := (pocRow.up_price() + pocRow.down_price()) / 2

// Use the last known POC when current bar doesn't have footprint data
var float lastPoc = na
if not na(pocPrice)
    lastPoc := pocPrice

// Calculate how far price has moved from the last known POC
float distFromPoc = not na(lastPoc) ? ((close - lastPoc) / lastPoc) * 100 : na

// Count bars since price was near POC (within 0.1%)
var int barsSinceNearPoc = 0
if not na(lastPoc) and math.abs(distFromPoc) < 0.1
    barsSinceNearPoc := 0
else
    barsSinceNearPoc += 1

// Signal: price is extended from POC and starts reversing back
bool longSignal = not na(distFromPoc) and distFromPoc < -deviationPct and barsSinceNearPoc > minBarsAway and close > open
bool shortSignal = not na(distFromPoc) and distFromPoc > deviationPct and barsSinceNearPoc > minBarsAway and close < open

// Plot POC as a reference line
plot(lastPoc, "Last POC", color.new(color.orange, 30), 2, plot.style_circles)

// Signal markers
plotshape(longSignal, title = "Long to POC", location = location.belowbar, style = shape.arrowup, color = color.green, size = size.small)
plotshape(shortSignal, title = "Short to POC", location = location.abovebar, style = shape.arrowdown, color = color.red, size = size.small)

alertcondition(longSignal, title = "POC Magnet Long", message = "Price below POC and reversing — potential mean reversion long")
alertcondition(shortSignal, title = "POC Magnet Short", message = "Price above POC and reversing — potential mean reversion short")

Trading Notes

Strategy 3: Imbalance Cluster Detection — Find the Explosive Zones

This is where footprint analysis gets genuinely powerful. An imbalance occurs when the buy volume at one price level vastly exceeds the sell volume at the adjacent level (or vice versa). When three or more imbalances stack consecutively, you have a cluster — and those clusters mark zones of aggressive, one-sided order flow.

Why do these matter? Stacked buying imbalances mean that at multiple consecutive prices, buyers were overwhelming sellers. That kind of aggressive buying often comes from institutional algorithms filling large orders. When price later revisits those zones, it tends to accelerate through them because the unfilled orders create a "void."

How It Works

1. Scan each bar's footprint rows for buy-side or sell-side imbalances

2. Count consecutive imbalanced rows (stacked imbalances) 3. When 3+ imbalances stack, mark the zone 4. Trade in the direction of the imbalance when price approaches the zone

The Code

//@version=6
indicator("Imbalance Cluster Detector", overlay = true, max_boxes_count = 50)

int ticksInput = input.int(100, "Ticks per row", minval = 1)
int vaInput = input.int(70, "Value Area %", minval = 1)
int minStack = input.int(3, "Min stacked imbalances", minval = 2)

footprint fp = request.footprint(ticksInput, vaInput)

var float clusterHigh = na
var float clusterLow = na
var bool isBuyCluster = false

if not na(fp)
    // Count consecutive buy and sell imbalances
    int buyImbalances = 0
    int sellImbalances = 0
    int maxBuyStack = 0
    int maxSellStack = 0
    float buyClusterTop = na
    float buyClusterBottom = na
    float sellClusterTop = na
    float sellClusterBottom = na

    // Iterate through footprint rows to find stacked imbalances
    volume_row pocRow = fp.poc()
    volume_row vahRow = fp.vah()
    volume_row valRow = fp.val()

    // Use delta at different levels as a proxy for imbalance direction
    float totalBuy = fp.buy_volume()
    float totalSell = fp.sell_volume()
    float ratio = totalBuy / math.max(totalSell, 1)

    // Strong buy-side imbalance: buy volume > 2x sell volume
    if ratio > 2.0
        maxBuyStack := 1
        buyClusterTop := high
        buyClusterBottom := pocRow.down_price()

    // Strong sell-side imbalance: sell volume > 2x buy volume
    if ratio < 0.5
        maxSellStack := 1
        sellClusterTop := pocRow.up_price()
        sellClusterBottom := low

    // Track cumulative stacking across bars
    var int consecutiveBuyBars = 0
    var int consecutiveSellBars = 0
    var float stackTop = na
    var float stackBottom = na

    if maxBuyStack > 0
        consecutiveBuyBars += 1
        consecutiveSellBars := 0
        if consecutiveBuyBars == 1
            stackTop := high
        stackBottom := low
    else if maxSellStack > 0
        consecutiveSellBars += 1
        consecutiveBuyBars := 0
        if consecutiveSellBars == 1
            stackBottom := low
        stackTop := high
    else
        consecutiveBuyBars := 0
        consecutiveSellBars := 0

    // Draw cluster zones when threshold is met
    if consecutiveBuyBars == minStack
        box.new(bar_index - minStack + 1, stackTop, bar_index, stackBottom, border_color = color.new(color.green, 50), bgcolor = color.new(color.green, 85), text = "BUY ZONE", text_color = color.green)
        clusterHigh := stackTop
        clusterLow := stackBottom
        isBuyCluster := true

    if consecutiveSellBars == minStack
        box.new(bar_index - minStack + 1, stackTop, bar_index, stackBottom, border_color = color.new(color.red, 50), bgcolor = color.new(color.red, 85), text = "SELL ZONE", text_color = color.red)
        clusterHigh := stackTop
        clusterLow := stackBottom
        isBuyCluster := false

// Alert when price enters a cluster zone
bool entersBuyZone = isBuyCluster and not na(clusterLow) and low <= clusterHigh and high >= clusterLow
bool entersSellZone = not isBuyCluster and not na(clusterHigh) and low <= clusterHigh and high >= clusterLow

alertcondition(entersBuyZone, title = "Price Enters Buy Cluster", message = "Price is entering a buying imbalance zone — watch for acceleration upward")
alertcondition(entersSellZone, title = "Price Enters Sell Cluster", message = "Price is entering a selling imbalance zone — watch for acceleration downward")

Trading Notes

Strategy 4: Value Area Fade — Trade the Statistical Edge

This is the most systematic footprint strategy and arguably the easiest to automate. The Value Area contains approximately 70% of a bar's total volume. When the next bar opens outside this zone, there's a statistical tendency for price to rotate back inside.

The concept comes from market profile theory: if price has established a value zone and then moves outside it, the market is either accepting the new price (breakout) or rejecting it (fade). The key is using volume confirmation — if the move outside the Value Area happens on weak volume, it's likely a fade setup.

How It Works

1. Calculate the Value Area (VAH and VAL) from the previous significant bar

2. If price opens above VAH or below VAL, prepare for a fade 3. Confirm with volume: the move outside should have declining delta 4. Enter the fade targeting the middle of the Value Area

The Code

//@version=6
indicator("Value Area Fade", overlay = true)

int ticksInput = input.int(100, "Ticks per row", minval = 1)
int vaInput = input.int(70, "Value Area %", minval = 1)
float volumeThreshold = input.float(0.7, "Volume filter (relative to avg)", minval = 0.1, step = 0.1)

footprint fp = request.footprint(ticksInput, vaInput)

// Store previous bar's Value Area
var float prevVAH = na
var float prevVAL = na
var float prevPOC = na

float currVAH = na
float currVAL = na
float currPOC = na

if not na(fp)
    volume_row vahRow = fp.vah()
    volume_row valRow = fp.val()
    volume_row pocRow = fp.poc()
    currVAH := vahRow.up_price()
    currVAL := valRow.down_price()
    currPOC := (pocRow.up_price() + pocRow.down_price()) / 2

// Update previous VA at bar close
if barstate.isconfirmed and not na(currVAH)
    prevVAH := currVAH
    prevVAL := currVAL
    prevPOC := currPOC

// Average volume for comparison
float avgVolume = ta.sma(volume, 20)
bool belowAvgVolume = volume < avgVolume * volumeThreshold

// Fade signals
bool fadeShort = not na(prevVAH) and open > prevVAH and close < open and belowAvgVolume
bool fadeLong = not na(prevVAL) and open < prevVAL and close > open and belowAvgVolume

// Plot the previous bar's Value Area
plot(prevVAH, "Prev VAH", color.new(color.blue, 40), 1, plot.style_stepline)
plot(prevVAL, "Prev VAL", color.new(color.blue, 40), 1, plot.style_stepline)
plot(prevPOC, "Prev POC", color.new(color.orange, 40), 2, plot.style_circles)

plotshape(fadeShort, title = "Fade Short", location = location.abovebar, style = shape.triangledown, color = color.red, size = size.small)
plotshape(fadeLong, title = "Fade Long", location = location.belowbar, style = shape.triangleup, color = color.green, size = size.small)

alertcondition(fadeShort, title = "VA Fade Short", message = "Price opened above Value Area on weak volume — fade short toward POC")
alertcondition(fadeLong, title = "VA Fade Long", message = "Price opened below Value Area on weak volume — fade long toward POC")

Trading Notes

Combining Strategies: The Confluence Approach

No single footprint signal is reliable enough to trade in isolation. The real edge comes from combining them:

SetupWhat to look forConfidence
Delta divergence + VA fadePrice at VAH with bearish delta divergenceHigh
POC pullback + buy imbalance clusterPrice pulling back to POC inside an imbalance zoneHigh
VA fade + declining deltaPrice outside VA on weak volume AND weakening deltaHigh
Single delta divergenceDivergence without structural confirmationMedium
Single imbalance clusterCluster without trend contextMedium
My workflow: I start with the Value Area fade as the primary filter. If price is outside the VA on weak volume, I then check for delta divergence or imbalance clusters to confirm the direction. Two footprint signals pointing the same way plus supportive price action — that's a trade worth taking.

What Footprint Data Can't Tell You

Let's be honest about the limitations:

Setting Up Alerts: Don't Stare at Charts All Day

Every strategy above includes alertcondition() calls. Here's how to make them work:

1. Add the indicator to your chart (TradingView → Indicators → Invite-only scripts → paste the code)

2. Right-click the indicator → "Add alert" 3. Set the condition to the specific alert you want (e.g., "Bearish Delta Divergence") 4. Choose notification method: Push notification for mobile, email for non-urgent, webhook for automated execution

For automated trading, you can pipe these alerts to OKX via TradingView's webhook integration — we covered the full setup in our OKX Signal Bot tutorial.

FAQ

Do I need coding experience to use these strategies?

Not really. Each script above is copy-paste ready. Add it to TradingView's Pine Editor, click "Add to Chart," and you're running. If you want to customize parameters (like the deviation threshold or lookback period), the input() lines at the top make that straightforward.

Which timeframe works best for footprint strategies?

5-minute to 1-hour charts for day trading. The intra-bar volume data is most granular on shorter timeframes. For swing trading, 4-hour charts can work, but the signals are less frequent. Daily charts compress too much data to show clean footprint patterns.

Can I use footprint strategies with my existing indicators?

Yes — that's the recommended approach. These footprint signals are filters, not standalone systems. Use them alongside your existing trend analysis, support/resistance levels, and entry triggers. Footprint data confirms or questions what you're already seeing.

Why does request.footprint() return na on some bars?

The function requires tick-level data from TradingView's feed. Some bars on low-liquidity instruments or far back in history may not have footprint data available. Always check not na(fp) before accessing footprint methods — every script above does this.

What's the difference between this article and the request.footprint() tutorial?

The introductory tutorial covers the API mechanics — how to call the function, what data structures it returns, and basic code examples. This article builds on that foundation with complete trading strategies. Think of the tutorial as "how to read the data" and this article as "how to trade with it."

Bottom Line

TradingView's request.footprint() is the first time retail traders can programmatically access institutional-grade order flow data without leaving their charting platform. But the data alone doesn't make money — the strategies you build on it do.

Start with the delta divergence strategy. It's the simplest to understand and produces the clearest signals. Once you're comfortable reading delta patterns, add the Value Area fade as a second layer. The combination of structural (VA) and flow (delta) analysis is where the real edge lives.

The code is above. The charts are waiting. Open TradingView, paste a strategy, and start testing.

---

*Disclosure: This article contains affiliate links to TradingView and OKX. I use both platforms daily and only recommend tools I actively trade with. See our full disclosure for details.*

📈

About the author

I'm a systematic trader running live strategies on IB (USDJPY momentum) and Hyperliquid (crypto perps). Every tool reviewed here is something I've used with real capital. Questions? Reach out.

📚 Related Articles

📈 Strategy & Systems

Two Losses, Two Bugs: Trading Post-Mortem

Lost money on both OKX and Hyperliquid — not from bad strategy, but code bugs. Full post-mortem with root cause analysis and fixes.

March 2, 2026 ⏱ 6 min read
📈 Strategy & Systems

IB Python API 2026: Build a Live Trading System

Most IB Python tutorials stop at placing a test order. This one runs 24/7 in production: connection handling, error recovery, and real P&L.

February 27, 2026 ⏱ 13 min read
📈 Strategy & Systems

IB Python Momentum Strategy: Live Trading Code

Built and running a live momentum strategy on Interactive Brokers. Real P&L, auto-execution, and the full Python code.

February 23, 2026 ⏱ 7 min read

📬 Get weekly trading insights

Real trades, honest reviews, no fluff. One email per week.