DocumentationVolume Profile

Volume Profile

A volume profile shows how much volume traded at each price rather than over time - a horizontal histogram that reveals high-volume nodes (acceptance) and low-volume gaps (rejection). Like Market Profile it comes in two pieces: computeVolumeProfileSessions (analytics, profile tier) and the VolumeProfile primitive that draws it.

import { computeVolumeProfileSessions, VolumeProfile } from 'openalgo-charts/profile';
 
const result = computeVolumeProfileSessions(bars, { tickSize: 0.05, session: 'composite' });
chart.addPrimitive(new VolumeProfile(result));

Variants

  • Visible-range - pass the currently visible slice of bars with session: 'composite'. One profile spanning the view.
  • Session - session: 'day' | 'week' | 'month' builds one profile per period, each drawn under its own bars with its own POC and value area. Periods are grouped on the calendar of the timezone option, which defaults to Asia/Kolkata; pass chart.timezone() so the buckets match the axis. See Timezones.

Every session reports a POC (highest-volume price), a Value Area (the band holding valueAreaPercent of volume), and - optionally - a buy/sell split.

Total volume

The default total mode draws one bar per price, anchored at the session’s right edge:

live
Rendering live chart…
Visible-range volume profile: POC (gold), value area highlighted, rows outside it dimmed.
View example code
const chart = lib.createChart(el);
const bars = lib.generateBars(1700000000, 120, 1800);
chart.addSeries('candlestick').setData(bars);

const vp = lib.computeVolumeProfileSessions(bars, { tickSize: 1, session: 'composite' });
chart.addPrimitive(new lib.VolumeProfile(vp));

chart.timeScale.fitContent(bars.length);
return chart;

Buy vs sell, and delta

displayMode: 'buySell' splits each row into buy and sell; 'delta' draws the net, colored by sign. Both use the direction-based split (below).

live
Rendering live chart…
buySell mode: buy (teal) and sell (red) split per price.
View example code
const chart = lib.createChart(el);
const bars = lib.generateBars(1700000000, 120, 1800);
chart.addSeries('candlestick').setData(bars);

const vp = lib.computeVolumeProfileSessions(bars, { tickSize: 1, session: 'composite' });
chart.addPrimitive(new lib.VolumeProfile(vp, { displayMode: 'buySell', width: 100 }));

chart.timeScale.fitContent(bars.length);
return chart;
live
Rendering live chart…
delta mode: net buy-minus-sell per price, colored by sign.
View example code
const chart = lib.createChart(el);
const bars = lib.generateBars(1700000000, 120, 1800);
chart.addSeries('candlestick').setData(bars);

const vp = lib.computeVolumeProfileSessions(bars, { tickSize: 1, session: 'composite' });
chart.addPrimitive(new lib.VolumeProfile(vp, { displayMode: 'delta' }));

chart.timeScale.fitContent(bars.length);
return chart;
⚠️

The buy/sell split is an honest approximation from OHLCV: a bar’s whole volume is counted as buying when it closed up (close >= open) and selling when it closed down. It is not true bid/ask delta - for that you need classified trades, see the Footprint. Set deltaFromBarDirection: false to keep volume un-split.

Session profiles

live
Rendering live chart…
A separate volume profile per calendar day, on the default Asia/Kolkata, each with its own POC and value area.
View example code
const chart = lib.createChart(el);
const bars = lib.generateBars(1700000000, 160, 1800);
chart.addSeries('candlestick').setData(bars);

const vp = lib.computeVolumeProfileSessions(bars, { tickSize: 1, session: 'day' });
chart.addPrimitive(new lib.VolumeProfile(vp, { side: 'right', width: 70 }));

chart.timeScale.fitContent(bars.length);
return chart;

computeVolumeProfileSessions options

computeVolumeProfileSessions(bars, options): VolumeProfileFamilyResult
OptionDefaultDescription
tickSize0.05price bucket size
session'composite''composite' / 'day' / 'week' / 'month'
valueAreaPercent0.7fraction of volume in the value area (0..1)
deltaFromBarDirectiontruesplit volume into buy/sell by bar direction
timezone'Asia/Kolkata'IANA zone the day / week / month buckets resolve on. See Timezones

VolumeProfile primitive options

Constructor new VolumeProfile(result, options) or live profile.setOptions({ ... }).

OptionDefaultDescription
displayModetotaltotal / buySell / delta
siderightanchor bars at the session’s left or right edge
width90maximum bar length, px
barColorslatebar color in total mode
buyColor / sellColorteal / redbuySell and delta colors
opacity0.85bar opacity
showPoc / pocColor / showPocLabeltrue / gold / truePoint of Control line + label
showValueArea / vahColor / valColor / showValueAreaLabelstrueVAH & VAL lines + labels
highlightValueArea / valueAreaFillColor / valueAreaFillOpacitytrueshade the value-area band
valueAreaOpacityDim0.4opacity multiplier for rows outside the value area
labelSiderightwhich edge the POC/VA labels sit on
zOrderbottomdraw behind (bottom) or over (top) the series

Data model

interface VolumeProfileFamilyResult {
  sessions: VolumeProfileSessionResult[];
  options: VolumeProfileFamilyOptions;
}
 
interface VolumeProfileSessionResult {
  startTime: number; endTime: number;      // UTC seconds
  levels: { price: number; volume: number; buyVolume: number; sellVolume: number; delta: number }[]; // high -> low
  poc: number; vah: number; val: number;
  totalVolume: number; buyVolume: number; sellVolume: number; delta: number;
}

Troubleshooting

SymptomCauseFix
Nothing rendersNo price series (time axis has no bars)Add a series before the profile
Bars cover the candlesProfile anchored over the price actionLower width, set side: 'left', or zOrder: 'top' with lower opacity
buySell / delta look emptydeltaFromBarDirection: falseEnable it (default) so volume is split
One profile for many dayssession: 'composite'Use session: 'day' (or week / month)
Value area looks offvalueAreaPercent passed as a percentUse a fraction (0.7), not 70