How to make a variable for prior S/R level?

Hello,

I apologize, if this has been ask before, but how would you make variable for price level, which has been a support and resistance in the past for multiple times?

I was thinking something like this, but with this approach, I would limit my reach and its probably not the best approach...

S/R_level = Ref(L,-3) AND Ref(H,-5) AND Ref(H,-11);

Cross(L, S/R_level);

Any better Ideas?

Thanks

Price levels are not boolean and can't be combined using "AND" operator (logical op). Also / is a division operator, you can't use S/R_level as variable name.

I suggest you first look at the existing threads using forum Search:
https://forum.amibroker.com/search?q=support%20resistance

Alternatively, if you are looking just for price levels that have been hit less than 10% of the time within last 100 bars, you can do thing like that:

period = 100;  // Period to consider for support and resistance
threshold = 10;  // Percentile threshold

// Calculate support and resistance levels
supportLevel = Percentile(Low, period, threshold);
resistanceLevel = Percentile(High, period, 100 - threshold);

bi = BarIndex();
last = BarCount - 100;

supportLevel = IIf( bi >= last, LastValue( supportLevel ), Null );
resistanceLevel = IIf( bi >= last, LastValue( resistanceLevel ), Null );

Plot( C, "Price", colorDefault, styleCandle );
Plot( supportLevel, "Support", colorGreen );
Plot( resistanceLevel, "Resistance", colorRed );
5 Likes

Another approach using Peak & Trough,

/*
_____________________________________________________________________________________________
|This code is for AmiBroker Formula Language (AFL) learning (non-commercial) purposes only.  |
|Please do not copy this code (or any other version of it) and paste it over on other forums |
|or anywhere else on the Internet or in any other form without the AmiBroker Forum owner's   |
|consent (https://forum.amibroker.com/).                                            		 |
_____________________________________________________________________________________________|
*/

procedure drawLine( x1, y1, x2, y2, color ) {
	GfxSelectPen( color );
	GfxMoveTo( x1, y1 );	
	GfxLineTo( x2, y2 );
}

_SECTION_BEGIN( "Price Chart" );
	SetChartOptions( 1, chartShowDates );
	Plot( C, "Price", colorDefault, styleCandle );
_SECTION_END();

_SECTION_BEGIN( "Potent levels" );
	pertCh = Param( "Level change %", 10, 1, 20, 1 ) / 100;
	
	bi = BarIndex();						lvbi = LastValue( bi );
	fvb = Status( "firstVisibleBar" );		lvb = Status( "lastVisibleBar" );
	
	highs = Peak( H, pertCh );				visibleHighs = HighestSince( bi == fvb, highs );
	lows  = Trough( L, pertCh );			visibleLows  = LowestSince( bi == fvb, lows );
	
	GfxSetCoordsMode( 1 );
	for( i = fvb; i <= lvbi; ++i ) {
		if( visibleHighs[ i ] != visibleHighs[ Max( fvb, i - 1 ) ] ) {
			drawLine(
				x1 = i,		y1 = visibleHighs[ i ],
				x2 = lvb,	y2 = y1,
				color = ColorRGB( 74, 93, 128 )
			);
		}
		if( visibleLows[ i ] != visibleLows[ Max( fvb, i - 1 ) ] ) {
			drawLine(
				x1 = i,		y1 = visibleLows[ i ],
				x2 = lvb,	y2 = y1,
				color = ColorRGB( 45, 63, 92 )
			);
		}
	}
_SECTION_END();
1 Like

i also have a version I made many years ago where I tried something like that, meaning to counts pivots that fall within a zone. The default code requires at least 4 points to fall within a certain zone. The zone width, number of points etc. are all variables you can adjust in the parameter settings. The default settings give the following result for the Oil daily chart (see below). If someone has ideas to improve it let me know.

