Why does VAH and VAL values ( monthly )keep differing?

Continuing the discussion from How to get the most traded area value of previous day intraday data:

Dear @empottasch,

First of all thanks a ton for creating such a wonderful afl. I just noticed the actual plot of VAH and VAL ( monthly) keep differing on 5 mins to lower time frames Vs 10 mins to upper time frames.
I suppose values of the monthly and weekly levels should remain same irrespective of the time frame selected with in a day.

I am confused as it was not addressed or not mentioned in the previous thread.. Below is an example image for the same highlighting the diff of values. Left side image is on 5mins and and the right one is on 15 mins. Noticed the same for many stocks but for others the levels remains same.

Value Area High & Low

Request you to please have a look into this.

Also, the Weekly and Yearly levels doesn't plot at all..

Plots as of now are happening either on Daily & monthly using Separator. Can't we get an option to plot all time frames levels at once? I mean to say Monthly | Weekly | Daily at the same time..

hi, it is a long time ago I posted this. The levels you say are changing are the VAH and VAL of the current segment. They change because new data is still coming in. I think in the parameter window these levels are set "off" per default. VAHn1 and VALn1 are the levels of the previous segment. They will be stationary.

So VAHn1 is the top level of the previous segment displayed in the current segment. VAH is the top level of the current segment. So in the last segment that is still getting new data this level will change as long as new data is coming in. Once the segment is completed this levels will stay stationary and will be drawn in the next segment as VAHn1.

I think the guy asking the question back then wanted it like this. But the levels VAHn1 and VALn1 are the important levels in this code. It is a bit confusing but I think VAHn1 in my code is what usually is called the VAH level.

Dear @empottasch,

I absolutely understood the plotting very clearly since I have gone through the whole thread couple of times.. I tried to highlight the issued that just got unnoticed. My concern is VAHN1 and VALN1 values (SHIFTED Levels) that r changing but instead they are supposed to be static for monthly selection. Please take a look at the image i have shared. I shall re-post the same switching off the other levels.

Screenshot 2020-12-11 004142

If you notice, both are monthly shifted levels which are supposed to be stationary on any given time. But the left side shows a different level than the levels on right side image. Both are of same stock and are on 5 and 15 mins respectively.

Interestingly, this is happening for few stocks and not all the stocks.. Which level is supposed to be a true level is my concern. It was not highlighted on the previous thread so thought of bringing to your notice.

Also, the Weekly and Yearly levels doesn't plot at all..

Plots as of now are happening either on Daily & monthly using Separator. Can't we get an option to plot all time frames levels at once? I mean to say Monthly | Weekly | Daily at the same time..

will have a look tomorrow. There are also others who made similar code, maybe you can have a look if their code suits you better. But I will have a look at your question tomorrow

ok I see what you are saying, i did not read your question carefully yesterday. I actually only looked at it in the 5min timeframe using "day" as separator. Then I noticed small differences when switching from 5min to for instance 15min but attributed this to the lower resolution. The differences you are showing are enormous.

Also this code becomes really slow if you use as separator for instance monthly and then use 5min data. This is too slow to work with.

So I see the differences you are talking about and will have to dig into the code again. Probably there are much smarter and faster ways to do this. I will have a look at it again and see if I can come up with a slimmed down version with just the VAH, VAL and POC

1 Like

@empottasch
Thanks a lot Ed..

I will certainly wait for your updated version of the code. I have not seen any afl for Value area other than yours so far and that makes this afl really special and handy for many if I am not wrong. Since this afl is one of its kind, I am requesting you to make it more efficient than what it is now.. I would really love to have an updated version with correct plotting.
Just to re-iterate the pointers I have noticed..

  1. Difference between VAHn1 and VAHLn1 values on a monthly separator when used on 15 and above Vs 5 mins and below time frames.

  2. Weekly & Yearly separators are not getting plotted . The issue with weekly could be because of no weekly() available in ami..

  3. Possibility to plot all 3 separators ( daily , Weekly and Monthly) instead of either of them as it is coded in the afl.

  4. When we run explorer to identify stocks that are crossing VAHn1 and VALn1 of Monthly separator on a daily time frame, we are forced to select dates from starting of the month and to End of the month or current date. This would fetch all the crossovers happened on all days till current date. Instead, would there be a possibility to fetch stocks that crossed VAHn1 annd VALn1 of Monthly on current date ?

