How to get the most traded area value of previous day intraday data

TickSize = 0.25; // <<<<==== adjust yourself

oh yeah I commented that out because I set this in the Information Window, see "Contract Specification"

contr

there were still small errors in there. But also made a cool update where you can select a separator other then Day().

so up until now you could only (using my code) make a MP using intraday data. But will post code 2morrow where you can chose between Day/Week/Month and Year. And yourself can easily add Hour/15min etc all the way down to ticks.

I just have little issue left with Week since there is no Week() function in Amibroker. Thought I solved it but just found 1 more imperfection.

what I use now

if( sep == "Week" )
{
    dw = DayOfWeek();
    newWeek = dw < Ref( dw, -1 );;
    newYear = Year() != Ref( Year(), -1 );
    dn = weekNumber = Sum( newWeek, BarsSince( newYear ) + 1 );
    dn1 = inWeekly;
}

so weekNumber returns same as Day(), Month() etc functions. But not perfect yet. Why again is there no Week() function in Amibroker?

no issue just seems at cross of years in Weekly. And is due to H/L inWeekly. Not a big issue, will solve that tomorrow

so most of it works pretty good. So with code below try using 15min data and then set in the Parameter window “Separator Interval” to week, month or year

oh yeah, Ticksize is commented out in code. Set that correctly, else it will not work

cheers :beer:

// in reply to: http://forum.amibroker.com/t/how-to-get-the-most-traded-area-value-of-previous-day-intraday-data/2472
// code assumes your working with intraday data (e.g. 5min data)
// Value area calculation: http://onlinelibrary.wiley.com/doi/10.1002/9781118659724.app1/pdf
// !!!! TickSize is hardcoded in code. You need to adjust for, according to contract specifics.
// E.M.Pottasch (10/2017)

// possible strategies (did not test any myself)
// - 80% rule: https://marketdelta.com/wp-content/uploads/Strategy-80PercentRule.pdf
// -

//TickSize = 0.25; // <<<<==== adjust yourself

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 );
showVal1 = ParamToggle( "Show Value area inside same day", "No|Yes", 0 );
showVal0 = ParamToggle( "Show Value area projected into the next day", "No|Yes", 1 );
showPOC = ParamToggle( "Show Point Of Control (POC)", "No|Yes", 1 );
vai = Param( "Volume Value area Color Intensity", 10, 0, 254, 1 );
sep = ParamList( "Separator Interval", "Day|Week|Month|Year", 0 );

if( sep == "Day" )
{
    dn = Day();
    dn1 = inDaily;
}

if( sep == "Week" )
{
    dw = DayOfWeek();
    newWeek = dw < Ref( dw, -1 );;
    newYear = Year() != Ref( Year(), -1 );
    dn = weekNumber = Sum( newWeek, BarsSince( newYear ) + 1 );
    dn1 = inWeekly;
}

if( sep == "Month" )
{
    dn = Month();
    dn1 = inMonthly;
}

if( sep == "Year" )
{
    dn = Year();
    dn1 = inYearly;
}

"dn: " + dn;

separator = dn != Ref( dn, -1 );

bi = BarIndex();
fvb = Max( 0, FirstVisibleValue( bi ) );
lvb = Max( 0, LastVisibleValue( bi ) );

valueHigh1 = valueLow1 = Null;
valueHigh0 = valueLow0 = Null;
pointOfControl = Null;
highOfDay = TimeFrameGetPrice( "H", dn1 );
lowOfDay = TimeFrameGetPrice( "L", dn1 );

lbx2 = ValueWhen( separator, bi, 2 );
lbx1 = ValueWhen( separator, bi, 1 );
lbx0 = ValueWhen( separator, bi, 0 );

function calculateVolumeValueArea( fb, lb )
{
    GfxSetZOrder( -4 );
    GfxSetCoordsMode( 1 );

    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( ( highOfDay[ idx1 - 1 ] - lowOfDay[ idx1 - 1 ] ) / TickSize );
        mx = PriceVolDistribution( H, L, V, bins, False, idx2, idx1 - 1 );
        //bins = MxGetSize( mx, 0 );
 
        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 )
            {
                GfxSelectPen( ColorRGB( 80, 0, 0 ), 1, 0 );
                GfxMoveTo( idx2, price );
                GfxLineTo( idx2 + relbar, price );
            }

            if( relvolume > mxVol )
            {
                mxVol = relvolume;
                mxVolBinIdx = j;
            }

            totVol = totVol + relvolume;
        }

        // calculate Volume Value area
        mxih = mxil = mxVolBinIdx;
        tvol = mx[ mxVolBinIdx ][ 1 ];
        pricePOC = mx[ mxVolBinIdx ][ 0 ];

        for( j = 0; j < bins; j++ )
        {
            if( mxih + 2 < bins AND mxil - 2 >= 0 )
            {
                relvolumeh = mx[ mxih + 1 ][ 1 ] + mx[ mxih + 2 ][ 1 ];
                relvolumel = mx[ mxil - 1 ][ 1 ] + mx[ mxil - 2 ][ 1 ];

                if( relvolumeh > relvolumel )
                {
                    mxih = mxih + 2;
                    tvol = tvol + relvolumeh;
                }
                else
                    if( relvolumeh < relvolumel )
                    {
                        mxil = mxil - 2;
                        tvol = tvol + relvolumel;
                    }
                    else
                        if( relvolumeh == relvolumel )
                        {
                            mxih = mxih + 2;
                            mxil = mxil - 2;
                            tvol = tvol + relvolumeh + relvolumel;
                        }
            }
            else
                if( mxih + 2 >= bins AND mxil - 2 >= 0 )
                {
                    relvolumel = mx[ mxil - 1 ][ 1 ] + mx[ mxil - 2 ][ 1 ];
                    mxil = mxil - 2;
                    tvol = tvol + relvolumel;
                }
                else
                    if( mxih + 2 < bins AND mxil - 2 < 0 )
                    {
                        relvolumeh = mx[ mxih + 1 ][ 1 ] + mx[ mxih + 2 ][ 1 ];
                        mxih = mxih + 2;
                        tvol = tvol + relvolumeh;
                    }

            // handle uneven bars left
            if( mxih + 2 >= bins AND mxil - 2 < 0 )
            {
                if( mxih + 1 < bins )
                {
                    relvolumeh = mx[ mxih + 1 ][ 1 ];
                    mxih = mxih + 1;
                    tvol = tvol + relvolumeh;
                }

                if( mxil - 1 >= 0 )
                {
                    relvolumel = mx[ mxil - 1 ][ 1 ];
                    mxil = mxil - 1;
                    tvol = tvol + relvolumel;
                }
            }

            if( ( tvol / totVol ) >= volumeValuearea ) break;
        }

        priceh = mx[ mxih ][ 0 ];
        pricel = mx[ mxil ][ 0 ];
        x1 = idx2;
        x2 = idx1;
        line = LineArray( x1, priceh, x2, priceh );
        valueHigh1 = IIf( line, line, valueHigh1 );
        line = LineArray( x1, pricel, x2, pricel );
        valueLow1 = IIf( line, line, valueLow1 );

        // draw into next day
        if( cnt > 0 )
        {
            x1 = idx1;
            x2 = idx0;
            line = LineArray( x1, priceh, x2, priceh );
            valueHigh0 = IIf( line, line, valueHigh0 );
            line = LineArray( x1, pricel, x2, pricel );
            valueLow0 = IIf( line, line, valueLow0 );

            line = LineArray( x1, pricePOC, x2, pricePOC );
            pointOfControl = IIf( line, line, pointOfControl );
        }
        else
            if( cnt == 0 )
            {
                x1 = idx1;
                x2 = BarCount - 1;
                line = LineArray( x1, priceh, x2, priceh );
                valueHigh0 = IIf( line, line, valueHigh0 );
                line = LineArray( x1, pricel, x2, pricel );
                valueLow0 = IIf( line, line, valueLow0 );

                line = LineArray( x1, pricePOC, x2, pricePOC );
                pointOfControl = IIf( line, line, pointOfControl );

                // draw volume current day
                if( showVolume )
                {
                    bins = int( ( highOfDay[ x2 ] - lowOfDay[ x2 ] ) / TickSize );
                    mx = PriceVolDistribution( H, L, V, bins, False, x1, x2 );
                    //bins = MxGetSize( mx, 0 );
                    //_trace( "bins: " + bins + " x1: " + x1 + " x2: " + x2  );

                    dx = x2 - x1;

                    for( j = 0; j < bins; j++ )
                    {
                        price = mx[ j ][ 0 ]; // price level
                        relvolume = mx[ j ][ 1 ]; // relative volume 0..1
                        relbar = relvolume * dx;

                        GfxSelectPen( ColorRGB( 80, 0, 0 ), 1, 0 );
                        GfxMoveTo( x1, price );
                        GfxLineTo( x1 + relbar, price );
                    }
                }
            }

        i = lbx1[ idx1 ];
        cnt++;
    }
}

if( Status( "Action" ) == actionIndicator )
{
    Title = Name() + "   >>|<<   Volume area (%): " + volumeValuearea * 100 + "   >>|<<   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 );

    if( showHL )
    {
        Plot( highOfDay, "", colorGreen, styleDots | styleNoLine | styleNoRescale, Null, Null, 0, 0, 1 );
        Plot( lowOfDay, "", colorRed, styleDots | styleNoLine | styleNoRescale, Null, Null, 0, 0, 1 );
    }

    calculateVolumeValueArea( fvb, lvb );

    if( showVal1 )
    {
        Plot( valueHigh1, "", ColorRGB( 0, 250, 250 ), styleDots | styleNoLine | styleNoRescale, Null, Null, 0, -3, 1 );
        Plot( valueLow1, "", ColorRGB( 0, 250, 250 ), styleDots | styleNoLine | styleNoRescale, Null, Null, 0, -3, 1 );
    }

    if( showVal0 )
    {
        Plot( valueHigh0, "", ColorRGB( 250, 250, 0 ), styleDots | styleNoLine | styleNoRescale, Null, Null, 0, -3, 1 );
        Plot( valueLow0, "", ColorRGB( 250, 250, 0 ), styleDots | styleNoLine | styleNoRescale, Null, Null, 0, -3, 1 );
        PlotOHLC( valueLow0, valueLow0, valueHigh0, valueHigh0, "", ColorRGB( vai, vai, 0 ), styleCloud | styleNoRescale, Null, Null, 0, -5, 1 );
    }

    if( showPOC )
    {
        Plot( pointOfControl, "", ColorRGB( 250, 0, 250 ), styleDots | styleNoLine | styleNoRescale, Null, Null, 0, -3, 1 );
    }
}

if( Status( "Action" ) == actionExplore )
{
    calculateVolumeValueArea( 0, BarCount - 1 );

    Filter = Nz( valueHigh0 ) OR Nz( valueLow0 );
    AddColumn( Nz( valueHigh0 ), "Upper Boundary Volume Value area", 1.2, colorWhite, colorRed, -1 );
    AddColumn( Nz( valueLow0 ), "Lower Boundary Volume Value area", 1.2, colorWhite, colorGreen, -1 );
}
2 Likes

just looked into the exploration issue since for the backtest it is the same.

found simple solution that seems to work. Just replace the bottom actionExplore part with code below.

However this works only if you use “From- To Dates”.

But I am not too lazy to use it.

if( Status( "Action" ) == actionExplore )
{
	br = Status( "barinrange" );
	br = br - Ref( br, -1 );
	st = LastValue( ValueWhen( br == 1, bi ) );
	ed = LastValue( ValueWhen( br == (-1), bi ) );
	if( ed == 0 ) ed = BarCount - 1;
	
	//_Trace( "st: " + st + " ed: " + ed + " (Barcount - 1): " + (BarCount - 1 ) ); 
	
    calculateVolumeValueArea( st, ed );

    Filter = Nz( valueHigh0 ) OR Nz( valueLow0 );
    AddColumn( Nz( valueHigh0 ), "Upper Boundary Volume Value area", 1.2, colorWhite, colorRed, -1 );
    AddColumn( Nz( valueLow0 ), "Lower Boundary Volume Value area", 1.2, colorWhite, colorGreen, -1 );
}
1 Like

Since you asked...
Exploration can be used to output profile/footprint stats instead of outputting them on chart pane. Exploration is pretty flexible clean table environment. Below are two older sample pictures.

1
1

4 Likes

ok, yes my point was that I would prefer to see it in the chart, but your table looks nice.