rightstrength = Param( "Right Strength", 10, 2, 50, 1 );
leftstrength = Param( "Left Strength", 10, 2, 50, 1 );
nlevels = Param( "Number of Levels", 4, 0, 20, 1 );
atrmultiplier = Param( "ATR Multiplier", 0.25, 0, 10, 0.01 );
atrperiod = Param( "ATR Period", 50, 10, 200, 1 );
minpiv = Param( "Minimum number of Pivots inside ATR zone", 4, 2, 10, 1 );
zonedisplay = ParamToggle( "Display of Zone", "Cloud|Line", 1 );
pivnumberdisplay = ParamToggle( "Display Pivot Number in Zone", "Off|On", 1 );

GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );
GfxSelectPen( ColorRGB( 80, 80, 80 ), 2, 0 );
GfxSelectSolidBrush( ColorRGB( 30, 30, 30 ) );
ft = "Arial Black";
sz = 10;
bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );

pk = H == HHV( H, leftstrength ) AND Ref( HHV( H, rightstrength ), rightstrength ) < H;
tr = L == LLV( L, leftstrength ) AND Ref( LLV( L, rightstrength ), rightstrength ) > L;

for( i = 0; i < 4; i++ )
{
    VarSet( "px" + i, ValueWhen( pk, bi, i ) );
    VarSet( "tx" + i, ValueWhen( tr, bi, i ) );
    VarSet( "ph" + i, ValueWhen( pk, H, i ) );
    VarSet( "tl" + i, ValueWhen( tr, L, i ) );
}

ll = tr AND tl1 < tl2;
hl = tr AND tl1 > tl2;
hh = pk AND ph1 > ph2;
lh = pk AND ph1 < ph2;
dt = pk AND ph1 == ph2;
db = tr AND tl1 == tl2;

idxarray = ValueWhen( pk OR tr, bi, 1 );

SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );
Plot( C, "C", colorWhite, styleCandle, Null, Null, 0, 0, 1 );
PlotShapes( shapeSmallCircle * tr, ColorRGB( 0, 255, 0 ), 0, L, -10 );
PlotShapes( shapeSmallCircle * pk, ColorRGB( 255, 0, 0 ), 0, H, 10 );

function drawLevel( cnt, storageval, storageidx, cntlevels, valtop, valbot )
{
    if( pivnumberdisplay )
    {
        clr1 = colorBlack;
        clr2 = colorGold;

        for( k = 0; k < cnt; k++ )
        {
            str = "" + ( k + 1 );
            PlotTextSetFont( str, ft, sz, storageidx[k] - 1, valbot, clr1, clr2, -2 * sz );
        }
    }

    x0 = storageidx[cnt - 1];
    y0 = valbot;
    x1 = storageidx[0];
    y1 = valtop;
    GfxRectangle( x0, y0, x1, y1 );
    linetop = LineArray( x0, y1, x1, y1, 1 );
    linebot = LineArray( x0, y0, x1, y0, 1 );

    if( zonedisplay )
    {
        lineavg = IIf( !IsEmpty( linetop ) && !IsEmpty( linebot ), ( linetop + linebot ) / 2, Null );
        lineavg = IIf( bi >= x1, lineavg, Null );
        y1 = IIf( bi >= x1, y1, Null );
        y0 = IIf( bi >= x1, y0, Null );
        Plot( y0, "", IIf( lineavg > C, ColorRed, ColorGreen ), styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, 0, 1 );
        Plot( y1, "", IIf( lineavg > C, ColorRed, ColorGreen ), styleLine | styleNoLabel | styleNoRescale, Null, Null, 0, 0, 1 );
        clr = LastValue( IIf( lineavg > C, ColorRed, ColorGreen ) );
        avgval = ( y0 + y1 ) / 2;
        PlotTextSetFont( "" + avgval, ft, sz, BarCount + 2, ( y0[BarCount - 1] + y1[BarCount - 1] ) / 2, colorWhite, clr, -sz / 2 );
    }
    else
    {
        lineavg = IIf( !IsEmpty( linetop ) && !IsEmpty( linebot ), ( linetop + linebot ) / 2, Null );
        lineavg = IIf( bi >= x1, lineavg, Null );
        y1 = IIf( bi >= x1, y1, Null );
        y0 = IIf( bi >= x1, y0, Null );
        PlotOHLC( y1, y1, y0, y0, "", IIf( lineavg > C, ColorRed, ColorGreen ), styleCloud | styleNoLabel | styleNoRescale, Null, Null, 0, 0, 1 );
        clr = LastValue( IIf( lineavg > C, ColorRed, ColorGreen ) );
        avgval = ( y0 + y1 ) / 2;
        PlotTextSetFont( "" + avgval, ft, sz, BarCount + 2, ( y0[BarCount - 1] + y1[BarCount - 1] ) / 2, colorWhite, clr, -sz / 2 );
    }

    cntlevels += 1;
    return cntlevels;
}

delta = atrmultiplier * Ref( ATR( atrperiod ), -1 );
cnt = 0;
storageval = 0;
storageidx = 0;
cntlevels = 0;
lpcnt = 0;

for( i = lvb; i > fvb; i-- )
{
    if( pk[i] OR tr[i] )
    {
        if( pk[i] )
        {
            val = ph1[i];
            valtop = val + delta[i];
            valbot = val - delta[i];
            nexti = idxarray[i - 1] + 1;
        }
        else
            if( tr[i] )
            {
                val = tl1[i];
                valtop = val + delta[i];
                valbot = val - delta[i];
                nexti = idxarray[i - 1] + 1;
            }

        storageval[0] = val;
        storageidx[0] = i;
        cnt = 1;

        for( j = i - 1; j > fvb; j-- )
        {
            if( pk[j] && ph1[j] < valtop && ph1[j] > valbot )
            {
                storageval[cnt] = ph1[j];
                storageidx[cnt] = px1[j];
                cnt = cnt + 1;
                j = idxarray[j - 1] + 1;
            }
            else
                if( tr[j] && tl1[j] < valtop && tl1[j] > valbot )
                {
                    storageval[cnt] = tl1[j];
                    storageidx[cnt] = tx1[j];
                    cnt = cnt + 1;
                    j = idxarray[j - 1] + 1;
                }
        }

        // draw level
        if( cnt >= minpiv )
        {
            cntlevels = drawLevel( cnt, storageval, storageidx, cntlevels, valtop, valbot );
            str = "1";
        }

        i = nexti;
    }

    lpcnt = lpcnt + 1;

    if( cntlevels >= nlevels ) break;
}
5 Likes

i just updated the code, was a little error in there

2 Likes

Thank you everyone for your input.

@empottasch Thank you, your solution is probably closest what I am been looking for.

You may be interested in using volume profile to discover S&R.

_SECTION_BEGIN("VP Support Resistance");

filt = Param("Volume Filter (1 = None)", 10, 0, 10);
maxbins = Param("max bins (0=auto)", 0, 0, 1000, 20);
volchg = Param("Volume Change (%)", 30, 10, 90, 5);
maxwidthpct = Param("Width Chart %", 20, 0, 100, 5)*0.01; 
dispvap = ParamToggle("Display VAP", "No|Yes",1);

//////////
BI = BarIndex(); 
fvb = FirstVisibleValue(BI); lvb = LastVisibleValue(BI); 

sv = SelectedValue(BI); if (IsNan(sv) || (sv >= BarCount-30)) sv = fvb;

hh = LastVisibleValue(HHV(H, lvb-sv+1)); 
ll = LastVisibleValue(LLV(L, lvb-sv+1));

if (maxbins <= 0) {
	if (TickSize == 0) {
		bins = 100;
	} else {
		bins = 1 + (hh - ll) / TickSize;
	}
} else {
	if (TickSize > 0) bins = Min(1 + (hh - ll) / TickSize, maxbins);
	else bins = maxbins;
}
if (bins > BarCount) bins = BarCount;

mx = PriceVolDistribution( H, L, Max(V, 1), bins, False, sv, lvb ); 
bins = MxGetSize(mx, 0);

