Volume Profile Lite · Free

See where trading was busiest.

Find the busy price zones on your TradingView chart. Then see how price behaves around them.

A map of estimated activity. It does not give buy/sell signals.

Pastel clay illustration of volume collecting into horizontal price rows, highlighting the busiest row and framing the value area's upper and lower edges.
Workflow illustration
  1. Group volume by price

    Bars contribute to horizontal rows.

  2. Find the busiest row

    Highlight the point of control.

  3. Frame the value area

    Mark the main concentration and both edges.

See the profile explained

What is Volume Profile Lite?

Volume Profile Lite is a free TradingView indicator that estimates volume by price and draws a horizontal profile, point of control and value area boundaries. Choose a rolling window or the current daily session. It distributes candle volume across price rows, so the profile is an estimate rather than a record of individual trades.

What is the difference between Rolling and Session mode?

Rolling uses a fixed number of recent candles, two hundred by default. Session uses the symbol's current daily boundary, which may differ from local midnight. The session calculation includes at most the latest two thousand bars. Read window and detail settings

Why do the POC and value area lines move?

The profile recalculates as the live candle changes or the selected window advances. Its busiest row and value boundaries can therefore move. Volume Profile Lite redraws the current profile; it does not store a historical profile for every candle. Read why the profile changes

Does Volume Profile Lite show buy and sell order flow?

No. It spreads each candle's available volume across that candle's price range. It does not separate buyer and seller volume or reconstruct individual trades. Depending on the instrument and feed, the volume may represent tick activity. Read the volume estimation limits

Why this tool exists.

A chart shows where price went. This tool adds another clue: where was trading busiest?

From trades to a map

Imagine a market’s sales receipts. Add up how many units sold at each price. The biggest total shows its busiest price. That is the idea behind Volume Profile: group trading activity by price.

How our free version helps

VP Lite estimates that map from the price bars, or candles, on your chart. Busy areas give you places to watch as price returns to them or moves beyond them.

Slide to build the map.

Press Play or move the slider. Watch volume build the rows on the right.

Fictional candles · 10 rows for clarity; the indicator starts with 60. The longest row fills the width at each step. Rows and levels update as data changes.

One candle becomes several rows. This candle’s 12 units are shared across $100–$103. All rows start equal, so no price stands out yet.

This candle adds 12 volume units.

Latest price
$102.00
Gold row · tied
$100.15
Blue zone
$100.00$102.10
Find these markers on a real chart ↓

How can this help you make money?

Use the map to choose an area to study. Then use your trading rules to decide when to enter, where to take profit, and when to exit if you are wrong.

Across trades, gains must cover losses and trading costs. The map alone cannot tell you whether a trade will succeed.

A simple example: where a gain or loss comes from

Suppose the map helps you find a busy area to watch. Your entry rules are met, and you buy 1 unit at $100. You plan to take profit at $106 or exit a losing trade near $97.

If you sell at $106
$6 gain before costs.
If you sell at $97
$3 loss before costs.

These are made-up prices, not signals from VP Lite. Fees and the price you actually get can change the result. A bigger possible gain does not tell you how often the trade will work.

Practise with virtual money first. Record your entries, exits and costs to see how your complete plan performs.

Understand the map.

Longer rows mean more estimated volume at that price.

Gold line

Busiest price row · POC.

Blue zone

About 70% of estimated volume · Value Area.

Dashed edges

Top: VAH. Bottom: VAL.

Now find them on a real chart.

ETH/USDT · 15-minute candles · Binance

Real TradingView ETH/USDT 15-minute chart with Volume Profile Lite. Price is inside the blue volume area, just below the gold POC line. The upper and lower dashed edges surround that area.
TradingView snapshot · . Guide labels 1–3 added.Open full-size chart ↗ (new tab)

Here, price is inside the blue zone. Compare that with “Inside” in the example below.

  1. Gold line · POC

    2,502.08 — the busiest price row.

  2. Upper edge · VAH

    2,516.67 — the top of the blue zone.

  3. Lower edge · VAL

    2,478.27 — the bottom of the blue zone.

All prices in USDT. Default settings: Rolling 200 · 60 rows · 70% Value Area.