I know little about the market profile but I always wanted to get hold of the volume value area in the form of arrays and that is now made easier with the PlotVolDistribution() function. As I understand it these market profile traders work with a “Volume Value Area” and a “Time Value Area”. With a POC (point of control => the price at with it stayed the max amount of time) and the VPOC, the volume point of control => the price at which the maximum amount of volume is traded.

So we still need a function PlotTimeDistribution() :wink: :wink:

your chart does not show a “time value area”, right?

Ed,
You are mixing things…market profile and volume profile are similar concepts but not the same.
Volume profiles (here by using PriceVolDistribution() function) are based on volume.
Market profiles are based on time, see time price opportunity (TPO).
The terms Value Area, VAH/VAL, POC are used in both profile versions.

It shows volume profiles.

1 Like

yes ok. I read that some mix the “time profile” with the “volume profile” chart and look for confluence between the two. But apparently the volume profile is the more powerful. I wanted to use the volume area high and low similar to how levels S4 and R4 are used in the Camarilla system. But so far I only managed to make a system that loses money. Buy above the volume area high and sell at the close, short below volume area low and cover at the close is not good enough.

How about Buy at the MidpPoint, if Volumes & Closes are Progressive , And Short if the Closes are lower and Volumes are Progressive ?

with midpoint you mean the average of the volume area high (VAH) with the VAL? Or the Volume area POC? It can all be tested.

Eyeballing the volume profile I would say that below VAL the market is weak, above VAH it is strong and between VAH and VAL it is range bound. It generally is the case but translating this into a winning system is another matter :slight_smile:

I can post a version of earlier code that allows you to add a trading system

2 Likes

I expanded my code a bit with

  1. the requested shifted arrays
  2. requested strategy.
  3. value area is calculated going 1 step at a time instead of 2, I do not see the point of doing steps of 2.

The array valueHigh0 contains the volume value area high (VAH) shifted on the next time segment.
The array valueLow0 contains the VAL shifted to next time segment. Time segments are divided by the separator (blue vertical line). The arrays can be used for trading system development.

In the parameter window (“Shift Volume Value Area”) you can add shifted valueHighs, valueLows and VPOCs. They are stored in dynamic arrays and can also be used to build a trading strategy. Shifted 1 additional time segment I call it valueHigh1, etc. You see it appearing in the chart.

I added a simple strategy. For you to see it you need to set the “Shift Volume Value Area” in the param window to 1 (because the strategy uses 1 shifted valueArea array). The strategy buys when the price is above valueHigh0 and above valueHigh1 and sells at the last bar of the separator interval. Same for short, it goes short below valueLow0 and valueLow1 and cover at last bar of the separator interval.

// in reply to: http://forum.amibroker.com/t/how-to-get-the-most-traded-area-value-of-previous-day-intraday-data/2472
// !!!! TickSize is hardcoded in code. You need to adjust for, according to contract specifics.
// E.M.Pottasch (10/2017)

// some reading
// - Value area calculation used in this FL: http://onlinelibrary.wiley.com/doi/10.1002/9781118659724.app1/pdf
// - http://www.ranchodinero.com/volume-tpo-essentials/

// possible strategies (did not test any myself)
// - 80% rule: https://marketdelta.com/wp-content/uploads/Strategy-80PercentRule.pdf
// -

TickSize = 0.25; // <<<<==== adjust yourself
if( TickSize == 0 )
{
    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 );