// iterated median filter
function median3(p, q, r) {
	local mini, maxi;
	mini = Min(p, Min(q, r));
	maxi = Max(p, Max(q, r));
	return (p + q + r) - mini - maxi;
}

for(j = 1; j < filt; ++j) {
	for( i = 1; i < bins-1; i++ ) {
		mx[i][1] = median3(mx[i-1][1], mx[i][1], mx[i+1][1]);
	}
	mx[0][1] = median3(mx[0][1], mx[1][1], mx[2][1]); // (mx[0][1]+mx[1][1])*0.5;
	if (bins > 2) mx[bins-1][1] = median3(mx[bins-1][1], mx[bins-2][1], mx[bins-3][1]); // (mx[bins-1][1]+mx[bins-2][1])*0.5;
}
////////// 
VaP = 0;
for(i = 0; i < bins; ++i) VaP[i] = mx[i][1]; 
// Use Trough
T = TroughBars(VaP, volchg) == 0;

///////// Display S/R
GfxSetCoordsMode( 1 ); 
GfxSelectPen( colorBlue, 2 );
for(i = 0; i < bins; ++i) if (T[i]) {
	price = mx[i][0];
	GfxMoveTo( sv, price ); GfxLineTo(lvb, price);
}

////////// Display VaP ?
if (dispvap) {
	GfxSelectPen( colorRed );
	fnb = (lvb-sv+1) * maxwidthpct;
	bins = MxGetSize( mx, 0 ); 
	for( i = 0; i < bins; i++ ) { 
		price = mx[ i ][ 0 ]; // price level 
		GfxMoveTo( sv, price ); 
		GfxLineTo( sv + mx[i][1]*fnb, price ); 
	} 
	GfxSelectPen( colorBlack );
	GfxMoveTo( sv + mx[0][1]*fnb, mx[0][0] ); 
	for( i = 0; i < bins; i++ ) { 
		GfxLineTo( sv + mx[i][1]*fnb, mx[i][0] ); 
	}
}

Plot( C, "", colorDefault, styleBar );

_SECTION_END();

And use @empottasch method to store levels in variables.
Cheers.

3 Likes

thanks. I will study your code.

We were just working on similar code where you can chose the number of trading sessions to create a profile plus the number of sessions to shift back that profile. So in the case/chart below the profile is made from 3 sessions shifted 1 session back so the idea is (in this particular example) to derive S/R from past 3 sessions to be used during the current session. But we also include Buyvolume and Sellvolume. Your levels look interesting, thanks for the code.

Is this a 'rolling' profile or a single one? In the latter case, here's a modified version:

_SECTION_BEGIN("VP Support Resistance");

filt = Param("Volume Filter (1 = None)", 10, 0, 10);
maxbins = Param("max bins (0=auto)", 0, 0, 1000, 20);
volchg = Param("Volume Change (%)", 30, 10, 90, 5);
minpeak = Param("Minimum Peak Height (%)", 20, 0, 100, 5)*0.01;
deeptrough = Param("Deep Trough (%)", 10, 0, 100, 5)*0.01;
timescale = ParamList("Sessions Timescale", "Hour|Day|Week|Month|Year", 1);
sessions = Param("Sessions (0: manual selection)", 3, 0, 23);
backshift = Param("Backshift (days)", 1, 0, 23);

maxwidthpct = Param("Width Chart %", 20, 0, 100, 5)*0.01; 
dispvap = ParamToggle("Display VAP", "No|Yes",1);

//////////
BI = BarIndex(); 
fvb = FirstVisibleValue(BI); lvb = LastVisibleValue(BI); 

if (sessions) {
	switch(timescale) {
		case "Hour": D = Hour(); break;
		case "Day": D = Day(); break;
		case "Month": D = Month(); break;
		case "Year": D = Year(); break;
		default: D = Day(); break;
	}
	New = D != Ref(D, -1);
	fvb = LastValue(ValueWhen(New, BI, sessions + backshift));
	if (backshift) lvb = LastValue(ValueWhen(New, BI, backshift))-1;
	sv = fvb;
} else {
	sv = SelectedValue(BI); if (IsNan(sv) || (sv >= BarCount-30)) sv = fvb;
}

