Pine Script Volume Profile

Master this powerful technical analysis tool in TradingView's Pine Script that displays trading activity over a specified price range, revealing key areas of supply and demand for more informed trading decisions.

Last Updated on: Expertise: Advanced

What is Volume Profile?

Volume Profile is an advanced charting indicator that displays trading activity over a specified price range during a specified period. It shows the total volume traded at each price level, often represented as a horizontal histogram on the left or right side of the price chart. Unlike traditional volume indicators that show total volume traded over a specific time period (e.g., a bar), Volume Profile reveals *where* that volume was traded across different price points.

This insight into the distribution of volume provides a deeper understanding of market dynamics and participant behavior. Key components derived from the Volume Profile include:

Volume Profile is an indispensable tool for active traders, helping them identify key supply/demand zones, understand market sentiment, and plan their pine script strategies for entries, exits, and risk management.

Components and Calculation (Conceptual)

The Volume Profile is constructed by taking all the trades (volume) that occurred within a defined time window (e.g., a day, a week, or a fixed number of bars) and categorizing them into discrete price "bins" or levels. For each bin, the total volume traded at that price level is accumulated. This accumulation is then displayed as a horizontal bar, with longer bars indicating more volume traded at that price. The calculation conceptually involves:

  1. Define the Analysis Period: This can be a specific session (e.g., daily, weekly) or a fixed number of bars (e.g., last 100 bars).
  2. Determine Price Range: Identify the high and low prices within the analysis period.
  3. Define Bins: Divide the price range into a specified number of equal price "bins." For example, if the range is $10 and you want 10 bins, each bin would be $1 wide.
  4. Distribute Volume: For each bar within the analysis period, distribute its volume across the price bins that its `high`-`low` range covers. If a bar's `high` is in one bin and its `low` in another, its volume is proportionally distributed. Many implementations simply assign the bar's entire volume to the bin corresponding to its `close` price, or use a more complex mid-point distribution.
  5. Aggregate Volume per Bin: Sum up the volume for each price bin.
  6. Identify POC, VA, HVN, LVN: Once the volume distribution is complete, the POC is the bin with the highest volume. The Value Area is found by accumulating volume from the POC outwards until the specified percentage (e.g., 70%) of total volume is reached. HVNs are visually prominent large bars, and LVNs are visually prominent small bars or gaps.

Basic Volume Profile Implementation in Pine Script

Pine Script v5 significantly simplifies Volume Profile implementation with its dedicated `volume` module. It provides functions like `volume.profile_fixed()` and `volume.profile_session()` to draw these profiles directly on the chart.


//@version=5
indicator("My Volume Profile", overlay=true, max_bars_back=500) // overlay=true to plot on price chart.

// --- User Inputs ---
profileType = input.string("Fixed Range", title="Profile Type", options=["Fixed Range", "Session Profile"])
fixedRangeBars = input.int(100, title="Fixed Range Bars (for Fixed Range Profile)", minval=10, maxval=500, tooltip="Number of historical bars to calculate the fixed volume profile over.")
numberOfRows = input.int(20, title="Number of Rows (Bins)", minval=5, maxval=100, tooltip="Number of price bins for the volume profile histogram.")
valueAreaPercentage = input.int(70, title="Value Area (%)", minval=50, maxval=99, tooltip="Percentage of total volume contained within the Value Area.")

// --- Plotting Colors ---
upVolumeColor = input.color(color.new(color.lime, 40), title="Up Volume Color")
downVolumeColor = input.color(color.new(color.red, 40), title="Down Volume Color")
pocColor = input.color(color.new(color.purple, 0), title="POC Color")
vaColor = input.color(color.new(color.yellow, 60), title="Value Area Color")

// --- Implement Volume Profile Logic ---

if profileType == "Fixed Range"
    // Use volume.profile_fixed for a profile calculated over a fixed number of recent bars.
    profile = volume.profile_fixed(fixedRangeBars, numberOfRows, valueAreaPercentage)

    if barstate.islast and not na(profile)
        profile.plot(upVolumeColor, downVolumeColor, pocColor, vaColor)