showValI = ParamToggle( "Show Value area inside same day", "No|Yes", 0 );
showVal0 = ParamToggle( "Show Value area projected into the next day", "No|Yes", 1 );
showVPOC = ParamToggle( "Show Volume Point Of Control (VPOC)", "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", 1, 1, 10, 1 );
lineWidth2 = Param( "GFX Volume Line Width", 1, 1, 10, 1 );
shft = Param( "Shift Volume Value Area (Interval)", 0, 0, 10, 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;
Buy = Sell = Short = Cover = BuyPrice = SellPrice = ShortPrice = CoverPrice = 0;
valueHighI = valueLowI = Null;
valueHigh0 = valueLow0 = Null;
pointOfControl0 = Null;
highOfDay = TimeFrameGetPrice( "H", dn1 );
lowOfDay = 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 tradingStrategy1( fb, lb )
{
    inLong = inShort = 0;
    valueHigh1 = Nz( VarGet( "valueHigh1" ) );
    valueLow1 = Nz( VarGet( "valueLow1" ) );

    for( i = fb; i < lb; i++ )
    {
        if( valueHigh0[i] == 0 OR valueHigh1[i] == 0 )
        {
            // do nothing, try next i
        }
        else
            if( separator[i + 1] AND inLong )
            {
                Sell[i] = 1;
                SellPrice[i] = C[i];
                inLong = 0;
            }
            else
                if( separator[i + 1] AND inShort )
                {
                    Cover[i] = 1;
                    CoverPrice[i] = C[i];
                    inShort = 0;
                }
                else
                    if( !inLong AND !inShort AND C[i] > valueHigh0[i] AND C[i] > valueHigh1[i] )
                    {
                        Buy[i] = 1;
                        BuyPrice[i] = C[i];
                        inLong = 1;
                    }
                    else
                        if( !inLong AND !inShort AND C[i] < valueLow0[i] AND C[i] < valueLow1[i] )
                        {
                            Short[i] = 1;
                            ShortPrice[i] = C[i];
                            inShort = 1;
                        }
    }
}

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( ( highOfDay[ idx1 - 1 ] - lowOfDay[ idx1 - 1 ] ) / TickSize );
        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, price, ColorRGB( 80, 0, 0 ), -4, 0, "", lineWidth2 );
            }

            if( relvolume > mxVol )
            {
                mxVol = relvolume;
                mxVolBinIdx = j;
            }

            totVol = totVol + relvolume;
        }

        // calculate Volume Value area
        mxih = mxil = mxVolBinIdx;
        tvol = mx[ mxVolBinIdx ][ 1 ];
        priceVPOC = mx[ mxVolBinIdx ][ 0 ];

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

        priceh = mx[ mxih ][ 0 ];
        pricel = mx[ mxil ][ 0 ];
        x1 = idx2;
        x2 = idx1;
        line = Ref( LineArray( x1, priceh, x2, priceh ), 1 );
        valueHighI = IIf( line, line, valueHighI );
        line = Ref( LineArray( x1, pricel, x2, pricel ), 1 );
        valueLowI = IIf( line, line, valueLowI );

        if( showHL )
        {
            ph = highOfDay[ x2 - 1 ];
            drawHLine( x1, x2, ph, ColorRGB( 0, 250, 0 ), -3, 1, "HOD ", lineWidth1 );
            pl = lowOfDay[ x2 - 1 ];
            drawHLine( x1, x2, pl, ColorRGB( 250, 0, 0 ), -3, 1, "LOD ", lineWidth1 );
        }

        if( showValI )
        {
            drawHLine( x1, x2, priceh , ColorRGB( 0, 250, 250 ), -3, 1, "" + volumeValueArea * 100 + "% H ", lineWidth1 );
            drawHLine( x1, x2, pricel , ColorRGB( 0, 250, 250 ), -3, 1, "" + volumeValueArea * 100 + "% L ", lineWidth1 );
        }

        // draw into next day
        if( cnt > 0 )
        {
            x1 = idx1;
            x2 = idx0;
            line = Ref( LineArray( x1, priceh, x2, priceh ), 1 );
            valueHigh0 = IIf( line, line, valueHigh0 );
            line = Ref( LineArray( x1, pricel, x2, pricel ), 1 );
            valueLow0 = IIf( line, line, valueLow0 );

            if( showVal0 )
            {
                drawHLine( x1, x2, priceh , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% H ", lineWidth1 );
                drawHLine( x1, x2, pricel , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% L ", lineWidth1 );
            }

            line = Ref( LineArray( x1, priceVPOC, x2, priceVPOC ), 1 );
            pointOfControl0 = IIf( line, line, pointOfControl0 );

            if( showVPOC )
            {
                drawHLine( x1, x2, priceVPOC , ColorRGB( 250, 0, 250 ), -3, 1, "VPOC ", lineWidth1 );
            }
        }
        else
            if( cnt == 0 )
            {
                x1 = idx1;
                x2 = BarCount - 1;
                line = Ref( LineArray( x1, priceh, x2, priceh ), 1 );
                valueHigh0 = IIf( line, line, valueHigh0 );
                line = Ref( LineArray( x1, pricel, x2, pricel ), 1 );
                valueLow0 = IIf( line, line, valueLow0 );

                if( showVal0 )
                {
                    drawHLine( x1, x2, priceh , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% H ", lineWidth1 );
                    drawHLine( x1, x2, pricel , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% L ", lineWidth1 );
                }

                line = Ref( LineArray( x1, priceVPOC, x2, priceVPOC ), 1 );
                pointOfControl0 = IIf( line, line, pointOfControl0 );

                if( showVPOC )
                {
                    drawHLine( x1, x2, priceVPOC , ColorRGB( 250, 0, 250 ), -3, 1, "VPOC ", lineWidth1 );
                }

                if( showHL )
                {
                    ph = highOfDay[ x2 ];
                    drawHLine( x1, x2, ph, ColorRGB( 0, 250, 0 ), -3, 1, "HOD ", lineWidth1 );

                    pl = lowOfDay[ x2 ];
                    drawHLine( x1, x2, pl, ColorRGB( 250, 0, 0 ), -3, 1, "LOD ", lineWidth1 );
                }

                // draw volume current day
                if( showVolume )
                {
                    GfxSetZOrder( -4 );
                    bins = int( ( highOfDay[ x2 ] - lowOfDay[ x2 ] ) / TickSize );
                    mx = PriceVolDistribution( H, L, V, bins, False, x1, x2 );
                    dx = x2 - x1;

                    for( j = 0; j < bins; j++ )
                    {
                        price = mx[ j ][ 0 ]; // price level
                        relvolume = mx[ j ][ 1 ]; // relative volume 0..1
                        relbar = relvolume * dx;
                        drawHLine( x1, x1 + relbar, price, ColorRGB( 80, 0, 0 ), -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 = BarCount - 1;
        }
        else
            if( lbx1[i] < lbx0[i] )
            {
                x1 = lbx1[i];
                x2 = lbx0[i];
            }

        for( s = 0; s < shft; s++ )
        {
            xx0 = VarGet( "lbx" + ( s + 0 ) );
            xx1 = VarGet( "lbx" + ( s + 1 ) );
            xx2 = VarGet( "lbx" + ( s + 2 ) );

            if( xx0[i] == xx1[i] )
            {
                // source index
                x1s = xx2[i];
                x2s = xx1[i];

                if( !IsEmpty( x1s ) )
                {
                    drawHLine( x1, x2, pointOfControl0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 0, 250 / ( s + 2 ) ), -3, 1, "[VPOC" + -( s + 1 ) + "] ", lineWidth1 );
                    drawHLine( x1, x2, valueHigh0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[H" + -( s + 1 ) + "] ", lineWidth1 );
                    drawHLine( x1, x2, valueLow0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[L" + -( s + 1 ) + "] ", lineWidth1 );

                    // save shifted value area in dynamic arrays
                    if( !IsEmpty( pointOfControl0[ x1s ] ) )
                    {
                        line = Ref( LineArray( x1, pointOfControl0[ x1s ], x2, pointOfControl0[ x1s ] ), 1 );
                        VarSet( "pointOfControl" + ( s + 1 ), IIf( line, line, VarGet( "pointOfControl" + ( s + 1 ) ) ) );
                    }

                    if( !IsEmpty( valueHigh0[ x1s ] ) )
                    {
                        line = Ref( LineArray( x1, valueHigh0[ x1s ], x2, valueHigh0[ x1s ] ), 1 );
                        VarSet( "valueHigh" + ( s + 1 ), IIf( line, line, VarGet( "valueHigh" + ( s + 1 ) ) ) );
                    }

                    if( !IsEmpty( valueLow0[ x1s ] ) )
                    {
                        line = Ref( LineArray( x1, valueLow0[ x1s ], x2, valueLow0[ x1s ] ), 1 );
                        VarSet( "valueLow" + ( s + 1 ), IIf( line, line, VarGet( "valueLow" + ( s + 1 ) ) ) );
                    }

                }
            }
            else
                if( xx1[i] < xx0[i] )
                {
                    // source index
                    x1s = xx2[i];
                    x2s = xx1[i];

                    if( !IsEmpty( x1s ) )
                    {
                        drawHLine( x1, x2, pointOfControl0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 0, 250 / ( s + 2 ) ), -3, 1, "[VPOC" + -( s + 1 ) + "] ", lineWidth1 );
                        drawHLine( x1, x2, valueHigh0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[H" + -( s + 1 ) + "] ", lineWidth1 );
                        drawHLine( x1, x2, valueLow0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[L" + -( s + 1 ) + "] ", lineWidth1 );

                        // save shifted value area in dynamic arrays
                        if( !IsEmpty( pointOfControl0[ x1s ] ) )
                        {
                            line = Ref( LineArray( x1, pointOfControl0[ x1s ], x2, pointOfControl0[ x1s ] ), 1 );
                            VarSet( "pointOfControl" + ( s + 1 ), IIf( line, line, VarGet( "pointOfControl" + ( s + 1 ) ) ) );
                        }

                        if( !IsEmpty( valueHigh0[ x1s ] ) )
                        {
                            line = Ref( LineArray( x1, valueHigh0[ x1s ], x2, valueHigh0[ x1s ] ), 1 );
                            VarSet( "valueHigh" + ( s + 1 ), IIf( line, line, VarGet( "valueHigh" + ( s + 1 ) ) ) );
                        }

                        if( !IsEmpty( valueLow0[ x1s ] ) )
                        {
                            line = Ref( LineArray( x1, valueLow0[ x1s ], x2, valueLow0[ x1s ] ), 1 );
                            VarSet( "valueLow" + ( s + 1 ), IIf( line, line, VarGet( "valueLow" + ( s + 1 ) ) ) );
                        }
                    }
                }
        }

        i = lbx1[i];
    }
}

if( Status( "Action" ) == actionIndicator )
{
    Title = Name() + "   >>|<<   Volume area (%): " + volumeValueArea * 100 + "   >>|<<   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 );

    calculateVolumeValueArea( fvb, lvb );
    shiftVolumeValueArea( fvb, lvb, shft );
    tradingStrategy1( fvb, lvb );

    PlotShapes( IIf( Buy, shapeUpArrow, shapeNone ), colorDarkGreen, 0, L, -15 );
    PlotShapes( IIf( Buy, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
    PlotShapes( IIf( Sell, shapeDownArrow, shapeNone ), colorRed, 0, H, -15 );
    PlotShapes( IIf( Sell, shapeSmallCircle, shapeNone ), colorwhite, 0, SellPrice, 0 );
    PlotShapes( IIf( Short, shapeSmallDownTriangle, shapeNone ), colorRed, 0, H, IIf( Short AND Sell, -30, -15 ) );
    PlotShapes( IIf( Short, shapeSmallCircle, shapeNone ), colorWhite, 0, ShortPrice, 0 );
    PlotShapes( IIf( Cover, shapeSmallUpTriangle, shapeNone ), colorDarkGreen, 0, L, IIf( Cover AND Buy, -30, -15 ) );
    PlotShapes( IIf( Cover, shapeSmallCircle, shapeNone ), colorwhite, 0, CoverPrice, 0 );
}

if( Status( "Action" ) == actionExplore )
{
    // !!!*** use From- To Dates in analysis Window ***!!!

    br = Status( "barinrange" );
    periods = IIf( br, separator, Null );
    periods = LastValue( Cum( periods ) );

    br = br - Ref( br, -1 );
    st = LastValue( ValueWhen( br == 1, bi ) );
    ed = LastValue( ValueWhen( br == ( -1 ), bi ) );

    if( ed == 0 ) ed = BarCount - 1;

    // shift the startpoint else valueHigh1,2,3 etc does not line up in the exploration
    dif = int( ( ed - st ) / periods * shft );
    st = Max( st - dif, 0 );

    calculateVolumeValueArea( st, ed );
    shiftVolumeValueArea( st, ed, shft );

    fil = IIf( Nz( valueHigh0 ) != 0, 1, 0 );
    fil = IIf( Nz( valueLow0 ) != 0, 1, fil );

    for( i = 1; i <= shft; i++ )
    {
        fil = IIf( Nz( VarGet( "valueHigh" + i ) ) != 0, 1, fil );
        fil = IIf( Nz( VarGet( "valueLow" + i ) ) != 0, 1, fil );
    }

    Filter = fil;

    AddColumn( Nz( valueHigh0 ), "VH0", 1.2, colorWhite, colorRed, -1 );

    for( i = 1; i <= shft; i++ )
    {
        AddColumn( VarGet( "valueHigh" + i ), "VH" + i, 1.2, colorWhite, colorRed, -1 );
    }

    AddColumn( Nz( valueLow0 ), "VL0", 1.2, colorWhite, colorGreen, -1 );

    for( i = 1; i <= shft; i++ )
    {
        AddColumn( VarGet( "valueLow" + i ), "VL" + i, 1.2, colorWhite, colorGreen, -1 );
    }
}

if( Status( "Action" ) == actionBacktest )
{
    // !!!*** use From- To Dates in analysis Window ***!!!

    SetTradeDelays( 0, 0, 0, 0 );
    SetOption( "FuturesMode", True );
    SetOption( "MaxOpenPositions", 1 );
    SetOption( "CommissionAmount", 2.02 );
    SetPositionSize( 1, spsShares );

    br = Status( "barinrange" );
    periods = IIf( br, separator, Null );
    periods = LastValue( Cum( periods ) );

    br = br - Ref( br, -1 );
    st = LastValue( ValueWhen( br == 1, bi ) );
    ed = LastValue( ValueWhen( br == ( -1 ), bi ) );

    if( ed == 0 ) ed = BarCount - 1;

    if( periods * shft > 0 )
        dif = int( ( ed - st ) / periods * shft );

    st = Max( st - dif, 0 );

    calculateVolumeValueArea( st, ed );
    shiftVolumeValueArea( st, ed, shft );
    tradingStrategy1( st, ed );
}
3 Likes

Thank You Ed,
We asked for a flower and you gave us Bouquet.
:smiling_face:

2 Likes

thanks,

sometimes you see the signals of the trading strategy jumping. This is because in the visual chart there are not enough bars for the correct calculation. You can add a few bars to avoid this.

in the section if( Status( “Action” ) == actionIndicator ) add the following code above the function calculateVolumeValueArea( fvb, lvb )

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

First of all a Big thanks to empottasch for his effort to taking this to this extend with his continuous effort

as per my understanding

vah0 (plotting yesterdays valuearea high in todays chart)
val0 (plotting yesterdays valuearea low in todays chart)
pointofcontrol (potting yesterdayspoc in todays chart)

what help iam looking is

lets draw the current (vah,val and poc) on todays chart
now if we need yesterdays vah or val or poc
lets use reference to get previous day vah,val, or poc

for example if we need yesterdays poc

ypoc =ref(poc,-1) this is yesterday poc
yvah =ref(vah,-1) this is yesterday vah
yval =ref(vall,-1) this is yesterday val

for example if we need day before yesterdays poc

ypoc1 =ref(poc,-2) this is day before yesterday poc
yvah1 =ref(vah,-2) this is day before yesterday vah
yval1 =ref(val,-2) this is day before yesterday val

if we can get in this format without any loop
we can go deep to add muti indicators to backtest and testing ideas with it

thank you so much for your effort for making this wonderful afl so far

_SECTION_BEGIN("");
// in reply to: http://forum.amibroker.com/t/how-to-get-the-most-traded-area-value-of-previous-day-intraday-data/2472
// !!!! TickSize is hardcoded in code. You need to adjust for, according to contract specifics.
// E.M.Pottasch (10/2017)

// some reading
// - Value area calculation used in this FL: http://onlinelibrary.wiley.com/doi/10.1002/9781118659724.app1/pdf
// - http://www.ranchodinero.com/volume-tpo-essentials/

// possible strategies (did not test any myself)
// - 80% rule: https://marketdelta.com/wp-content/uploads/Strategy-80PercentRule.pdf
// -

TickSize = 0.05; // <<<<==== adjust yourself
if( TickSize == 0 )
{
    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 );
showValI = ParamToggle( "Show Value area inside same day", "No|Yes", 0 );
showVal0 = ParamToggle( "Show Value area projected into the next day", "No|Yes", 1 );
showVPOC = ParamToggle( "Show Volume Point Of Control (VPOC)", "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", 1, 1, 10, 1 );
lineWidth2 = Param( "GFX Volume Line Width", 1, 1, 10, 1 );
shft = Param( "Shift Volume Value Area (Interval)", 0, 0, 10, 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;
Buy = Sell = Short = Cover = BuyPrice = SellPrice = ShortPrice = CoverPrice = 0;
valueHighI = valueLowI = Null;
valueHigh0 = valueLow0 = Null;
pointOfControl0 = Null;
highOfDay = TimeFrameGetPrice( "H", dn1 );
lowOfDay = 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 tradingStrategy1( fb, lb )
{
    inLong = inShort = 0;
    valueHigh1 = Nz( VarGet( "valueHigh1" ) );
    valueLow1 = Nz( VarGet( "valueLow1" ) );

    for( i = fb; i < lb; i++ )
    {
        if( valueHigh0[i] == 0 OR valueHigh1[i] == 0 )
        {
            // do nothing, try next i
        }
        else
            if( separator[i + 1] AND inLong )
            {
                Sell[i] = 1;
                SellPrice[i] = C[i];
                inLong = 0;
            }
            else
                if( separator[i + 1] AND inShort )
                {
                    Cover[i] = 1;
                    CoverPrice[i] = C[i];
                    inShort = 0;
                }
                else
                    if( !inLong AND !inShort AND C[i] > valueHigh0[i] AND C[i] > valueHigh1[i] )
                    {
                        Buy[i] = 1;
                        BuyPrice[i] = C[i];
                        inLong = 1;
                    }
                    else
                        if( !inLong AND !inShort AND C[i] < valueLow0[i] AND C[i] < valueLow1[i] )
                        {
                            Short[i] = 1;
                            ShortPrice[i] = C[i];
                            inShort = 1;
                        }
    }
}

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( ( highOfDay[ idx1 - 1 ] - lowOfDay[ idx1 - 1 ] ) / TickSize );
        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, price, ColorRGB( 80, 0, 0 ), -4, 0, "", lineWidth2 );
            }

            if( relvolume > mxVol )
            {
                mxVol = relvolume;
                mxVolBinIdx = j;
            }

            totVol = totVol + relvolume;
        }

        // calculate Volume Value area
        mxih = mxil = mxVolBinIdx;
        tvol = mx[ mxVolBinIdx ][ 1 ];
        priceVPOC = mx[ mxVolBinIdx ][ 0 ];

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

        priceh = mx[ mxih ][ 0 ];
        pricel = mx[ mxil ][ 0 ];
        x1 = idx2;
        x2 = idx1;
        line = Ref( LineArray( x1, priceh, x2, priceh ), 1 );
        valueHighI = IIf( line, line, valueHighI );
        line = Ref( LineArray( x1, pricel, x2, pricel ), 1 );
        valueLowI = IIf( line, line, valueLowI );

        if( showHL )
        {
            ph = highOfDay[ x2 - 1 ];
            drawHLine( x1, x2, ph, ColorRGB( 0, 250, 0 ), -3, 1, "HOD ", lineWidth1 );
            pl = lowOfDay[ x2 - 1 ];
            drawHLine( x1, x2, pl, ColorRGB( 250, 0, 0 ), -3, 1, "LOD ", lineWidth1 );
        }

        if( showValI )
        {
            drawHLine( x1, x2, priceh , ColorRGB( 0, 250, 250 ), -3, 1, "" + volumeValueArea * 100 + "% H ", lineWidth1 );
            drawHLine( x1, x2, pricel , ColorRGB( 0, 250, 250 ), -3, 1, "" + volumeValueArea * 100 + "% L ", lineWidth1 );
        }

        // draw into next day
        if( cnt > 0 )
        {
            x1 = idx1;
            x2 = idx0;
            line = Ref( LineArray( x1, priceh, x2, priceh ), 1 );
            valueHigh0 = IIf( line, line, valueHigh0 );
            line = Ref( LineArray( x1, pricel, x2, pricel ), 1 );
            valueLow0 = IIf( line, line, valueLow0 );

            if( showVal0 )
            {
                drawHLine( x1, x2, priceh , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% H ", lineWidth1 );
                drawHLine( x1, x2, pricel , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% L ", lineWidth1 );
            }

            line = Ref( LineArray( x1, priceVPOC, x2, priceVPOC ), 1 );
            pointOfControl0 = IIf( line, line, pointOfControl0 );

            if( showVPOC )
            {
                drawHLine( x1, x2, priceVPOC , ColorRGB( 250, 0, 250 ), -3, 1, "VPOC ", lineWidth1 );
            }
        }
        else
            if( cnt == 0 )
            {
                x1 = idx1;
                x2 = BarCount - 1;
                line = Ref( LineArray( x1, priceh, x2, priceh ), 1 );
                valueHigh0 = IIf( line, line, valueHigh0 );
                line = Ref( LineArray( x1, pricel, x2, pricel ), 1 );
                valueLow0 = IIf( line, line, valueLow0 );

                if( showVal0 )
                {
                    drawHLine( x1, x2, priceh , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% H ", lineWidth1 );
                    drawHLine( x1, x2, pricel , ColorRGB( 250, 250, 0 ), -3, 1, "" + volumeValueArea * 100 + "% L ", lineWidth1 );
                }

                line = Ref( LineArray( x1, priceVPOC, x2, priceVPOC ), 1 );
                pointOfControl0 = IIf( line, line, pointOfControl0 );

                if( showVPOC )
                {
                    drawHLine( x1, x2, priceVPOC , ColorRGB( 250, 0, 250 ), -3, 1, "VPOC ", lineWidth1 );
                }

                if( showHL )
                {
                    ph = highOfDay[ x2 ];
                    drawHLine( x1, x2, ph, ColorRGB( 0, 250, 0 ), -3, 1, "HOD ", lineWidth1 );

                    pl = lowOfDay[ x2 ];
                    drawHLine( x1, x2, pl, ColorRGB( 250, 0, 0 ), -3, 1, "LOD ", lineWidth1 );
                }

                // draw volume current day
                if( showVolume )
                {
                    GfxSetZOrder( -4 );
                    bins = int( ( highOfDay[ x2 ] - lowOfDay[ x2 ] ) / TickSize );
                    mx = PriceVolDistribution( H, L, V, bins, False, x1, x2 );
                    dx = x2 - x1;

                    for( j = 0; j < bins; j++ )
                    {
                        price = mx[ j ][ 0 ]; // price level
                        relvolume = mx[ j ][ 1 ]; // relative volume 0..1
                        relbar = relvolume * dx;
                        drawHLine( x1, x1 + relbar, price, ColorRGB( 80, 0, 0 ), -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 = BarCount - 1;
        }
        else
            if( lbx1[i] < lbx0[i] )
            {
                x1 = lbx1[i];
                x2 = lbx0[i];
            }

        for( s = 0; s < shft; s++ )
        {
            xx0 = VarGet( "lbx" + ( s + 0 ) );
            xx1 = VarGet( "lbx" + ( s + 1 ) );
            xx2 = VarGet( "lbx" + ( s + 2 ) );

            if( xx0[i] == xx1[i] )
            {
                // source index
                x1s = xx2[i];
                x2s = xx1[i];

                if( !IsEmpty( x1s ) )
                {
                    drawHLine( x1, x2, pointOfControl0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 0, 250 / ( s + 2 ) ), -3, 1, "[VPOC" + -( s + 1 ) + "] ", lineWidth1 );
                    drawHLine( x1, x2, valueHigh0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[H" + -( s + 1 ) + "] ", lineWidth1 );
                    drawHLine( x1, x2, valueLow0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[L" + -( s + 1 ) + "] ", lineWidth1 );

                    // save shifted value area in dynamic arrays
                    if( !IsEmpty( pointOfControl0[ x1s ] ) )
                    {
                        line = Ref( LineArray( x1, pointOfControl0[ x1s ], x2, pointOfControl0[ x1s ] ), 1 );
                        VarSet( "pointOfControl" + ( s + 1 ), IIf( line, line, VarGet( "pointOfControl" + ( s + 1 ) ) ) );
                    }

                    if( !IsEmpty( valueHigh0[ x1s ] ) )
                    {
                        line = Ref( LineArray( x1, valueHigh0[ x1s ], x2, valueHigh0[ x1s ] ), 1 );
                        VarSet( "valueHigh" + ( s + 1 ), IIf( line, line, VarGet( "valueHigh" + ( s + 1 ) ) ) );
                    }

                    if( !IsEmpty( valueLow0[ x1s ] ) )
                    {
                        line = Ref( LineArray( x1, valueLow0[ x1s ], x2, valueLow0[ x1s ] ), 1 );
                        VarSet( "valueLow" + ( s + 1 ), IIf( line, line, VarGet( "valueLow" + ( s + 1 ) ) ) );
                    }

                }
            }
            else
                if( xx1[i] < xx0[i] )
                {
                    // source index
                    x1s = xx2[i];
                    x2s = xx1[i];

                    if( !IsEmpty( x1s ) )
                    {
                        drawHLine( x1, x2, pointOfControl0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 0, 250 / ( s + 2 ) ), -3, 1, "[VPOC" + -( s + 1 ) + "] ", lineWidth1 );
                        drawHLine( x1, x2, valueHigh0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[H" + -( s + 1 ) + "] ", lineWidth1 );
                        drawHLine( x1, x2, valueLow0[ x1s ] , ColorRGB( 250 / ( s + 2 ), 250 / ( s + 2 ), 0 ), -3, 1, "[L" + -( s + 1 ) + "] ", lineWidth1 );

                        // save shifted value area in dynamic arrays
                        if( !IsEmpty( pointOfControl0[ x1s ] ) )
                        {
                            line = Ref( LineArray( x1, pointOfControl0[ x1s ], x2, pointOfControl0[ x1s ] ), 1 );
                            VarSet( "pointOfControl" + ( s + 1 ), IIf( line, line, VarGet( "pointOfControl" + ( s + 1 ) ) ) );
                        }

                        if( !IsEmpty( valueHigh0[ x1s ] ) )
                        {
                            line = Ref( LineArray( x1, valueHigh0[ x1s ], x2, valueHigh0[ x1s ] ), 1 );
                            VarSet( "valueHigh" + ( s + 1 ), IIf( line, line, VarGet( "valueHigh" + ( s + 1 ) ) ) );
                        }

                        if( !IsEmpty( valueLow0[ x1s ] ) )
                        {
                            line = Ref( LineArray( x1, valueLow0[ x1s ], x2, valueLow0[ x1s ] ), 1 );
                            VarSet( "valueLow" + ( s + 1 ), IIf( line, line, VarGet( "valueLow" + ( s + 1 ) ) ) );
                        }
                    }
                }
        }

        i = lbx1[i];
    }
}

if( Status( "Action" ) == actionIndicator )
{
    Title = Name() + "   >>|<<   Volume area (%): " + volumeValueArea * 100 + "   >>|<<   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 );
    tradingStrategy1( fvb, lvb );

    PlotShapes( IIf( Buy, shapeUpArrow, shapeNone ), colorDarkGreen, 0, L, -15 );
    PlotShapes( IIf( Buy, shapeSmallCircle, shapeNone ), colorWhite, 0, BuyPrice, 0 );
    PlotShapes( IIf( Sell, shapeDownArrow, shapeNone ), colorRed, 0, H, -15 );
    PlotShapes( IIf( Sell, shapeSmallCircle, shapeNone ), colorwhite, 0, SellPrice, 0 );
    PlotShapes( IIf( Short, shapeSmallDownTriangle, shapeNone ), colorRed, 0, H, IIf( Short AND Sell, -30, -15 ) );
    PlotShapes( IIf( Short, shapeSmallCircle, shapeNone ), colorWhite, 0, ShortPrice, 0 );
    PlotShapes( IIf( Cover, shapeSmallUpTriangle, shapeNone ), colorDarkGreen, 0, L, IIf( Cover AND Buy, -30, -15 ) );
    PlotShapes( IIf( Cover, shapeSmallCircle, shapeNone ), colorwhite, 0, CoverPrice, 0 );
}

if( Status( "Action" ) == actionExplore )
{
    // !!!*** use From- To Dates in analysis Window ***!!!

    br = Status( "barinrange" );
    periods = IIf( br, separator, Null );
    periods = LastValue( Cum( periods ) );

    br = br - Ref( br, -1 );
    st = LastValue( ValueWhen( br == 1, bi ) );
    ed = LastValue( ValueWhen( br == ( -1 ), bi ) );

    if( ed == 0 ) ed = BarCount - 1;

    // shift the startpoint else valueHigh1,2,3 etc does not line up in the exploration
    dif = int( ( ed - st ) / periods * shft );
    st = Max( st - dif, 0 );

    calculateVolumeValueArea( st, ed );
    shiftVolumeValueArea( st, ed, shft );

    fil = IIf( Nz( valueHigh0 ) != 0, 1, 0 );
    fil = IIf( Nz( valueLow0 ) != 0, 1, fil );

    for( i = 1; i <= shft; i++ )
    {
        fil = IIf( Nz( VarGet( "valueHigh" + i ) ) != 0, 1, fil );
        fil = IIf( Nz( VarGet( "valueLow" + i ) ) != 0, 1, fil );
    }

    Filter = fil;

    AddColumn( Nz( valueHigh0 ), "VH0", 1.2, colorWhite, colorRed, -1 );

    for( i = 1; i <= shft; i++ )
    {
        AddColumn( VarGet( "valueHigh" + i ), "VH" + i, 1.2, colorWhite, colorRed, -1 );
    }

    AddColumn( Nz( valueLow0 ), "VL0", 1.2, colorWhite, colorGreen, -1 );
    

    for( i = 1; i <= shft; i++ )
    {
        AddColumn( VarGet( "valueLow" + i ), "VL" + i, 1.2, colorWhite, colorGreen, -1 );
    }
}

if( Status( "Action" ) == actionBacktest )
{
    // !!!*** use From- To Dates in analysis Window ***!!!

    SetTradeDelays( 0, 0, 0, 0 );
    SetOption( "FuturesMode", True );
    SetOption( "MaxOpenPositions", 1 );
    SetOption( "CommissionAmount", 2.02 );
    SetPositionSize( 1, spsShares );

    br = Status( "barinrange" );
    periods = IIf( br, separator, Null );
    periods = LastValue( Cum( periods ) );

    br = br - Ref( br, -1 );
    st = LastValue( ValueWhen( br == 1, bi ) );
    ed = LastValue( ValueWhen( br == ( -1 ), bi ) );

    if( ed == 0 ) ed = BarCount - 1;

    if( periods * shft > 0 )
        dif = int( ( ed - st ) / periods * shft );

    st = Max( st - dif, 0 );

    calculateVolumeValueArea( st, ed );
    shiftVolumeValueArea( st, ed, shft );
    tradingStrategy1( st, ed );
}
_SECTION_END();
1 Like

in the default chart of the last code I posted the yellow “High” line is called valueHigh0

this is an array and you can calculate signals with it, for instance:

Buy = Cross( C, valueHigh0 )

the shifted line (1 time segment) is also an array. It is stored dynamically but can be used in the same way.

Buy = Cross( C, valueHigh1 )
etc.

the code is setup so you can show the indicator ( have it plot the chart very fast) and at the same time do an exploration and a backtest, but if you want to do other things you will have to do it yourself or change the code for your own needs. The ingredients are all in there in my opinion.

If you have a good trading setup and are able to present this in detail in a chart, where you want to buy, sell, short etc, i am willing to help and give another trading Strategy example. But for the rest you need to figure it out yourself.

I was put my time in this because I only just found out about this priceVolDistribution() function and find it interesting but I am not going to rewrite the entire thing

2 Likes

Another general note, you better calculate (intraday) profiles on base intervals (such as 1-tick, 1sec, …) and store it there (that’s especially true for intra-bar profiles aka Footprints). It is pretty much pointless doing it i.e. in 15-min, 30 -min, hourly, etc. intervals.

1 Like

hi fx, tanks, will respond a bit more tomorrow.

I understand that making a 15-min intra-bar volume area profile with 1-hour data will be a tough job. Will think about it a bit. In any case when you have for instance 5-min data then the levels calculated in the 5-min, 15-min, 1-hour etc time frame should be identical, which I believe they are.

1 Like

Ed,

What I was saying was that you just have less information available per each profile the lower the granularity. It's like trying to figure out on a daily bar whether high or low occurred first intraday. So for example I think you would agree that it would make no sense to calculate bar profile on selected bar since you would get this.. a rectangle.

p4

The higher the granularity the more information you get intrabar

p1

So then profile can be stored to static var on base interval and be called on other intervals showing the very same profile as by base interval then (compare below picture to first one).

p5

2 Likes

hi fx,

yes I agree. Since most of the calculations are done in the visual range of the chart working with 1min or 5min data I do not think there is a real problem because Amibroker is so fast. But I understand what you are saying and will think a bit if I will add this. But you know here in Belgium we are forced by law to drink beer after 6PM so today I will leave it.

Most of us probably work in the 1 or 5min timeframe. Then it is no real issue I think.

Tomorrow I will check with finer granularity and see what I will find. But if you or anyone else sees obvious improvements for the code I posted go ahead, I am always happy to learn some new tricks.

2 Likes