hh = LastVisibleValue(HHV(H, lvb-sv+1)); 
ll = LastVisibleValue(LLV(L, lvb-sv+1));

if (maxbins <= 0) {
	if (TickSize == 0) {
		bins = 100;
	} else {
		bins = 1 + (hh - ll) / TickSize;
	}
} else {
	if (TickSize > 0) bins = Min(1 + (hh - ll) / TickSize, maxbins);
	else bins = maxbins;
}
if (bins > BarCount) bins = BarCount;

mx = PriceVolDistribution( H, L, Max(V, 1), bins, False, sv, lvb ); 
bins = MxGetSize(mx, 0);

// iterated median filter
function median3(p, q, r) {
	local mini, maxi;
	mini = Min(p, Min(q, r));
	maxi = Max(p, Max(q, r));
	return (p + q + r) - mini - maxi;
}

for(j = 1; j < filt; ++j) {
	for( i = 1; i < bins-1; i++ ) {
		mx[i][1] = median3(mx[i-1][1], mx[i][1], mx[i+1][1]);
	}
	mx[0][1] = median3(mx[0][1], mx[1][1], mx[2][1]); // (mx[0][1]+mx[1][1])*0.5;
	if (bins > 2) mx[bins-1][1] = median3(mx[bins-1][1], mx[bins-2][1], mx[bins-3][1]); // (mx[bins-1][1]+mx[bins-2][1])*0.5;
}
////////// 
VaP = 0;
for(i = 0; i < bins; ++i) VaP[i] = mx[i][1]; 
// Use Trough
P = Peak(VaP, volchg);
T = Trough(VaP, volchg);
TB = TroughBars(VaP, volchg);
ValidT = (TB == 0)  AND ( ((P >= minpeak) AND (T <= P*volchg)) OR ( T <= deeptrough ) );

///////// Display S/R
GfxSetCoordsMode( 1 ); 
GfxSelectPen( colorBlue, 2 );
for(i = 0; i < bins; ++i) if (ValidT[i]) {
	price = mx[i][0];
	GfxMoveTo( sv, price ); GfxLineTo(lvb, price);
}

////////// Display VaP ?
if (dispvap) {
	GfxSelectPen( colorRed );
	fnb = (lvb-sv+1) * maxwidthpct;
	bins = MxGetSize( mx, 0 ); 
	for( i = 0; i < bins; i++ ) { 
		price = mx[ i ][ 0 ]; // price level 
		GfxMoveTo( sv, price ); 
		GfxLineTo( sv + mx[i][1]*fnb, price ); 
	} 
	GfxSelectPen( colorBlack );
	GfxMoveTo( sv + mx[0][1]*fnb, mx[0][0] ); 
	for( i = 0; i < bins; i++ ) { 
		GfxLineTo( sv + mx[i][1]*fnb, mx[i][0] ); 
	}
}

Plot( C, "", colorDefault, styleBar );

_SECTION_END();
2 Likes

I forgot:

		case "Week": D0 = DayOfWeek(); D = Cum(D0 < Ref(D0,-1)); break;

1 Like

thanks. Yes that looks similar. I use a separator between days based on the trading session. So for ES and most of the US futures the session starts at 18:00 PM ET and ends the next day at 17:00 PM ET.

So I set these times in the intraday settings and then use as the separator between sessions:

separator = Ref( Nz( TimeFrameExpand( 1, inDaily, expandPoint ) ), -1 );

then I used the SparceCompress function for easy access to start and end points of the profile since SparceCompress uses the same array length but stores all the values at the end of the resulting array.

sep_idx = SparseCompress( separator OR bi == ( BarCount - 1 ), bi );
start_idx = Ref( sep_idx, -( ndays + backshift_days ) );
end_idx = Ref( sep_idx, -backshift_days );

i did not study your code yet. I was interested in the way you calculate the levels since you seem to select them in 'volume valleys". Will study it later

1 Like