else if profileType == "Session Profile"
    profile = volume.profile_session(numberOfRows, valueAreaPercentage)

    if not na(profile)
        profile.plot(upVolumeColor, downVolumeColor, pocColor, vaColor)

// Note: Pine Script's built-in `volume` module handles the complex drawing
// of the histogram, POC line, and Value Area shading automatically.
// We just need to define the parameters and tell it to plot.
Supply & Demand Hotspots: Volume Profile helps identify where the majority of trading activity occurred, pinpointing true supply and demand zones where institutions and large players have likely taken positions.

Practical Volume Profile Trading Strategies

1. Support and Resistance with HVNs & LVNs

HVNs act as strong areas of agreement and potential support/resistance, while LVNs represent areas of rapid transit and weak support/resistance.


//@version=5
strategy("VP: HVN/LVN Strategy", overlay=true)

// Use a session profile for clear daily levels
numberOfRows = input.int(30, title="Number of Rows (Bins)", minval=5, maxval=100)
valueAreaPercentage = input.int(70, title="Value Area (%)", minval=50, maxval=99)

profile = volume.profile_session(numberOfRows, valueAreaPercentage)

// Plotting (kept for visual context, usually this is in a separate indicator script)
if not na(profile)
    profile.plot(
        color.new(color.lime, 40),
        color.new(color.red, 40),
        color.new(color.purple, 0),
        color.new(color.yellow, 60)
    )

// Example strategy logic (conceptual, as Pine Script strategy cannot directly access historical HVN/LVN)
// The `volume.profile_session` only returns the current session's profile.
// To get previous session's POC/VA, use request.security + profile_fixed

if not na(profile) and barstate.isconfirmed
    pocPrice = profile.poc_price

    // Simple strategy: Price crosses POC
    longCondition = ta.crossover(close, pocPrice)
    shortCondition = ta.crossunder(close, pocPrice)

    if longCondition
        strategy.entry("Long POC Break", strategy.long)

    if shortCondition
        strategy.entry("Short POC Break", strategy.short)

    // Exit on opposite POC cross
    strategy.close("Long POC Break", when=shortCondition)
    strategy.close("Short POC Break", when=longCondition)

2. Value Area (VA) as Fair Price Zone

The Value Area represents where the majority of trades occurred and where market participants found "fair value."


//@version=5
strategy("VP: Value Area Strategy", overlay=true)

numberOfRows = input.int(30, title="Number of Rows (Bins)", minval=5, maxval=100)
valueAreaPercentage = input.int(70, title="Value Area (%)", minval=50, maxval=99)

profile = volume.profile_session(numberOfRows, valueAreaPercentage)

// Plotting
if not na(profile)
    profile.plot(
        color.new(color.lime, 40),
        color.new(color.red, 40),
        color.new(color.purple, 0),
        color.new(color.yellow, 60)
    )

// Store previous session VAH/VAL manually
var float prevVAH = na
var float prevVAL = na

isNewSession = ta.change(time("D"))

if isNewSession[1] and not na(profile[1])
    prevVAH := profile[1].value_area_high
    prevVAL := profile[1].value_area_low

prevVAH := nz(prevVAH[1], prevVAH)
prevVAL := nz(prevVAL[1], prevVAL)

if not na(prevVAH) and not na(prevVAL)

    // Absorption entries
    longAbsorption = open < prevVAL and close > prevVAL
    shortAbsorption = open > prevVAH and close < prevVAH

    if (longAbsorption)
        strategy.entry("Long VA Absorb", strategy.long)
        strategy.exit("Long VA Absorb Exit", profit=prevVAH, stop=prevVAL - syminfo.mintick * 10)

    if (shortAbsorption)
        strategy.entry("Short VA Absorb", strategy.short)
        strategy.exit("Short VA Absorb Exit", profit=prevVAL, stop=prevVAH + syminfo.mintick * 10)

    // Trend continuation entries
    longTrendDay = open > prevVAH and close > open and close > prevVAH
    shortTrendDay = open < prevVAL and close < open and close < prevVAL

    if (longTrendDay)
        strategy.entry("Long VA Trend", strategy.long)
        strategy.exit("Long VA Trend Exit", profit=strategy.position_avg_price + (prevVAH - prevVAL) * 1.5, stop=prevVAH)

    if (shortTrendDay)
        strategy.entry("Short VA Trend", strategy.short)
        strategy.exit("Short VA Trend Exit", profit=strategy.position_avg_price - (prevVAH - prevVAL) * 1.5, stop=prevVAL)

