in my opinion my chart is correct. This is just a volume profile that shows the POC and the Volume value area.
So Sierra charts shows this description:
My chart shows the Volume Point of Control and the Volume Value area. In this article they describe how it is calculated which I did precisely.
Please show me some charts that show significantly different values. It is just not possible imo but I like to see them. Ofcourse my profile is based on the Amibroker function PriceVolDistribution but I assume that is correct. Visually it appears to be correct.
here is my updated code:
//https://pypi.org/project/MarketProfile/
//https://github.com/bfolkens/py-market-profile/blob/master/examples/example.ipynb
//https://www.multicharts.com/trading-software/index.php/Volume_Profile
//https://www.sierrachart.com/index.php?page=doc/StudiesReference/TimePriceOpportunityCharts.html#Calculations
//http://onlinelibrary.wiley.com/doi/10.1002/9781118659724.app1/pdf
GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );
volumeValueArea = Param( "Volume Value area (%)", 0.7, 0, 1, 0.01 );
showVolume = ParamToggle( "Show Volume", "No|Yes", 1 );
showMain = ParamToggle( "Show main levels VAH, VAL and VPOC", "No|Yes", 0 );
showHL = ParamToggle( "Show Main High/Low boundaries", "No|Yes", 0 );
showShifted = ParamToggle( "Show shifted levels VAHn1, VALn1, VPOCn1, etc", "No|Yes", 1 );
showShiftedHL = ParamToggle( "Show shifted High/Low HODn1, LODn1, etc", "No|Yes", 0 );
giveVolumeAreaColor = ParamToggle( "Give Volume Value Area Other color", "No|Yes", 1 );
sep = ParamList( "Separator Interval", "Day|Week|Month|Year", 0 );
fontType = ParamList( "GFX Font Type", "Tahoma|Arial|Verdana|Courier New|Times New Roman|Open Sans|Segoe UI|DejaVu Sans", 3 );
fontSize = Param( "GFX Font Size", 9, 1, 20, 1 );
lineWidth1 = Param( "GFX Value Area Line Width", 1, 1, 10, 1 );
lineWidth2 = Param( "GFX Volume Line Width", 5, 1, 30, 1 );
showLabel = ParamToggle( "Show Labels on Levels", "No|Yes", 1 );
shft = Param( "Shift Volume Value Area (Interval)", 1, 1, 10, 1 );
volfact = Param( "Volume Factor", 0.8, 0.5, 1, 0.01 );
bins = Param( "Bins", 50, 10, 500, 1 );
switch( sep )
{
case "Day":
dn = Day();
dn1 = inDaily;
break;
case "Week":
dw = DayOfWeek();
newWeek = dw < Ref( dw, -1 );;
newYear = Year() != Ref( Year(), -1 );
dn = weekNumber = Sum( newWeek, BarsSince( newYear ) + 1 );
dn1 = inWeekly;
break;
case "Month":
dn = Month();
dn1 = inMonthly;
break;
case "Year":
dn = Year();
dn1 = inYearly;
break;
}
separator = dn != Ref( dn, -1 );
bi = BarIndex();
fvb = Max( 0, FirstVisibleValue( bi ) );
lvb = Max( 0, LastVisibleValue( bi ) );
dif = 0;
mxih = mxil = 0;
VPOC = VAH = VAL = Null;
HOD = TimeFrameGetPrice( "H", dn1 );
LOD = TimeFrameGetPrice( "L", dn1 );
for( i = 0; i <= 2 + shft; i++ )
{
VarSet( "lbx" + i, ValueWhen( separator, bi, i ) );
}
function drawHLine( x1, x2, y , clr, zorder, flag, txt, lw )
{
GfxSetZOrder( zorder );
GfxSelectPen( clr, lw, 0 );
GfxMoveTo( x1, y );
GfxLineTo( x2, y );
if( flag )
{
GfxSetZOrder( 4 );
GfxSetTextAlign( 2 | 8 );
GfxSetBkColor( ColorRGB( 0, 0, 0 ) );
GfxSetTextColor( clr );
GfxSelectFont( fontType, fontSize );
GfxTextOut( txt + Prec( y, 2 ), x2 , y );
}
}
function calculateVolumeArea( mxVolBinIdx, totVol, bins, mx )
{
/*
Calculates Volume Value area.
I do just 1 step at a time. For the meaning of what I mean with steps see:
http://onlinelibrary.wiley.com/doi/10.1002/9781118659724.app1/pdf
see also:
https://www.sierrachart.com/index.php?page=doc/StudiesReference/TimePriceOpportunityCharts.html#Calculations
Starting at the Point of Control we move outward one row up and one row down. At the first set of rows we see
which one has the greatest Volume. The one with the greatest Volume is used as part of the Value Area. The one
with the lesser Volume, is used in our next comparison. We then continue to the next set of rows which consists
of the row beyond the one with the greatest Volume previously made part of the Value Area and the row we compared
to previously which was not included in the Value Area. We then do the same comparison seeing which one has the
greatest Volume and we use that row as part of the Value Area.
*/
mxih = mxil = mxVolBinIdx;
tvol = mx[mxVolBinIdx][1];
for( j = 0; j < bins; j++ )
{
if( mxih + 1 < bins AND mxil - 1 >= 0 )
{
relvolumeh = mx[mxih + 1][1];
relvolumel = mx[mxil - 1][1];
if( relvolumeh > relvolumel )
{
mxih = mxih + 1;
tvol = tvol + relvolumeh;
}
else
if( relvolumeh < relvolumel )
{
mxil = mxil - 1;
tvol = tvol + relvolumel;
}
else
if( relvolumeh == relvolumel )
{
mxih = mxih + 1;
mxil = mxil - 1;
tvol = tvol + relvolumeh + relvolumel;
}
}
else
if( mxih + 1 >= bins AND mxil - 1 >= 0 )
{
relvolumel = mx[mxil - 1][1];
mxil = mxil - 1;
tvol = tvol + relvolumel;
}
else
if( mxih + 1 < bins AND mxil - 1 < 0 )
{
relvolumeh = mx[mxih + 1][1];
mxih = mxih + 1;
tvol = tvol + relvolumeh;
}
//_TRACE( "tvol: " + tvol + " totVol: " + totVol + " ratio: " + ( tvol / totVol ) );
if( ( tvol / totVol ) >= volumeValueArea ) break;
}
}
function calculateVolumeValueArea( fb, lb )
{
cnt = 0; // count number of visible days
for( i = lb; i > fb; i-- )
{
idx0 = lbx0[i];
idx1 = lbx1[i];
idx2 = lbx2[i];
if( IsEmpty( idx1 ) ) break;
mx = PriceVolDistribution( H, L, V, bins, False, idx2, idx1 - 1 );
dx = idx1 - idx2;
mxVolBinIdx = 0;
mxVol = 0;
totVol = 0;
for( j = 0; j < bins; j++ )
{
price = mx[j][0]; // price level
relvolume = mx[j][1]; // relative volume 0..1
relbar = relvolume * dx;
if( showVolume )
{
drawHLine( idx2, idx2 + ( relbar * volfact ), price, ColorRGB( 60, 0, 0 ), -4, 0, "", lineWidth2 );
}
// calculation of POC
if( relvolume >= mxVol )
{
// check case if mxVol is the same as prior mxVol
if( relvolume == mxVol )
{
// only shift to this top if it is closer to the center of the distribution
if( abs( j - ( bins / 2 ) ) < abs( mxVolBinIdx - ( bins / 2 ) ) )
{
mxVol = relvolume;
mxVolBinIdx = j;
}
}
else
{
mxVol = relvolume;
mxVolBinIdx = j;
}
}
totVol = totVol + relvolume;
}
priceVPOC = mx[mxVolBinIdx][0];
calculateVolumeArea( mxVolBinIdx, totVol, bins, mx );
priceh = mx[mxih][0];
pricel = mx[mxil][0];
x1 = idx2;
x2 = Min( idx1, BarCount - 1 );
line = Ref( LineArray( x1, priceh, x2, priceh ), 1 );
VAH = IIf( !IsEmpty( line ), line, VAH );
line = Ref( LineArray( x1, pricel, x2, pricel ), 1 );
VAL = IIf( !IsEmpty( line ), line, VAL );
line = Ref( LineArray( x1, priceVPOC, x2, priceVPOC ), 1 );
VPOC = IIf( !IsEmpty( line ), line, VPOC );
// give volume value area other color
if( giveVolumeAreaColor )
{
for( j = 0; j < bins; j++ )
{
price = mx[j][0]; // price level
relvolume = mx[j][1]; // relative volume 0..1
relbar = relvolume * dx;
if( showVolume AND price >= pricel AND price <= priceh )
{
drawHLine( idx2, idx2 + ( relbar * volfact ), price, ColorRGB( 0, 40, 0 ), -4, 0, "", lineWidth2 + 0 );
}
}
}
if( showHL )
{
ph = HOD[x2 - 1];
drawHLine( x1, x2, ph, ColorRGB( 0, 250, 0 ), -3, showLabel, "HOD ", lineWidth1 );
pl = LOD[x2 - 1];
drawHLine( x1, x2, pl, ColorRGB( 250, 0, 0 ), -3, showLabel, "LOD ", lineWidth1 );
}
if( showMain )
{
drawHLine( x1, x2, priceh , ColorRGB( 0, 250, 250 ), -3, showLabel, "" + "VAH ", lineWidth1 );
drawHLine( x1, x2, pricel , ColorRGB( 0, 250, 250 ), -3, showLabel, "" + "VAL ", lineWidth1 );
drawHLine( x1, x2, priceVPOC , ColorRGB( 204, 77, 153 ), -3, showLabel, "" + "VPOC ", lineWidth1 );
}
if( cnt == 0 )
{
x1 = x2;
x2 = Min( lvb, BarCount - 1 );
if( x2 == 0 ) x2 = BarCount - 1;
if( showHL )
{
ph = HOD[x2];
drawHLine( x1, x2, ph, ColorRGB( 0, 250, 0 ), -3, showLabel, "HOD ", lineWidth1 );
pl = LOD[x2];
drawHLine( x1, x2, pl, ColorRGB( 250, 0, 0 ), -3, showLabel, "LOD ", lineWidth1 );
}
mx = PriceVolDistribution( H, L, V, bins, False, x1, x2 );
dx = x2 - x1;
mxVolBinIdx = 0;
mxVol = 0;
totVol = 0;
for( j = 0; j < bins; j++ )
{
price = mx[j][0]; // price level
relvolume = mx[j][1]; // relative volume 0..1
relbar = relvolume * dx;
if( showVolume ) drawHLine( x1, x1 + ( relbar * volfact ), price, ColorRGB( 80, 80, 80 ), -4, 0, "", lineWidth2 );
if( relvolume > mxVol )
{
mxVol = relvolume;
mxVolBinIdx = j;
}
totVol = totVol + relvolume;
}
priceVPOC = mx[mxVolBinIdx][0];
calculateVolumeArea( mxVolBinIdx, totVol, bins, mx );
priceh = mx[mxih][0];
pricel = mx[mxil][0];
line = LineArray( x1, priceh, x2, priceh );
VAH = IIf( !IsEmpty( line ), line, VAH );
line = LineArray( x1, pricel, x2, pricel );
VAL = IIf( !IsEmpty( line ), line, VAL );
line = LineArray( x1, priceVPOC, x2, priceVPOC );
VPOC = IIf( !IsEmpty( line ), line, VPOC );
if( showMain )
{
drawHLine( x1, x2, priceh , ColorRGB( 0, 250, 250 ), -3, showLabel, "" + "VAH ", lineWidth1 );
drawHLine( x1, x2, pricel , ColorRGB( 0, 250, 250 ), -3, showLabel, "" + "VAL ", lineWidth1 );
drawHLine( x1, x2, priceVPOC , ColorRGB( 204, 77, 153 ), -3, showLabel, "" + "VPOC ", lineWidth1 );
}
if( giveVolumeAreaColor AND showVolume )
{
for( j = 0; j < bins; j++ )
{
price = mx[j][0]; // price level
relvolume = mx[j][1]; // relative volume 0..1
relbar = relvolume * dx;
if( showVolume AND price >= pricel AND price <= priceh )
{
drawHLine( x1, x1 + ( relbar * volfact ), price, ColorRGB( 0, 40, 40 ), -4, 0, "", lineWidth2 );
}
}
}
}
i = lbx1[ idx1 ];
cnt++;
}
}
function shiftVolumeValueArea( fb, lb, shft )
{
for( i = lb; i > fb; i-- )
{
// destination index
if( lbx0[i] == lbx1[i] )
{
x1 = lbx1[i];
x2 = Min( lb, BarCount - 1 );
}
else
if( lbx1[i] < lbx0[i] )
{
x1 = lbx1[i];
x2 = Min( lb, lbx0[i] );
}
for( s = 1; s <= shft; s++ )
{
xx0 = VarGet( "lbx" + ( s - 1 ) );
xx1 = VarGet( "lbx" + ( s + 0 ) );
xx2 = VarGet( "lbx" + ( s + 1 ) );
if( xx0[i] == xx1[i] )
{
// source index
x1s = xx2[i];
x2s = xx1[i];
if( !IsEmpty( x1s ) )
{
if( showShifted )
{
drawHLine( x1, x2, VPOC[x1s] , ColorRGB( 250 / s, 0, 250 / s ), -3, showLabel, "VPOCn" + s + " ", lineWidth1 );
drawHLine( x1, x2, VAH[x1s] , ColorRGB( 250 / s, 250 / s, 0 ), -3, showLabel, "VAHn" + s + " ", lineWidth1 );
drawHLine( x1, x2, VAL[x1s] , ColorRGB( 250 / s, 250 / s, 0 ), -3, showLabel, "VALn" + s + " ", lineWidth1 );
}
if( showShiftedHL )
{
drawHLine( x1, x2, HOD[x1s], ColorRGB( 0, 250, 0 ), -3, showLabel, "HODn" + s + " ", lineWidth1 );
drawHLine( x1, x2, LOD[x1s], ColorRGB( 250, 0, 0 ), -3, showLabel, "LODn" + s + " ", lineWidth1 );
}
// save shifted value area in dynamic arrays
if( !IsEmpty( VPOC[x1s] ) )
{
line = Ref( LineArray( x1, VPOC[x1s], x2, VPOC[x1s] ), 1 );
VarSet( "VPOCn" + s, IIf( line, line, VarGet( "VPOCn" + s ) ) );
}
if( !IsEmpty( VAH[x1s] ) )
{
line = Ref( LineArray( x1, VAH[x1s], x2, VAH[x1s] ), 1 );
VarSet( "VAHn" + s, IIf( line, line, VarGet( "VAHn" + s ) ) );
}
if( !IsEmpty( VAL[ x1s ] ) )
{
line = Ref( LineArray( x1, VAL[x1s], x2, VAL[x1s] ), 1 );
VarSet( "VALn" + s, IIf( line, line, VarGet( "VALn" + s ) ) );
}
}
}
else
if( xx1[i] < xx0[i] )
{
// source index
x1s = xx2[i];
x2s = xx1[i];
if( !IsEmpty( x1s ) )
{
if( showShifted )
{
drawHLine( x1, x2, VPOC[x1s] , ColorRGB( 250 / s, 0, 250 / s ), -3, showLabel, "VPOCn" + s + " ", lineWidth1 );
drawHLine( x1, x2, VAH[x1s] , ColorRGB( 250 / s, 250 / s, 0 ), -3, showLabel, "VAHn" + s + " ", lineWidth1 );
drawHLine( x1, x2, VAL[x1s] , ColorRGB( 250 / s, 250 / s, 0 ), -3, showLabel, "VALn" + s + " ", lineWidth1 );
}
if( showShiftedHL )
{
drawHLine( x1, x2, HOD[x1s], ColorRGB( 0, 250, 0 ), -3, showLabel, "HODn" + s + " ", lineWidth1 );
drawHLine( x1, x2, LOD[x1s], ColorRGB( 250, 0, 0 ), -3, showLabel, "LODn" + s + " ", lineWidth1 );
}
// save shifted value area in dynamic arrays
if( !IsEmpty( VPOC[x1s] ) )
{
line = Ref( LineArray( x1, VPOC[x1s], x2, VPOC[x1s] ), 1 );
VarSet( "VPOCn" + s, IIf( line, line, VarGet( "VPOCn" + s ) ) );
}
if( !IsEmpty( VAH[x1s] ) )
{
line = Ref( LineArray( x1, VAH[x1s], x2, VAH[x1s] ), 1 );
VarSet( "VAHn" + s, IIf( line, line, VarGet( "VAHn" + s ) ) );
}
if( !IsEmpty( VAL[x1s] ) )
{
line = Ref( LineArray( x1, VAL[x1s], x2, VAL[x1s] ), 1 );
VarSet( "VALn" + s, IIf( line, line, VarGet( "VALn" + s ) ) );
}
}
}
}
i = lbx1[i];
}
}
if( Status( "Action" ) == actionIndicator )
{
Title = Name() + " >>|<< Volume area (%): " + EncodeColor( colorYellow ) + volumeValueArea * 100 + EncodeColor( colorWhite ) + " >>|<< Separator Timeframe: " + sep;
SetChartOptions( 0, chartShowDates );
SetChartBkColor( ColorRGB( 0, 0, 0 ) );
Plot( Close, "Price", colorDefault, styleCandle, Null, Null, 0, 0, 1 );
Plot( separator, "", colorDarkBlue, styleHistogram | styleOwnScale | styleNoLabel | styleNoRescale, 0, 1, 0, -2, 5 );
br = Flip( fvb == bi, lvb == bi );
periods = IIf( br, separator, 0 );
periods = LastValue( Cum( periods ) );
dif = int( ( lvb - fvb ) / periods );
fvb = Max( 0, fvb - 2 * dif );
calculateVolumeValueArea( fvb, lvb );
shiftVolumeValueArea( fvb, lvb, shft );
}