I am sorry for digging into a very old thread of yours but as I have mentioned in point 4, results are highly interesting & promising to watch out for intra-day trades. I am sure you would love them too. Kindly check the above..

Thanks a ton in advance.

hi, i did a quick fix. I now use a fixed number of bins. Seems to do a reasonable job. Is also much faster and the levels appear to stay the same when you change the timeframe. However, if you change the number of bins the exact location of the levels may differ a bit. I added the number of bins as a parameter (all the way at the bottom in the parameter window).

Also now we can use Python so you can also let Python calculate it for you

//https://pypi.org/project/MarketProfile/
//https://github.com/bfolkens/py-market-profile/blob/master/examples/example.ipynb

//TickSize = 0.25; // <<<<==== adjust yourself or set the ticksize in the Information window
if( TickSize == 0 )
{
    TickSize = 0.01;
    PopupWindow( "Ticksize is not set. The TickSize should be set in the code or in the Information Window", "TickSize Problem", 15, -1, -1, -1, -1, True );
}

GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );

volumeValueArea = Param( "Volume Value area (%)", 0.7, 0, 1, 0.01 );
showVolume = ParamToggle( "Show Volume", "No|Yes", 1 );
showHL = ParamToggle( "Show High/Low boundaries", "No|Yes", 0 );
showMain = ParamToggle( "Show main levels VAH, VAL and VPOC", "No|Yes", 0 );
showShifted = ParamToggle( "Show shifted levels VAHn1, VALn1, VPOCn1, etc", "No|Yes", 1 );
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 Main Volume area Line Width", 2, 1, 10, 1 );
lineWidth2 = Param( "GFX Volume Line Width", 10, 1, 30, 1 );
showLabel = ParamToggle( "Show Labels on Levels", "No|Yes", 1 );
shft = Param( "Shift Volume Value Area (Interval)", 1, 1, 10, 1 );
bins = Param( "Bins", 20, 10, 100, 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 );

yesterdayClosePrice = TimeFrameGetPrice( "C", inDaily, -1 ); // yesterdays close
todayOpenPrice = ValueWhen( separator, O ); // todays open
tn = TimeNum();

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
    */
    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;
                }

        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;

        //bins = int( ( HOD[idx1 - 1] - LOD[idx1 - 1] ) / TickSize );
        //_TRACE("bins: " + bins );
        //bins = 20;
        //if( bins == 0 ) 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 AND Status( "Action" ) == actionIndicator )
            {
                drawHLine( idx2, idx2 + relbar, price, ColorRGB( 60, 0, 0 ), -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];
        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 AND Status( "Action" ) == actionIndicator )
        {
            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, price, ColorRGB( 0, 40, 0 ), -4, 0, "", lineWidth2 + 0 );
                }
            }
        }

        if( showHL AND Status( "Action" ) == actionIndicator )
        {
            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 AND Status( "Action" ) == actionIndicator )
        {
            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 AND Status( "Action" ) == actionIndicator )
            {
                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 );
            }

            //bins = int( ( HOD[x2] - LOD[x2] ) / TickSize );
            //bins = 20;
            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 AND Status( "Action" ) == actionIndicator ) drawHLine( x1, x1 + relbar, 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 AND Status( "Action" ) == actionIndicator )
            {
                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 AND Status( "Action" ) == actionIndicator )
            {
                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, 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 AND Status( "Action" ) == actionIndicator )
                    {
                        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 );

                        //drawHLine( x1, x2, ( VAL[x1s] + VAH[x1s] ) / 2 , ColorRGB( 250 / s, 250 / s, 0 ), -3, showLabel, "VALn" + s + " ", 3 );
                    }

                    // 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 AND Status( "Action" ) == actionIndicator )
                        {
                            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 );

                            //drawHLine( x1, x2, ( VAL[x1s] + VAH[x1s] ) / 2 , ColorRGB( 250 / s, 250 / s, 0 ), -3, showLabel, "VALn" + s + " ", 3 );
                        }

                        // 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 );
}

4 Likes

another issue I probably did not check is that the value area I calculate using this manual:
http://onlinelibrary.wiley.com/doi/10.1002/9781118659724.app1/pdf

so it might happen that there is more than 1 top in volume. Then there are rules which top to pick. Probably I did not implement that. I might go over it again if I have time. But obviously you can also try that yourself. You can calculate the top volume faster using the MxSort functions and then not draw the volume using Gfx but use the PlotVAPOverlayA function. Then you need to check if the top is unique else find the top closest to the middle of the distribution.

Then calculate the value area. I used my own code for that but there might be a smarter way, not sure. Or maybe you can find code somewhere on the net that does this. But I will revisit it later

Dear @empottasch,

Thanks for the quick fix. The levels are now static. Having that said, changing bin param by each numeric alters the levels heavily making the entire system very unreliable . What is the ideal bin param value that can calculate accurate or close to accurate Value areas?.

I have been comparing the same with another platform and the results differ heavily. Is there a possibility for fixing this?

Sure Thanks for reverting.

just hard to believe there is a big difference. I check it with the PlotVAPOverlayA function and if you use the same number of bins then the volume profile is exactly the same. So the calculation of my POC is correct. Then it depends on how you calculate the value area. That depends on how you define this area. I used the article from the link I gave but maybe others calculate it differently. If You see a substantial difference in the POC that would be strange since like I said it is exactly as in the PlotVAPOverlayA function.

I did add some code that checks for multiple tops with exactly the same volume. I checked visually but did't see it ever happen. So I will post this code as soon as I checked my calculation of the value area. Visually my calculation of the value area looks correct so it can't be off that much

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 );
}
3 Likes

Dear @empottasch,

Let me revert with some comparative pictures soon. Thanks.

Have tried this code, as the current one i am running too has the same problem with moving previously weeks levels i only noticed after ive looked back at a few screens i've saved.
though this one plots daily and monthly fine, but doesn't plot any other week when using weekly seperator then the most recent? i see Hems brought this up to, has the issue been worked out at all?

Cheers Jon

it works for me but it might be you do not have enough data. Maybe that is the cause.

so if you replace the below. Code. If that works than it is probably you only have a few weeks or months or data. Amibroker does not have a Week() function. Not sure if there is a better solution. Probably on the forum some people made something better

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 );

with:

switch( sep )
{
case "Day":
    dn = Day();
    dn1 = inDaily;
    sep1 = dn != Ref( dn, -1 );
    break;

case "Week":
    dw = DayOfWeek();
    sep1 = 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;
    sep1 = dn != Ref( dn, -1 );
    break;

case "Year":
    dn = Year();
    dn1 = inYearly;
    sep1 = dn != Ref( dn, -1 );
    break;
}

separator = sep1;

to explain further, the separator between weeks is calculated differently because there is no Week() function. I calculated the weekNumber and used code I found here:

So maybe there is another solution. Using my code in the previous email will show if this is the cause. I bet you do not have a years worth of data so then that might be the cause

Thanks for the quick reply, awesome that seems to work alright,

Oddly though it seems to give wildley different values for last week, though every other week seems close/right..

b
b

I Suppose it just comes down to bin size.

so you use IQFeed data so you then probably have more than 1 years worth of data. Then my original code should work. I am using the Amibroker function PriceVolDistribution. Indeed depending on the number of bins the maximum of the distribution (VPOC) can sometimes change its position quite a bit. So if you use for instance 500 for the number of bins then the position is more stable.

So it is pretty obvious my code finds the correct VPOC, you can see that in the chart. Whether my value area (the 2 yellow lines) are correct depends on how you calculate them. As far as I know I use the same procedure as Sierracharts does but you need to check if the code is correct. I looked at the code back in December and in my opinion it is correct.

This is the chart I get using 500 bin.

xg3

1 Like

What timeframe yours in? looks hourly to me too
i am using iqfeed/dtn with a 6 hour time shift so i can get the full fdax session in 1 date period (makes my daily intraday charts easier)
and start of week is Sunday, i might do a forcebackfill and see if that changes anything ATM my 500 bin is showing this
d500

will have a closer look. The shifted HOD and LOD are not correct in the weekly. Monthly, Daily and Yearly seems to be correct. Maybe we just should remove Weekly :slightly_smiling_face: that seems the easiest way out. I will have a look and respond later maybe tomorrow

1 Like