Your chart will show different prices and levels as its data changes.

Try three situations.

Choose an example to see what changes.

Price is inside the busy zone.The white price line sits between the two dashed edges. Blue rows mark the busy zone. The gold row has the most estimated volume. Dashed lines mark the upper and lower edges.
Top dashed line · VAHBottom dashed line · VALGold line · POC
Made-up example. The blue zone stays fixed so you can compare. On your chart, the profile updates as data changes.

Price is inside the busy zone.

What you see
The white price line sits between the two dashed edges.
What it means
Price is within the area that holds most of this profile’s estimated volume.
What to watch next
Watch what happens at either edge. Being inside does not mean price must stay there.
Quick check: price is above the blue zone. Does that mean buy?

No. It only tells you where price is relative to that area. Watch whether the move holds or comes back inside, then use your own entry and risk rules.

Add it to your chart.

Free and open-source. Copy, paste, and keep it.

  1. Open the editor

    On a TradingView chart, use the Pine icon to open Pine Editor.

  2. Copy and paste

    Copy the code below. Paste it in place of the editor’s sample code.

  3. Add to chart

    Choose Add to chart, then save the script to keep it.

Indicator code

Copy the code as it is. No coding needed.

View the full code
//@version=6
// ─────────────────────────────────────────────────────────────────────────────
// Volume Profile Lite — ProEA Lab
// Free & open-source volume-at-price: the value-area histogram with POC,
// VAH and VAL, in Session or Rolling mode.
//
// Honest by design:
//  · Engine = bar-distribution: each bar's volume is spread across its
//    [low, high] range into price rows — an approximation, not tick data.
//  · The profile redraws as the live bar develops; closed-bar history is
//    read as-is and never rewritten.
//  · Context, not signals — it shows where volume traded, never what price
//    will do.
//
// © ProEA Lab — source available for download.
// ─────────────────────────────────────────────────────────────────────────────
indicator("Volume Profile Lite — ProEA Lab", "VP Lite", overlay = true, max_boxes_count = 200, max_lines_count = 50, max_labels_count = 20)

// ───────────────────────── Inputs ─────────────────────────
gP = "Profile"
inMode     = input.string("Rolling", "Mode", options = ["Rolling", "Session"], group = gP, tooltip = "Rolling = the last N bars. Session = the current day's profile.")
inLookback = input.int(200, "Rolling lookback (bars)", minval = 20, maxval = 500, group = gP)
inBins     = input.int(60, "Rows (bins)", minval = 10, maxval = 120, group = gP, tooltip = "Price resolution of the histogram.")
inVApct    = input.float(70, "Value Area %", minval = 50, maxval = 90, group = gP) / 100.0

gS = "Style"
inAnchor   = input.string("Right", "Profile side", options = ["Right", "Left"], group = gS, tooltip = "Right = profile to the right of the last bar. Left = overlays the start of the window.")
inWidth    = input.int(60, "Profile width (bars)", minval = 10, maxval = 150, group = gS)
inShowLvls = input.bool(true, "Show POC / VAH / VAL lines", group = gS)
inShowTags = input.bool(true, "Show price tags", group = gS)

// ───────────────────────── Fixed Aurora palette ─────────────────────────
color cVA  = #5B8DEF       // value-area rows
color cPOC = #FFD24A       // point of control
color cOut = #3A4257       // rows outside the value area
color cTxt = #C9CEDA
color cPnl = #0E1118
color cInk = #0B0E14

// ───────────────────────── History buffer ─────────────────────────
// 2000 bars covers a full 1-minute session (~1440 bars); the Session loop is
// hard-capped to the same depth so lower timeframes degrade gracefully.
max_bars_back(high, 2000)
max_bars_back(low, 2000)
max_bars_back(volume, 2000)

float rHi = ta.highest(high, inLookback)
float rLo = ta.lowest(low, inLookback)

// session tracking
bool newDay = ta.change(time("D")) != 0
var float sHi = high
var float sLo = low
var int   sStart = bar_index
if newDay
    sHi := high
    sLo := low
    sStart := bar_index
else
    sHi := math.max(sHi, high)
    sLo := math.min(sLo, low)

