AFL for Automatic Trend Lines
This Amibroker script is designed to automate the process of drawing:
1. Trend lines (sloping)
2. Support & resistance lines (horizontal)
3. Channel lines (parallel lines to trend lines & horizontal lines)
Script:
// We only want to draw trend lines where they are within +/-X% of the last close
PctLmt = Param(“% from last Close to draw”, 3, 0, 100, 1, 0);
// Number of breaks allowed before NullBars area
BreaksLimit = Param(“Number of breaks allowed (before NullBars)”, 2, 0, 100, 1, 0);
// Do we deem a price body as a valid “break”? If just the close as a valid break leave No.
OpenBreak = ParamToggle(“Price body considered break?”, “No|Yes”, 0);
// What are the minimum numbers of touches to draw a trend line, being mindful
// that a sloping trend line already has two trend line touches, whereas horizontal
// and channel lines only have one.
TouchesLimit = Param(“Minimum trend line touches”, 2, 0, 10, 1, 0);
// Only look back over the last X number of bars from the right-most edge of
// the chart. By placing a value in here besides 0 it can help speed up the
// computation time spent with processing this script.
LastBars = Param(“Only use last X bars (0 = Use all)”, 0, 0, 10000, 100, 0);
// Ignore all trend line breaks and touches in the last X bars
NullBars = Param(“Ignore touches and breaks in last X bars”, 10, 0, 100, 1, 0);
// Swing level
SwingPoints = Param(“Swing points”, 2, 1, 3, 1, 0 );
// Do you wish to plot sloping trend lines?
PlotTL = ParamToggle(“Plot trend lines?”, “No|Yes”, 1);
// Do you wish to plot horizontal support and resistance lines?
PlotSR = ParamToggle(“Plot sup/res lines?”, “No|Yes”, 1);
// Do you wish to plot sloping channel lines?
PlotChannels = ParamToggle(“Plot channel lines?”, “No|Yes”, 1);
// What colour do you wish to plot for upper lines?
UpperColor = ParamColor(“Upper trend line colours”, ColorRGB( 17,199,251 ) );
// What colour do you wish to plot for lower lines?
LowerColor = ParamColor(“Lower trend line colours”, ColorRGB( 243,126,120 ) );
// Do you wish to plot upper lines?
PlotUpper = ParamToggle(“Plot upper lines?”, “No|Yes”, 1);
// Do you wish to plot lower lines?
PlotLower = ParamToggle(“Plot lower lines?”, “No|Yes”, 1);
/*
* FUNCTIONS
*/
// Determine if the bars passed in form a swing high.
// @param {int} left bar
// @param {int} middle bar
// @param {int} right bar
// @return {boolean} if the three bars have created a swing high is True
function isSwingHigh( left, middle, right ) {
result = False;
if ( H[left] <= H[middle] && H[middle] > H[right] ) result = True;
return result;
}
// Determines if the bars passed in form a swing low.
// @param {int} left bar
// @param {int} middle bar
// @param {int} right bar
// @return {boolean} if the three bars have created a swing low is True
function isSwingLow( left, middle, right ) {
result = False;
if ( L[left] >= L[middle] && L[middle] < L[right] ) result = True;
return result;
}
// This function refines the swing array.
// @param {int[]} array of bar indexes
// @param {boolean} get swing high if True, otherwise get swing lows
// @return {int[]} array of bar indexes
function getSwingArray( swing, isHigh ) {
z = 0;
result = Null;
for ( i = 2; i < BarCount; i += 1 ) {
if ( IsNull( swing[i] ) ) break;
if ( swing[i] < BarCount-LastBars && LastBars > 0 ) continue;
if ( isHigh && isSwingHigh( swing[i-2], swing[i-1], swing[i] ) ) {
result[z] = swing[i-1];
z = z + 1;
} else if ( !isHigh && isSwingLow( swing[i-2], swing[i-1], swing[i] ) ) {
result[z] = swing[i-1];
z = z + 1;
}
}
return result;
}
// This function returns the highest or lowest bar between two bars.
// @param {int} bar1 – the leftmost bar
// @param {int} bar2 – the rightmost bar
// @param {boolean} isHigh – get highest bar if True, otherwise lowest bar
// @return {int} highest or lowest bar
function getBar( bar1, bar2, isHigh ) {
result = bar1;
for ( i = bar1; i <= bar2; i += 1 ) {
if ( isHigh && H[i] >= H[result] ) result = i;
if ( !isHigh && L[i] <= L[result] ) result = i;
}
return result;
}
// This function calculates the slope value.
// @param {int} bar1
// @param {int} bar2
// @param {boolean} isHigh
// @return {double} slope value
function getSlope( bar1, bar2, isHigh ) {
result = -1;
if ( bar1 – bar2 == 0 ) {
result = 0;
} else if ( isHigh ) {
result = ( H[bar1] – H[bar2] ) / ( bar1 – bar2 );
} else if ( !isHigh ) {
result = ( L[bar1] – L[bar2] ) / ( bar1 – bar2 );
}
return result;
}
// This function plots the trend line. Don’t directly call from code,
// pass through the checkTrendline procedure first.
// @param {int} bar1
// @param {int} bar2
// @param {boolean} isHigh
// @param {double} slope
// @param {int} tlType 0 = horizontal; 1 = sloping; -1 = parallel (channel)
// @return {boolean} was trend line plotted?
function plotTrendLine( bar1, bar2, isHigh, slope, tlType ) {
touches = 1; // being i == bar1
breaks = 0;
isPlotted = False;
flag = 0;
for ( i = bar1+1; i < BarCount-NullBars; i += 1 ) {
if ( isHigh ) {
trendlineValue = H[bar1] + ( slope * ( i – bar1 ) );
if ( C[i] > trendlineValue ) {
breaks = breaks + 1;
} else if ( O[i] > trendlineValue && OpenBreak ) {
breaks = breaks + 1;
}
// rather than just being a touch we’re checking that the touch
// was a swing point as trendlines can go through a cluster of price
// points and meet the touch trigger, here we’re refining what’s
// classified as a legitimate “touch”
if ( H[i] >= trendlineValue ) flag = i+1;
if ( flag == i && isSwingHigh( i-2, i-1, i ) ) touches = touches + 1;
} else {
trendlineValue = L[bar1] + ( slope * ( i – bar1 ) );
if ( C[i] < trendlineValue ) {
breaks = breaks + 1;
} else if ( O[i] < trendlineValue && OpenBreak ) {
breaks = breaks + 1;
}
if ( L[i] <= trendlineValue ) flag = i+1;
if ( flag == i && isSwingLow( i-2, i-1, i ) ) touches = touches + 1;
}
if ( breaks > BreaksLimit ) break;
}
if ( breaks <= BreaksLimit && touches >= TouchesLimit ) {
// time to plot the trend line
if ( isHigh ) {
// Plot if parallel (channel) line
if ( tlType == -1 ) {
trendlineArray = LineArray( bar1, H[bar1], bar1+1, H[bar1]+slope, 1 );
Plot( trendlineArray, “ch-up-“+bar1, LowerColor );
}
// Plot if horizontal line
if ( tlType == 0 ) {
trendlineArray = LineArray( bar1, H[bar1], BarCount, H[bar1], 1 );
Plot( trendlineArray, “hz-“+bar1, UpperColor );
}
// Plot if sloping line
if ( tlType == 1 ) {
trendlineArray = LineArray( bar1, H[bar1], bar2, H[bar2], 1 );
Plot( trendlineArray, “tl-up-“+bar1+”.”+bar2, UpperColor );
}
isPlotted = True;
} else {
if ( tlType == -1 ) {
trendlineArray = LineArray( bar1, L[bar1], bar1+1, L[bar1]+slope, 1 );
Plot( trendlineArray, “ch-dn-“+bar1, UpperColor );
}
if ( tlType == 0 ) {
trendlineArray = LineArray( bar1, L[bar1], BarCount, L[bar1], 1 );
Plot( trendlineArray, “hz-“+bar1, LowerColor );
}
if ( tlType == 1 ) {
trendlineArray = LineArray( bar1, L[bar1], bar2, L[bar2], 1 );
Plot( trendlineArray, “tl-dn-“+bar1+”.”+bar2, LowerColor );
}
isPlotted = True;
}
}
return isPlotted;
}
// Here is the main procedure that plots trend lines, however, it’s
// operating as a gatekeeper to help determine whether values inserted
// into the parameters are valid. The types of trend lines passed through
// this function are:
// 1. Sloping trend lines (required = bar1, bar2, isHigh, slope)
// 2. Horizontal support & resistance lines (required = bar1, isHigh, slope)
// 3. Sloping channel lines (required = bar1, bar2, isHigh, slope)
// @param {int} bar1
// @param {int} bar2
// @param {boolean} isHigh
// @param {double} slope
// @param {int} tlType 0 = horizontal; 1 = sloping; -1 = parallel (channel)
procedure checkTrendline( bar1, bar2, isHigh, slope, tlType ) {
if ( IsNull( bar2 ) ) bar2 = 0;
if ( IsNull( slope ) && bar1 == bar2 ) slope = 0;
if ( IsNull( slope ) ) slope = getSlope( bar1, bar2, isHigh );
if ( isHigh ) trendlineEnd = H[bar1] + ( slope * ( BarCount-1 – bar1 ) );
if ( !isHigh ) trendlineEnd = L[bar1] + ( slope * ( BarCount-1 – bar1 ) );
if ( abs( 1 – ( trendlineEnd / LastValue( C ) ) ) * 100 <= PctLmt ) {
if ( plotTrendline( bar1, bar2, isHigh, slope, tlType ) ) {
// Check corresponding channel lines
if ( PlotChannels ) {
channelBar = getBar( bar1, bar2, !isHigh );
plotTrendline( channelBar, channelBar, !isHigh, slope, -1 );
}
}
}
}
/*
* MAIN
* Here is the beginning of the script.
*/
// 1. Initialise our swing points with all bars
swingHighs = BarIndex();
swingLows = BarIndex();
// 2. Recursively loop through the array by obtaining the SwingPoint level needed
for ( i = 0; i < SwingPoints; i += 1 ) {
if ( PlotUpper ) swingHighs = getSwingArray( swingHighs, True );
if ( PlotLower ) swingLows = getSwingArray( swingLows, False );
}
// 3. Count the number of elements in the array
swingHighCount = BarCount – NullCount( swingHighs, 0 );
swingLowCount = BarCount – NullCount( swingLows, 0 );
// 4a. Loop through the swing high array
for ( a = 0; a < swingHighCount; a += 1 ) {
for ( b = a+1; b < swingHighCount; b += 1 ) {
bar1 = swingHighs[a];
bar2 = swingHighs[b];
if ( !IsNull( bar1 ) && !IsNull( bar2 ) ) {
slope = getSlope( bar1, bar2, True );
// Check sloping trend lines
if ( PlotTL ) checkTrendLine( bar1, bar2, True, slope, 1 );
// Check horizontal resistance lines
if ( PlotSR ) checkTrendLine( bar1, bar1, True, 0, 0 );
}
}
}
// 4b. Loop through the swing low array
for ( a = 0; a < swingLowCount; a += 1 ) {
for ( b = a+1; b < swingLowCount; b += 1 ) {
bar1 = swingLows[a];
bar2 = swingLows[b];
if ( !IsNull( bar1 ) && !IsNull( bar2 ) ) {
slope = getSlope( bar1, bar2, False );
// Check sloping trend lines
if ( PlotTL ) checkTrendLine( bar1, bar2, False, slope, 1 );
// Check horizontal resistance lines
if ( PlotSR ) checkTrendLine( bar1, bar1, False, 0, 0 );
}
}
}