3. POC Rejection/Breakout

The Point of Control (POC) is the single most traded price level. It acts as a significant magnet and a strong area of support/resistance.


//@version=5
strategy("VP: POC Strategy", overlay=true)

numberOfRows = input.int(30, title="Number of Rows (Bins)", minval=5, maxval=100)
valueAreaPercentage = input.int(70, title="Value Area (%)", minval=50, maxval=99)

profile = volume.profile_session(numberOfRows, valueAreaPercentage)

// Plotting
if not na(profile)
    profile.plot(
        color.new(color.lime, 40),
        color.new(color.red, 40),
        color.new(color.purple, 0),
        color.new(color.yellow, 60)
    )

// Get POC from previous session
var float prevPOC = na

isNewSession = ta.change(time("D"))

if isNewSession[1] and not na(profile[1])
    prevPOC := profile[1].poc_price

prevPOC := nz(prevPOC[1], prevPOC)

// Strategy Logic
if not na(prevPOC)

    // Rejection logic
    longPOCRejection = low < prevPOC and close > prevPOC and close[1] < prevPOC[1]
    shortPOCRejection = high > prevPOC and close < prevPOC and close[1] > prevPOC[1]

    if (longPOCRejection)
        strategy.entry("Long POC Rejection", strategy.long)
        strategy.exit(
            "Long POC Rejection Exit",
            profit=prevPOC + (prevPOC - open) * 1.0,
            stop=prevPOC - (prevPOC - low) * 1.0
        )

    if (shortPOCRejection)
        strategy.entry("Short POC Rejection", strategy.short)
        strategy.exit(
            "Short POC Rejection Exit",
            profit=prevPOC - (open - prevPOC) * 1.0,
            stop=prevPOC + (high - prevPOC) * 1.0
        )

    // Breakout logic
    longPOCBreakout = close > prevPOC and close[1] <= prevPOC[1] and volume > ta.sma(volume, 20) * 1.2
    shortPOCBreakout = close < prevPOC and close[1] >= prevPOC[1] and volume > ta.sma(volume, 20) * 1.2

    if (longPOCBreakout)
        strategy.entry("Long POC Breakout", strategy.long)
        strategy.exit(
            "Long POC Breakout Exit",
            profit=strategy.position_avg_price + (high - prevPOC) * 1.5,
            stop=prevPOC
        )

    if (shortPOCBreakout)
        strategy.entry("Short POC Breakout", strategy.short)
        strategy.exit(
            "Short POC Breakout Exit",
            profit=strategy.position_avg_price - (prevPOC - low) * 1.5,
            stop=prevPOC
        )

Optimizing Volume Profile Performance

To get the most from Volume Profile in Pine Script:

Institutional Footprint: Volume Profile helps visualize where "smart money" (large institutions) has likely accumulated or distributed positions, as these are often the areas of highest volume.

Common Volume Profile Pitfalls

Conclusion

Volume Profile is an exceptionally powerful and insightful technical analysis tool available in Pine Script for TradingView. By providing a clear visualization of trading activity across different price levels, it allows traders to identify key areas of supply and demand, understand market conviction, and pinpoint significant support and resistance zones. Mastering its components – the Point of Control (POC), Value Area (VA), High Volume Nodes (HVN), and Low Volume Nodes (LVN) – can significantly enhance your pine script strategies. While requiring careful consideration of its settings and integration with other technical analysis, Volume Profile provides a unique "x-ray" view into market structure, helping you make more informed and robust trading decisions by understanding where the true battle between buyers and sellers took place.