// ───────────────────────── Pools + header chip ─────────────────────────
var array<box>   g_boxes = array.new<box>()
var array<line>  g_lines = array.new<line>()
var array<label> g_lbls  = array.new<label>()
var table hdr = table.new(position.top_right, 2, 1, border_width = 1, border_color = #00000055)

f_clear() =>
    for b in g_boxes
        box.delete(b)
    array.clear(g_boxes)
    for l in g_lines
        line.delete(l)
    array.clear(g_lines)
    for t in g_lbls
        label.delete(t)
    array.clear(g_lbls)

// ───────────────────────── Build + render on the last bar ─────────────────────────
if barstate.islast
    f_clear()
    bool  sess  = inMode == "Session"
    float gHi   = sess ? sHi : rHi
    float gLo   = sess ? sLo : rLo
    int   loopN = sess ? math.min(bar_index - sStart + 1, 2000) : math.min(inLookback, bar_index + 1)
    float rng   = gHi - gLo
    if rng > 0 and loopN > 0 and not na(volume)
        float binSz = rng / inBins
        array<float> vol = array.new_float(inBins, 0.0)

        // Fast engine: distribute each bar's volume across its [low, high] span,
        // overlap-weighted per row (the same binning contract as the full engine).
        for i = 0 to loopN - 1
            float bl = low[i]
            float bh = high[i]
            float bv = nz(volume[i])
            if bv > 0 and bh >= bl
                if bh == bl
                    int idx = math.max(0, math.min(inBins - 1, int(math.floor((bl - gLo) / binSz))))
                    array.set(vol, idx, array.get(vol, idx) + bv)
                else
                    int b0 = math.max(0, math.min(inBins - 1, int(math.floor((bl - gLo) / binSz))))
                    int b1 = math.max(0, math.min(inBins - 1, int(math.floor((bh - gLo) / binSz))))
                    float span = bh - bl
                    for b = b0 to b1
                        float binLo = gLo + b * binSz
                        float ov = math.min(bh, binLo + binSz) - math.max(bl, binLo)
                        if ov > 0
                            array.set(vol, b, array.get(vol, b) + bv * ov / span)

        // totals + POC
        float maxV = 0.0
        int  pocIdx = 0
        float totV = 0.0
        for b = 0 to inBins - 1
            float v = array.get(vol, b)
            totV += v
            if v > maxV
                maxV := v
                pocIdx := b

        // value area: expand row-by-row around the POC toward the richer side
        float acc = array.get(vol, pocIdx)
        int up = pocIdx
        int dn = pocIdx
        float target = totV * inVApct
        while acc < target and (up < inBins - 1 or dn > 0)
            float vUp = up < inBins - 1 ? array.get(vol, up + 1) : -1.0
            float vDn = dn > 0 ? array.get(vol, dn - 1) : -1.0
            if vUp >= vDn
                up += 1
                acc += math.max(vUp, 0.0)
            else
                dn -= 1
                acc += math.max(vDn, 0.0)

        float vah = gLo + (up + 1) * binSz
        float val = gLo + dn * binSz
        float poc = gLo + (pocIdx + 0.5) * binSz

        int gap   = 2
        int xBase = inAnchor == "Left" ? math.max(0, bar_index - loopN - gap) : bar_index + gap
        int xl    = math.max(0, bar_index - loopN)
        int xrr   = bar_index + inWidth + gap

        // histogram — solid rows, value-area highlight, POC in gold
        if maxV > 0
            for b = 0 to inBins - 1
                float v = array.get(vol, b)
                if v > 0
                    int len = int(math.round(inWidth * v / maxV))
                    if len >= 1
                        float binLo = gLo + b * binSz
                        float binHi = binLo + binSz
                        float ratio = v / maxV
                        bool inVA = b >= dn and b <= up
                        color c = b == pocIdx ? cPOC : inVA ? cVA : cOut
                        int  tp = b == pocIdx ? 0 : math.min(90, inVA ? int(14 + (1 - ratio) * 26) : int(48 + (1 - ratio) * 30))
                        array.push(g_boxes, box.new(xBase, binHi, xBase + len, binLo, border_color = na, bgcolor = color.new(c, tp)))

        // POC / VAH / VAL lines
        if inShowLvls
            array.push(g_lines, line.new(xl, poc, xrr, poc, color = cPOC, width = 2))
            array.push(g_lines, line.new(xl, vah, xrr, vah, color = color.new(cVA, 15), style = line.style_dashed))
            array.push(g_lines, line.new(xl, val, xrr, val, color = color.new(cVA, 15), style = line.style_dashed))

        // price tags
        if inShowTags
            int xt = xrr + 1
            array.push(g_lbls, label.new(xt, poc, "POC " + str.tostring(poc, format.mintick), style = label.style_label_left, color = color.new(cPOC, 8), textcolor = cInk, size = size.small))
            array.push(g_lbls, label.new(xt, vah, "VAH " + str.tostring(vah, format.mintick), style = label.style_label_left, color = color.new(cVA, 30), textcolor = #ffffff, size = size.tiny))
            array.push(g_lbls, label.new(xt, val, "VAL " + str.tostring(val, format.mintick), style = label.style_label_left, color = color.new(cVA, 30), textcolor = #ffffff, size = size.tiny))

        // header chip
        table.cell(hdr, 0, 0, "◆ VP Lite", text_color = cPOC, text_size = size.small, text_halign = text.align_left, bgcolor = cPnl)
        table.cell(hdr, 1, 0, "ProEA Lab", text_color = color.new(cTxt, 40), text_size = size.small, text_halign = text.align_right, bgcolor = cPnl)
First time? Keep the default settings.

Rolling · 200 candles · 60 rows · 70% Value Area. This shows activity across the latest 200 candles. Find the gold line and blue zone first; adjust the detail later.

Explore more when you need it.

Optional topics. Open the one that answers your next question.

Find the same markers on your chart

POC means Point of Control: the middle of the busiest price row. VAH and VAL mean Value Area High and Low: the upper and lower edges of the blue area.

Ask one question: is price inside, above, or below the blue area? Inside means price is in the main volume area. Above or below means it is outside. Then watch whether it stays there or returns.

Did price stay outside, or come back in?

Stays outside → acceptance. Price crosses an edge and keeps trading beyond it. Watch where candles close and whether later pullbacks stay outside. Volume building there can add context.

Comes back inside → rejection. Price crosses an edge, then returns to the old area. The move has not held so far. A return to the gold line is possible, but is not promised.

There is no magic number of candles that proves either outcome. VP Lite does not label these events or send alerts. Mark an old edge yourself if you want to compare later price with it: the live profile can move.

What the profile’s shape tells you

The shape shows how activity is spread across prices. Read it as a description of the selected candles.

D shape

Most activity is near the middle.

P shape

More activity is near the top.

b shape

More activity is near the bottom.

Thin or stretched shape

Activity is spread across prices without one broad central bulge.

Shape alone cannot tell you who bought or sold, why they traded, or what happens next. A thin profile does not automatically prove a trend day. Do not assume price must return to the middle.

Five ways to add this to a trading plan

These are examples to study, not entry instructions. Keep your own rules for entering, exiting and limiting a loss.

1. A move outside fails — the edge fade

Look for: price crossing an edge of a roughly D-shaped profile, then closing back inside. Watch next: whether it stays inside and moves toward the gold line. Reconsider if: price holds outside instead. A stretched profile gives less support for an assumption that price will return to the middle.

2. A move outside holds — continuation

Look for: price staying outside an old edge, including when it comes back to test that edge. Watch next: whether activity builds outside. Reconsider if: price closes back inside the old area. This does not promise a move to the opposite edge.

3. Use the gold line as a reference

In a middle-heavy profile, compare price with the busiest row. It can be a level to observe or a target reference within your existing plan. It is not a magnet: price does not have to revisit it.

4. Watch the day develop — Session mode

Let some candles build before reading the day’s profile. For example, compare the first hour with later activity. Watch whether price keeps returning to the early area or holds outside it. Early levels can change substantially as more data arrives.

5. Compare with levels you already use — confluence

Mark prior highs, lows or your existing price zones first. Then see whether a profile edge falls nearby. When levels overlap, traders call it confluence. It gives you a place to examine, not proof that a trade will work.

Choose the time window and level of detail

Rolling uses the latest candles and moves forward with the chart. Session starts again at the symbol’s daily trading boundary. It is for intraday charts; it is not a custom London or New York session selector.

Settings use the same names as the indicator.

Mode
Default: Rolling
Recent candles, or the current trading day (Session).
Rolling lookback (bars)
Default: 200
How many candles to include: 20–500. A bar means one chart candle.
Rows (bins)
Default: 60
How finely prices are divided: 10–120. More rows show finer detail; fewer make broader zones.
Value Area %
Default: 70
How much estimated volume to include: 50–90%. A larger number widens the area; it does not make the edges stronger.
Profile side
Default: Right
Put the rows on the right, or over the start of the window (Left).
Profile width (bars)
Default: 60
Display width: 10–150. This changes appearance, not the volume calculation.
Show POC / VAH / VAL lines
Default: On
Show or hide the gold line and two edges.
Show price tags
Default: On
Show or hide the prices beside those levels.

Optional starting points

These are examples to adjust, not tested settings for better results.

  • Short intraday charts: 1–5 minute candles, Session, 80–120 rows for finer divisions.
  • A day’s overview: 15 minute candles, Session, 60 rows. Compare with Rolling 200 on a 1 hour chart for a longer view.
  • A longer view: 4 hour or daily candles, Rolling 200–500, 40–60 rows for broader zones.

If the rows look too fragmented, try fewer rows. Start with standard candles: changing the chart type or timeframe can change the estimate.

A simple routine for your next chart
  1. Look at a longer timeframe to see the wider price range.
  2. Choose Rolling or Session. If using Session, let some activity build.
  3. Locate price: inside, above, or below the blue area.
  4. At an edge, watch whether price holds outside or returns inside.
  5. Compare that behaviour with a setup your own strategy already uses.
  6. Apply your entry, exit and risk rules. No matching setup means no action is needed.
How the estimates work, and why lines move

VP Lite spreads each candle’s volume across its high-to-low price range, then adds it into rows. It estimates activity by price; it does not read every individual trade.

The gold line marks the middle of the row with the most estimated volume. The blue area grows around that row until it contains at least your selected percentage. Because whole rows are included, it can hold more than 70% at the default setting.

The profile can move. It is rebuilt as the live candle changes, new candles arrive, or the selected range changes. Earlier profiles and their lines are not saved automatically. Mark a reference level yourself if you want to track it.

Session has a limit: the volume calculation uses at most the latest 2,000 candles from the current trading day. The day follows the symbol’s daily boundary, which may differ from your local midnight.

Volume depends on your chart’s data. Forex and some CFDs may supply tick activity (price updates), rather than units traded. More rows cannot turn that data into exact trade-by-trade volume.

VP Lite maps where activity was concentrated — never what price will do next. It does not separate buying from selling, preserve old profiles, or send alerts.

  • Value Envelope

    Follow the estimated value band over time and study acceptance and re-entry events.

  • ICT Levels Desk

    Compare volume-derived areas with session and previous-period price references.

Built by ProEA Lab. Compare all free indicators.

05 · THE LOG

Every change, dated

What shipped and what changed in Volume Profile Lite, newest first. Ask for the next change below; what gets built lands here with a date, and on the lab queue on the front page.

  1. Volume Profile Lite Shipped

    Shows busy price areas using estimated trading volume.

BUILT WITH TRADERS

Something to change in Volume Profile Lite?

A small friction or a big idea. Tell us what would make this tool work better for you.

What would help?

Describe your rules and what you want the tool to help you see.

Add chart details Optional

For a question about your idea or an update if it ships.

Guides & new tools Optional

Add an email above to choose this.

Show us on your chart Optional

Crop out any account details you don't want to share.

No account needed. Sent privately to the lab.
  1. We read it
  2. We choose what to build
  3. Shipped changes get a date

Requests for a new tool go on the same queue: see what is asked for and what is being built.

Keep one thing in mind.

These are estimated zones, and their positions can change. Use them alongside your own trading and risk rules. This is a learning tool, not financial advice.

© 2026 ProEA · All rights reservedBuilt for MT5 · Not affiliated with MetaQuotes