Created
July 9, 2012 09:07
-
-
Save currencysecrets/3075277 to your computer and use it in GitHub Desktop.
MQL4: Automated Trend Lines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//+------------------------------------------------------------------+ | |
//| Automated Trend Lines.mq4 | | |
//| Ryan Sheehy, CurrencySecrets.com | | |
//| http://www.currencysecrets.com | | |
//+------------------------------------------------------------------+ | |
#property copyright "Ryan Sheehy, CurrencySecrets.com" | |
#property link "http://www.currencysecrets.com" | |
/* | |
* This script automates the generation and plotting of sloping trend | |
* lines and horizontal trend lines using swing points as reference | |
* points. | |
* v1.0 = MetaTrader 4 version released 20120709 | |
* v1.1 = Trend lines now have the ability to be extended after they have | |
* been broken by setting lineLife variable. | |
* v1.2 = Make trend line EA viewable on the weekends when market is | |
* closed. | |
* v1.2.1 = Delete all objects when deinitialised. | |
* v1.2.2 = Delete all objects when change of bar made. | |
* v2.0 = Operates on time frame of chart, level variable added to determine | |
* amount of swing points used to create turning point. | |
*/ | |
datetime now; | |
double pad; | |
extern int level = 3; // how many swing points to use to form a major turning point: Min = 0 Max = 3 | |
string bot = "^"; // symbol used to designate bottom (trough) | |
string top = "^"; // symbol used to designate top (peak) | |
int fontSize = 10; // size of font for symbols and text | |
int lineLife = 30; // how many bars after a broken line do you want to see? | |
int brk = 2; // number of breaks needed for trend line to be considered broken | |
int tch = 2; // number of touches needed for trend line to be drawn | |
bool body = false; // are candle bodies considered a break? Yes = true, No (only closes) = false | |
string fontFace = "Times New Roman"; // style of font | |
int resColour = Red; | |
int supColour = Blue; | |
int slresColour = LightPink; | |
int slsupColour = LightBlue; | |
//+------------------------------------------------------------------+ | |
//| expert initialization function | | |
//+------------------------------------------------------------------+ | |
int init() | |
{ | |
//---- | |
// remove all objects from the chart | |
// if you draw your own trend lines you may want to delete this | |
ObjectsDeleteAll(0); | |
// if the last bar is greater than the current local time PLUS an offset | |
// then we are exhibiting a weekend or broker holiday | |
// to calculate your offset, subtract your local GMT time by the server. | |
// At present Pepperstone exhibits GMT+3: | |
// https://pepperstone.com/forex-news/mt4-server-time-change-on-12th-march | |
int offset = 7 * 60; // Therefore, my calcs are => 7 hours (x 60 minutes) = GMT+10 (local) - GMT+3 (server) | |
if ( TimeLocal()-TimeCurrent() > (offset / Period()) ) { | |
start(); | |
} | |
//---- | |
return(0); | |
} | |
//+------------------------------------------------------------------+ | |
//| expert deinitialization function | | |
//+------------------------------------------------------------------+ | |
int deinit() | |
{ | |
//---- | |
ObjectsDeleteAll(0); | |
//---- | |
return(0); | |
} | |
//+------------------------------------------------------------------+ | |
//| expert start function | | |
//+------------------------------------------------------------------+ | |
int start() | |
{ | |
//---- | |
pad = MarketInfo(Symbol(), MODE_POINT) * Period(); // add padding for the text display on price | |
// only needs to be done at the change of every TF bar | |
if ( now != Time[0] ) { | |
ObjectsDeleteAll(0); | |
plotLines( brk, body, tch ); | |
now = Time[0]; | |
} | |
//---- | |
return(0); | |
} | |
//+------------------------------------------------------------------+ | |
/* | |
* In this function we will plot our trend lines automatically based | |
* upon identifying swing points. | |
* @param sym string Symbol of currency being analysed | |
* @param TF int TimeFrame of chart being analysed | |
* @param brk int Number of breaks before trend line considered to be null | |
* @param body bool Are opens beyond the trend line considered a break? TRUE = yes | |
* @param tch int How many touches are needed to be considered a significant trend line? | |
* @return void | |
*/ | |
void plotLines( int brk, bool body, int tch ) { | |
int pkArr[1]; // lowest peak array good for weekly+ timeframes | |
int trArr[1]; // good for daily timeframe | |
int pk0A, pk0B, pk0C, pk1A, pk1B, pk1C; // good for 4H timeframe | |
int p = 0; // counters for their respective peak arrays | |
int tr0A, tr0B, tr0C, tr1A, tr1B, tr1C; // good for 4H timeframe | |
int t = 0; // counters for their respective trough arrays | |
double slope,endLine,xLine; | |
for ( int i = 1; i < Bars; i++ ) { | |
if ( High[i+1] > High[i] && High[i+1] >= High[i+2] ) { | |
pk0C = pk0B; | |
pk0B = pk0A; | |
pk0A = i+1; | |
if ( level < 1 ) { | |
if ( p > 0 ) { | |
ArrayResize( pkArr, p+1 ); | |
} | |
pkArr[p] = i+1; | |
ObjectCreate( "Pk@" + pk0A, OBJ_TEXT, 0, Time[pk0A], High[pk0A]+pad); | |
ObjectSetText( "Pk@" + pk0A, top, fontSize, fontFace, resColour); | |
p++; | |
} else if ( pk0C > 0 && High[pk0B] > High[pk0A] && High[pk0B] >= High[pk0C] ) { | |
pk1C = pk1B; | |
pk1B = pk1A; | |
pk1A = pk0B; | |
if ( level < 2 ) { | |
if ( p > 0 ) { | |
ArrayResize( pkArr, p+1 ); | |
} | |
pkArr[p] = pk0B; | |
ObjectCreate( "Pk@" + pk0B , OBJ_TEXT, 0, Time[pk0B], High[pk0B]+pad); | |
ObjectSetText( "Pk@" + pk0B, top, fontSize, fontFace, resColour ); | |
p++; | |
} else if ( pk1C > 0 && High[pk1B] > High[pk1A] && High[pk1B] >= High[pk1C] ) { | |
if ( p > 0 ) { | |
ArrayResize( pkArr, p+1 ); | |
} | |
pkArr[p] = pk1B; | |
//Print( sym + " p " + pk0C ); | |
// any conditions put in here | |
ObjectCreate( "Pk@" + pk1B , OBJ_TEXT, 0, Time[pk1B], High[pk1B]+pad); | |
ObjectSetText( "Pk@" + pk1B, top, fontSize, fontFace, resColour ); | |
p++; | |
} | |
} | |
} | |
if ( Low[i+1] < Low[i] && Low[i+1] <= Low[i+2] ) { | |
tr0C = tr0B; | |
tr0B = tr0A; | |
tr0A = i+1; | |
if ( level < 1 ) { | |
if ( t > 0 ) { | |
ArrayResize( trArr, t+1 ); | |
} | |
trArr[t] = i+1; | |
ObjectCreate( "Tr@" + tr0A, OBJ_TEXT, 0, Time[tr0A], High[tr0A]-pad); | |
ObjectSetText( "Tr@" + tr0A, bot, fontSize, fontFace, supColour ); | |
t++; | |
} else if ( tr0C > 1 && Low[tr0B] < Low[tr0A] && Low[tr0B] <= Low[tr0C] ) { | |
tr1C = tr1B; | |
tr1B = tr1A; | |
tr1A = tr0B; | |
if ( level < 2 ) { | |
if ( t > 0 ) { | |
ArrayResize( trArr, t+1 ); | |
} | |
trArr[t] = tr0B; | |
ObjectCreate( "Tr@" + tr0B, OBJ_TEXT, 0, Time[tr0B], High[tr0B]-pad); | |
ObjectSetText( "Tr@" + tr0B, bot, fontSize, fontFace, supColour ); | |
t++; | |
} else if ( tr1C > 0 && Low[tr1B] < Low[tr1A] && Low[tr1B] <= Low[tr1C] ) { | |
if ( t > 0 ) { | |
ArrayResize(trArr, t+1); | |
} | |
trArr[t] = tr1B; | |
// any conditions go here | |
ObjectCreate( "Tr@" + tr1B , OBJ_TEXT, 0, Time[tr1B], Low[tr1B]-pad); | |
ObjectSetText( "Tr@" + tr1B, bot, fontSize, fontFace, supColour ); | |
t++; | |
} | |
} | |
} | |
} // end for-bars loop | |
// now that we have obtained our swing points let's create our sloping and | |
// horizontal trendlines by going through each point. | |
// we'll loop through the highest PEAK array first | |
string u; | |
int x, a, j; | |
if ( ArraySize(pkArr) > 1 ) { | |
// we will sort the array so that the closest bars are at the end of the array | |
ArraySort( pkArr, WHOLE_ARRAY, 0, MODE_DESCEND ); | |
a = ArraySize(pkArr); | |
for ( i = 0; i < a; i++ ) { | |
u = countHCrosses(pkArr[i], brk, 0, true, body); | |
t = StrToInteger(StringSubstr(u, 0, StringFind(u,","))); | |
x = StrToInteger(StringSubstr(u, StringFind(u,",")+1, StringLen(u)-StringFind(u,",")-1)); | |
if ( t > tch && x <= lineLife ) { | |
ObjectCreate( "Res@" + pkArr[i],OBJ_TREND,0,Time[pkArr[i]],High[pkArr[i]],Time[x],High[pkArr[i]] ); | |
ObjectSet( "Res@" + pkArr[i], OBJPROP_STYLE, STYLE_SOLID ); | |
ObjectSet( "Res@" + pkArr[i], OBJPROP_COLOR, resColour ); | |
ObjectCreate( "ResText@" + pkArr[i], OBJ_TEXT, 0, Time[pkArr[i]], High[pkArr[i]]+pad); | |
ObjectSetText( "ResText@" + pkArr[i], DoubleToStr(High[pkArr[i]],MarketInfo(Symbol(),MODE_DIGITS)), fontSize, fontFace, resColour ); | |
Print( Symbol() + " res@ " + DoubleToStr(High[pkArr[i]],MarketInfo(Symbol(),MODE_DIGITS)) + " t:" + tch + " dist:" + | |
DoubleToStr( (High[pkArr[i]]-MarketInfo(Symbol(), MODE_ASK))/(MarketInfo(Symbol(),MODE_POINT)*10), 1 )); | |
if ( x > 0 ) { | |
ObjectSet( "Res@" + pkArr[i], OBJPROP_RAY, false ); | |
ObjectCreate( "BrokenRes@" + pkArr[i],OBJ_TREND,0,Time[x],High[pkArr[i]],Time[0],High[pkArr[i]] ); | |
ObjectSet( "BrokenRes@" + pkArr[i], OBJPROP_RAY, true ); | |
ObjectSet( "BrokenRes@" + pkArr[i], OBJPROP_STYLE, STYLE_DOT ); | |
ObjectSet( "BrokenRes@" + pkArr[i], OBJPROP_COLOR, resColour ); | |
} | |
} | |
// here we will be checking against sloping trend lines | |
for ( j = i + 1; j < a; j++ ) { | |
u = countSlopingCrosses(pkArr[i], pkArr[j], brk, 0, true, body); | |
t = StrToInteger(StringSubstr(u, 0, StringFind(u,","))); | |
x = StrToInteger(StringSubstr(u, StringFind(u,",")+1, StringLen(u)-StringFind(u,",")-1)); | |
if ( t > tch && x <= lineLife ) { | |
slope = (High[pkArr[i]] - High[pkArr[j]]) / (pkArr[i] - pkArr[j]); | |
endLine = (slope * (0 - pkArr[i])) + High[pkArr[i]]; | |
xLine = (slope * (x - pkArr[i])) + High[pkArr[i]]; | |
ObjectCreate( "SlopeRes@" + pkArr[i],OBJ_TREND,0,Time[pkArr[i]],High[pkArr[i]],Time[x],xLine ); | |
ObjectSet( "SlopeRes@" + pkArr[i], OBJPROP_STYLE, STYLE_SOLID ); | |
ObjectSet( "SlopeRes@" + pkArr[i], OBJPROP_COLOR, slresColour ); | |
Print( Symbol() + " res@ " + DoubleToStr(endLine,MarketInfo(Symbol(),MODE_DIGITS)) + " t:" + tch + " dist:" + | |
DoubleToStr( (endLine-MarketInfo(Symbol(), MODE_ASK))/(MarketInfo(Symbol(),MODE_POINT)*10), 1 )); | |
if ( x > 0 ) { | |
ObjectSet( "SlopeRes@" + pkArr[i], OBJPROP_RAY, false ); | |
ObjectCreate( "BrokenSlopeRes@" + pkArr[i],OBJ_TREND,0,Time[x],xLine,Time[0],endLine ); | |
ObjectSet( "BrokenSlopeRes@" + pkArr[i], OBJPROP_RAY, true ); | |
ObjectSet( "BrokenSlopeRes@" + pkArr[i],OBJPROP_STYLE, STYLE_DOT ); | |
ObjectSet( "BrokenSlopeRes@" + pkArr[i],OBJPROP_COLOR, slresColour ); | |
} | |
} | |
} | |
} | |
} | |
// now we'll loop through the TROUGH array | |
if ( ArraySize(trArr) > 1 ) { | |
ArraySort( trArr, WHOLE_ARRAY, 0, MODE_DESCEND ); | |
a = ArraySize(trArr); | |
for ( i = 0; i < a; i++ ) { | |
u = countHCrosses(trArr[i], brk, 0, false, body); | |
t = StrToInteger(StringSubstr(u, 0, StringFind(u,","))); | |
x = StrToInteger(StringSubstr(u, StringFind(u,",")+1, StringLen(u)-StringFind(u,",")-1)); | |
if ( t > tch && x <= lineLife ) { | |
ObjectCreate( "Sup@" + trArr[i],OBJ_TREND,0,Time[trArr[i]],Low[trArr[i]],Time[x],Low[trArr[i]] ); | |
ObjectSet( "Sup@" + trArr[i], OBJPROP_STYLE, STYLE_SOLID ); | |
ObjectSet( "Sup@" + trArr[i], OBJPROP_COLOR, supColour ); | |
ObjectCreate( "SupText@" + trArr[i],OBJ_TEXT,0,Time[trArr[i]],Low[trArr[i]]-pad); | |
ObjectSetText( "SupText@" + trArr[i], DoubleToStr(Low[trArr[i]],MarketInfo(Symbol(),MODE_DIGITS)), fontSize, fontFace, supColour ); | |
ObjectSet( "SupText@" + trArr[i], OBJPROP_COLOR, supColour ); | |
Print( Symbol() + " sup@ " + DoubleToStr(Low[trArr[i]],MarketInfo(Symbol(),MODE_DIGITS)) + " t:" + tch + " dist:" + | |
DoubleToStr( (MarketInfo(Symbol(), MODE_BID)-Low[trArr[i]])/(MarketInfo(Symbol(),MODE_POINT)*10), 1 )); | |
if ( x > 0 ) { | |
ObjectSet( "Sup@" + trArr[i], OBJPROP_RAY, false ); | |
ObjectCreate( "BrokenSup@" + trArr[i],OBJ_TREND,0,Time[x],Low[trArr[i]],Time[0],Low[trArr[i]] ); | |
ObjectSet( "BrokenSup@" + trArr[i], OBJPROP_RAY, true ); | |
ObjectSet( "BrokenSup@" + trArr[i], OBJPROP_STYLE, STYLE_DOT ); | |
ObjectSet( "BrokenSup@" + trArr[i], OBJPROP_COLOR, supColour ); | |
} | |
} | |
// here we will be checking against sloping trend lines | |
for ( j = i + 1; j < a; j++ ) { | |
u = countSlopingCrosses(trArr[i], trArr[j], brk, 0, false, body); | |
t = StrToInteger(StringSubstr(u, 0, StringFind(u,","))); | |
x = StrToInteger(StringSubstr(u, StringFind(u,",")+1, StringLen(u)-StringFind(u,",")-1)); | |
if ( t > tch && x <= lineLife ) { | |
slope = (Low[trArr[i]] - Low[trArr[j]]) / (trArr[i] - trArr[j]); | |
endLine = (slope * (0 - trArr[i])) + Low[trArr[i]]; | |
xLine = (slope * (x - trArr[i])) + Low[trArr[i]]; | |
ObjectCreate( "SlopeSup@" + trArr[i],OBJ_TREND,0,Time[trArr[i]],Low[trArr[i]],Time[x],xLine ); | |
ObjectSet( "SlopeSup@" + trArr[i], OBJPROP_STYLE, STYLE_SOLID ); | |
ObjectSet( "SlopeSup@" + trArr[i], OBJPROP_COLOR, slsupColour ); | |
Print( Symbol() + " sup@ " + DoubleToStr(endLine,MarketInfo(Symbol(),MODE_DIGITS)) + " t:" + tch + " dist:" + | |
DoubleToStr( (MarketInfo(Symbol(), MODE_BID)-endLine)/(MarketInfo(Symbol(),MODE_POINT)*10), 1 )); | |
if ( x > 0 ) { | |
ObjectSet( "SlopeSup@" + trArr[i], OBJPROP_RAY, false ); | |
ObjectCreate( "BrokenSlopeSup@" + trArr[i],OBJ_TREND,0,Time[x],xLine,Time[0],endLine ); | |
ObjectSet( "BrokenSlopeSup@" + trArr[i], OBJPROP_RAY, true ); | |
ObjectSet( "BrokenSlopeSup@" + trArr[i],OBJPROP_STYLE, STYLE_DOT ); | |
ObjectSet( "BrokenSlopeSup@" + trArr[i],OBJPROP_COLOR, slsupColour ); | |
} | |
} | |
} | |
} | |
} | |
} | |
/* | |
* With this function we will count how many crosses and how many touches there have | |
* been on an horizontal trend line. If the quantity of crosses exceeds the number of | |
* breaks we will return the last bar when crosses == brk, otherwise we will return | |
* 0, meaning the trend line is still alive. | |
* @param sym string Symbol of the currency being analysed | |
* @param TF int Timeframe of the symbol being analysed | |
* @param fromBar int Starting point - going all the way to 1 NOT 0 | |
* @param brk int Max number of breaks allowed | |
* @param rng double Allowable distance H/L price can be considered touched/broken (not used on breaks) | |
* @param pk bool If line is resistance make it "true" otherwise "false" | |
* @param body bool Is the body of a candle considered to be a cross? TRUE = yes, FALSE = no | |
* @return string touches,crosses | |
* TODO - add capacity to signal/colour bars responsible for touching (may not be evident with rng) | |
*/ | |
string countHCrosses(int fromBar, int brk, double rng, bool pk, bool body) { | |
int t, x, lastCross = 0; | |
bool flag; | |
for ( int i = fromBar; i > 0; i-- ) { | |
flag = true; | |
if ( pk == true ) { | |
// watching the highs | |
if ( High[i] + rng >= High[fromBar] ) { | |
t++; | |
} | |
if ( body == true && Open[i] > High[fromBar] ) { | |
flag = false; // need to make sure we don't double count if the close has broken above too! | |
x++; | |
} | |
if ( flag == true && Close[i] > High[fromBar] ) { | |
x++; | |
} | |
} else { | |
// watching the lows | |
if ( Low[i] - rng <= Low[fromBar] ) { | |
t++; | |
} | |
if ( body == true && Open[i] < Low[fromBar] ) { | |
flag = false; | |
x++; | |
} | |
if ( flag == true && Close[i] < Low[fromBar] ) { | |
x++; | |
} | |
} | |
if ( x > brk && brk > 0 ) { | |
lastCross = i; | |
break; | |
} | |
} // end for-bars loop | |
return( StringConcatenate(t , "," , lastCross) ); | |
} | |
/* | |
* With this function we will count how many crosses and how many touches there have | |
* been on a SLOPING trend line. | |
* @param sym string Symbol of the currency being analysed | |
* @param TF int Timeframe of the symbol being analysed | |
* @param fromBar int Starting point - going all the way to 1 NOT 0 | |
* @param toBar int Ending point of the trend line | |
* @param brk int Max number of breaks allowed | |
* @param rng double Allowable distance H/L price can be considered touched/broken (not used on breaks) | |
* @param pk bool If line is resistance make it "true" otherwise "false" | |
* @param body bool Is the body of a candle considered to be a cross? TRUE = yes, FALSE = no | |
* @return string touches,crosses | |
* TODO - add capacity to signal/colour bars responsible for touching (may not be evident with rng) | |
*/ | |
string countSlopingCrosses(int fromBar, int toBar, int brk, double rng, bool pk, bool body) { | |
int t, x, lastCross = 0; | |
bool flag; | |
double slope, val; | |
// obtain the slope of the trend line | |
if ( pk == true ) { | |
slope = ((High[fromBar] - High[toBar])/(fromBar - toBar)); | |
} else { | |
slope = ((Low[fromBar] - Low[toBar])/(fromBar - toBar)); | |
} | |
for ( int i = fromBar; i > 0; i-- ) { | |
flag = true; | |
if ( pk == true ) { | |
// calculate the value of the trend line at "i" | |
val = (slope * (i - fromBar)) + High[fromBar]; | |
if ( High[i] + rng >= val ) { | |
t++; | |
} | |
if ( body == true && Open[i] > val ) { | |
flag = false; // need to make sure we don't double count if the close has broken above too! | |
x++; | |
} | |
if ( flag == true && Close[i] > val ) { | |
x++; | |
} | |
} else { | |
// calculate it's current value at bar "i" | |
val = (slope * (i - fromBar)) + Low[fromBar]; | |
if ( Low[i] - rng <= val ) { | |
t++; | |
} | |
if ( body == true && Open[i] < val ) { | |
flag = false; | |
x++; | |
} | |
if ( flag == true && Close[i] < val ) { | |
x++; | |
} | |
} | |
if ( x > brk && brk > 0 ) { | |
lastCross = i; | |
break; | |
} | |
} // end for-bars loop | |
return( StringConcatenate(t , "," , lastCross) ); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment