//+------------------------------------------------------------------+ //| MACD_Colored_v104.mq4 | //+------------------------------------------------------------------+ /* Added variable levels for different currency pairs. Calculated reasonable levels based on Phillip Nel's 4-hr macd strategy, using a EURUSD price of 1.3 and levels of 15/30/45. If the price goes up, we take the levels up with it, and vice versa. Utilizies different levels (*2 for daily, 1/3 for minute) for different TFs, as in v103. Also, I like the border around the histogram area, so I've created a permanent border, and added the ability to change the signal line from it's default of 1 and still keep the border around the histogram (so you can make this a standard 12,26,9 MACD and still have a border around the histogram if you want). Enjoy! - JoshDance, ForexFactory.com */ #property copyright "Copyright © 2007, Herb Spirit, Inc., portions Josh Jones" #property link "http://www.herbspirit.com/mql" #define INDICATOR_NAME "MACD_Colored" #define INDICATOR_VERSION "v104" // JoshDance (forexfactory) added variable levels //---- indicator settings #property indicator_separate_window #property indicator_buffers 4 #property indicator_color1 Blue #property indicator_color2 Red #property indicator_color3 Blue #property indicator_style3 STYLE_SOLID #property indicator_color4 White #property indicator_style4 STYLE_SOLID #property indicator_level1 45 #property indicator_level2 30 #property indicator_level3 15 #property indicator_level4 -15 #property indicator_level5 -30 #property indicator_level6 -45 #property indicator_level7 0 #property indicator_levelcolor Gray #property indicator_levelstyle STYLE_DOT //---- indicator parameters extern string Alert_On=""; extern bool EMail_Alert=false; extern int Max_Alerts=1; extern int Alert_Before_Minutes=15; extern int Alert_Every_Minutes=5; extern bool ShowSignal=true; extern int FastEMA=5; extern int SlowEMA=13; extern int SignalSMA=1; extern int FontSize=8; extern color FontColor=White; //---- indicator buffers double MacdBuffer[]; double MacdBufferUp[]; double MacdBufferDn[]; double SignalBuffer[]; double BorderLine[]; string shortname; datetime alertbartime,nextalerttime; int alertcount; string alerttype[]={"RT","RB","VT","VB","TC","ZB"}; int minlevel[]={5,10,15,-5,-10,-15}; int hourlevel[]={45,30,15,-15,-30,-45}; int daylevel[]={90,60,30,-30,-60,-90}; datetime nextbartime; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { ///////////////////////////// ///////////////////////////// //---- drawing settings SetIndexStyle(0,DRAW_HISTOGRAM); SetIndexStyle(1,DRAW_HISTOGRAM); SetIndexStyle(2,DRAW_LINE); SetIndexStyle(3,DRAW_LINE); /* Okay, so I don't know what's up here--Apparently I can't reference a previously defined variable when initializing another variable, as it says that the variable is already defined... well, no duh, but I'm not trying to REdefine it. Anyway, all I know is, it's not like this in C or C++ or any other language I've used, so I'll just nasty my way around it this time... */ /* This is the base multiplier. Calculated from Phillip Nel's original 15/30/45 levels for EURUSD. So, if 15 is our low level for a typical EURUSD value of 1.3, we say 1.3 * baseMult = 15, and we get baseMult ~= 11.5. Good enough! Doesn't have to be really exact. */ double lastPrice = iClose(NULL,0,1); if (StringFind(Symbol(), "JPY", 0) != -1) { lastPrice = lastPrice / 100; } double baseHourMult = 11.5; double baseDayMult = baseHourMult*2; double baseMinMult = baseHourMult/3; double lowMinLevel = baseMinMult*lastPrice; double lowHourLevel = baseHourMult*lastPrice; double lowDayLevel = baseDayMult*lastPrice; ArrayInitialize(minlevel,0); minlevel[0] = lowMinLevel*3; minlevel[1] = lowMinLevel*2; minlevel[2] = lowMinLevel; minlevel[3] = -lowMinLevel; minlevel[4] = -lowMinLevel*2; minlevel[5] = -lowMinLevel*3; ArrayInitialize(hourlevel,0); hourlevel[0] = lowHourLevel*3; hourlevel[1] = lowHourLevel*2; hourlevel[2] = lowHourLevel; hourlevel[3] = -lowHourLevel; hourlevel[4] = -lowHourLevel*2; hourlevel[5] = -lowHourLevel*3; ArrayInitialize(daylevel,0); daylevel[0] = lowDayLevel*3; daylevel[1] = lowDayLevel*2; daylevel[2] = lowDayLevel; daylevel[3] = -lowDayLevel; daylevel[4] = -lowDayLevel*2; daylevel[5] = -lowDayLevel*3; switch(Period()) { case PERIOD_M1: case PERIOD_M5: case PERIOD_M15: case PERIOD_M30: for(int x=0;x<ArraySize(minlevel);x++) SetLevelValue(x,minlevel[x]); break; case PERIOD_H1: case PERIOD_H4: for(x=0;x<ArraySize(hourlevel);x++) SetLevelValue(x,hourlevel[x]); break; default: for(x=0;x<ArraySize(daylevel);x++) SetLevelValue(x,daylevel[x]); } SetIndexDrawBegin(1,SlowEMA); IndicatorDigits(1); //---- indicator buffers mapping SetIndexBuffer(0,MacdBufferUp); SetIndexBuffer(1,MacdBufferDn); SetIndexBuffer(2,SignalBuffer); SetIndexBuffer(3,BorderLine); //---- name for DataWindow and indicator subwindow label shortname=WindowExpertName(); IndicatorShortName(shortname); SetIndexLabel(0,"MACD Up"); SetIndexLabel(1,"MACD Down"); SetIndexLabel(2,"Signal"); SetIndexLabel(3,"Border"); ArrayResize(MacdBuffer,Bars-SlowEMA); ArraySetAsSeries(MacdBuffer,true); // check input parms ValidateAlertType(); //---- initialization done alertbartime=0; nextalerttime=0; alertcount=0; nextbartime=0; return(0); } int deinit() { string objname=shortname+","+Symbol()+","+Period(); int i; while(i<ObjectsTotal()) { string nextobj=ObjectName(i); if(StringSubstr(nextobj,0,StringLen(objname))==objname) ObjectDelete(nextobj); else i++; } } //+------------------------------------------------------------------+ //| Moving Averages Convergence/Divergence | //+------------------------------------------------------------------+ int start() { int limit; int counted_bars=IndicatorCounted(); //---- last counted bar will be recounted if(Time[0]!=nextbartime) { limit=Bars-SlowEMA; ArrayResize(MacdBuffer,limit); nextbartime=Time[0]; } else limit=MathMin(Bars-SlowEMA,Bars-counted_bars); //---- macd counted in the 1-st buffer for(int i=0;i<limit;i++) { MacdBuffer[i]=(iMA(NULL,0,FastEMA,0,MODE_EMA,PRICE_CLOSE,i)- iMA(NULL,0,SlowEMA,0,MODE_EMA,PRICE_CLOSE,i))/Point; } // macd colored set here bool firstsignal=true; for(i=0;i<limit;i++) { if(MacdBuffer[i]>MacdBuffer[i+1]) { MacdBufferUp[i]=MacdBuffer[i]; MacdBufferDn[i]=0; } else { MacdBufferDn[i]=MacdBuffer[i]; MacdBufferUp[i]=0; } if(ShowSignal||firstsignal) { if(!ShowTops(i)) { if(ShowBottoms(i)) firstsignal=false; } else firstsignal=false; } } //---- signal line counted in the 2-nd buffer for(i=0; i<limit; i++) { SignalBuffer[i]=iMAOnArray(MacdBuffer,Bars,SignalSMA,0,MODE_SMA,i); BorderLine[i]=MacdBuffer[i]; } //---- pips to change color calculation double priMACD=(iMA(NULL,0,FastEMA,0,MODE_EMA,PRICE_CLOSE,1)- iMA(NULL,0,SlowEMA,0,MODE_EMA,PRICE_CLOSE,1))/Point; double close[]; ArrayResize(close,Bars); ArraySetAsSeries(close,true); ArrayCopy(close,Close,0,0,ArraySize(close)); double curMACD=(iMAOnArray(close,0,FastEMA,0,MODE_EMA,0)- iMAOnArray(close,0,SlowEMA,0,MODE_EMA,0))/Point; int pips; if(curMACD<priMACD) { while(curMACD<priMACD) { pips++; close[0]+=Point; curMACD=(iMAOnArray(close,0,FastEMA,0,MODE_EMA,0)- iMAOnArray(close,0,SlowEMA,0,MODE_EMA,0))/Point; } } else { while(curMACD>priMACD) { pips--; close[0]-=Point; curMACD=(iMAOnArray(close,0,FastEMA,0,MODE_EMA,0)- iMAOnArray(close,0,SlowEMA,0,MODE_EMA,0))/Point; } } string objname=shortname+","+Symbol()+","+Period()+",pips"; if(ObjectFind(objname)<0) ObjectCreate(objname,OBJ_TEXT, WindowFind(shortname), Time[0]+Period()*60,MacdBuffer[0]/2); else ObjectMove(objname,0,Time[0]+Period()*60,MacdBuffer[0]/2); if(pips!=0) ObjectSetText(objname,DoubleToStr(pips,0),FontSize,"Courier",FontColor); else ObjectSetText(objname," ",FontSize,"Courier",FontColor); //---- send alerts if(Max_Alerts==0) return(0); string alertmsg; if(!IsAlert(alertmsg)) return(0); alertmsg=Symbol()+","+Period()+" : "+alertmsg; Alert(alertmsg); if(EMail_Alert) SendMail("MACD Colored Alert",TimeToStr(TimeLocal(),TIME_DATE|TIME_SECONDS)+" : "+alertmsg); Print(alertmsg); //---- done return(0); } //+------------------------------------------------------------------+ bool ShowTops(int shift) { // check for basic pattern string objname=SetPatternObjectName(shift); bool basicpattern=(MacdBuffer[shift]<MacdBuffer[shift+1]&& MacdBuffer[shift+2]<MacdBuffer[shift+1]&& MacdBuffer[shift+3]<MacdBuffer[shift+2]); if(!basicpattern) { ObjectDelete(objname); return(false); } double diff2=MathAbs(MacdBuffer[shift+2]-MacdBuffer[shift+3]); double diff1=MathAbs(MacdBuffer[shift+1]-MacdBuffer[shift+2]); double diff0=MathAbs(MacdBuffer[shift]-MacdBuffer[shift+1]); bool roundpattern=(diff2>diff1); if(MacdBuffer[shift+2]!=0) double ratio2=MathAbs(MacdBuffer[shift+3]/MacdBuffer[shift+2]); else ratio2=1000; if(MacdBuffer[shift+1]!=0) double ratio1=MathAbs(MacdBuffer[shift+2]/MacdBuffer[shift+1]); else ratio1=1000; if(MacdBuffer[shift+1]!=0) double ratio0=MathAbs(MacdBuffer[shift]/MacdBuffer[shift+1]); else ratio0=1000; roundpattern=(roundpattern||MathAbs(ratio0-ratio1)>0.1); // 0 and 2 are close to each other double minratio=0.8; if(MacdBuffer[shift+1]<10&&MacdBuffer[shift+1]>-10) minratio=0.6; bool ratioround=(ratio0>minratio&&ratio1>minratio&&ratio2>minratio); bool ratiovtop=(MathAbs(ratio0-ratio1)<0.3); string patname=" "; if(ratiovtop) patname="VT"; // default is v-top if(ratioround&&roundpattern) if(MacdBuffer[shift+1]<5) return(false); else patname="RT"; // round top pattern if(patname==" ") return(false); if(MacdBuffer[shift+1]<3&&MacdBuffer[shift+1]>-3) patname="ZB"; // zero line bounce if(MacdBuffer[shift+1]<=-3) patname="TC"; // trend continue bool strongpattern=(MacdBuffer[shift+4]<MacdBuffer[shift+3]&& MacdBuffer[shift+5]<MacdBuffer[shift+4]&& MacdBuffer[shift+1]>10); if(ObjectFind(objname)<0) { ObjectCreate(objname,OBJ_TEXT, WindowFind(shortname), Time[shift+1],0); } if(strongpattern) ObjectSetText(objname,patname,FontSize+2,"Arial",FontColor); else ObjectSetText(objname,patname,FontSize,"Arial",FontColor); return(true); } bool ShowBottoms(int shift) { // check for basic pattern string objname=SetPatternObjectName(shift); string objdesc=ObjectDescription(objname); bool basicpattern=(MacdBuffer[shift]>MacdBuffer[shift+1]&& MacdBuffer[shift+2]>MacdBuffer[shift+1]&& MacdBuffer[shift+3]>MacdBuffer[shift+2]); if(!basicpattern) { ObjectDelete(objname); return(false); } double diff2=MathAbs(MacdBuffer[shift+2]-MacdBuffer[shift+3]); double diff1=MathAbs(MacdBuffer[shift+1]-MacdBuffer[shift+2]); double diff0=MathAbs(MacdBuffer[shift]-MacdBuffer[shift+1]); bool roundpattern=(diff2>diff1);//&&diff2>diff0); if(MacdBuffer[shift+3]!=0) double ratio2=MathAbs(MacdBuffer[shift+2]/MacdBuffer[shift+3]); else ratio2=1000; if(MacdBuffer[shift+2]!=0) double ratio1=MathAbs(MacdBuffer[shift+1]/MacdBuffer[shift+2]); else ratio1=1000; if(MacdBuffer[shift]!=0) double ratio0=MathAbs(MacdBuffer[shift+1]/MacdBuffer[shift]); else ratio0=1000; roundpattern=(roundpattern||MathAbs(ratio0-ratio1)>0.1); // 0 and 2 are close to each other double minratio=0.8; if(MacdBuffer[shift+1]<10&&MacdBuffer[shift+1]>-10) minratio=0.6; bool ratioround=(ratio0>minratio&&ratio1>minratio&&ratio2>minratio); bool ratiovtop=(MathAbs(ratio0-ratio1)<0.3); string patname=" "; if(ratiovtop) patname="VB"; // default is v-top if(ratioround&&roundpattern) if(MacdBuffer[shift+1]>-5) return(false); else patname="RB"; // round top pattern if(patname==" ") return(false); if(MacdBuffer[shift+1]<3&&MacdBuffer[shift+1]>-3) patname="ZB"; // zero line bounce if(MacdBuffer[shift+1]>=3) patname="TC"; // trend continue bool strongpattern=(MacdBuffer[shift+4]>MacdBuffer[shift+3]&& MacdBuffer[shift+5]>MacdBuffer[shift+4]&& MacdBuffer[shift+1]>10); if(ObjectFind(objname)<0) ObjectCreate(objname,OBJ_TEXT, WindowFind(shortname), Time[shift+1],0); if(strongpattern) ObjectSetText(objname,patname,FontSize+2,"Arial",FontColor); else ObjectSetText(objname,patname,FontSize,"Arial",FontColor); return(true); } bool IsAlert(string& alertmsg) { if(ArraySize(alerttype)==0) return(false); if(alerttype[0]=="") return(false); int shift; if(TimeCurrent()<Time[0]+(Period()-Alert_Before_Minutes)*60) shift=1; string objname=SetPatternObjectName(shift); if(ObjectFind(objname)<0) return(false); string thisalert=StringTrimLeft(StringTrimRight(ObjectDescription(objname))); bool needalert=false; if(alerttype[0]=="ANY") needalert=(thisalert!=""); else { for(int i=0;i<ArraySize(alerttype);i++) { if(alerttype[i]==thisalert) { needalert=true; break; } } } if(alertbartime!=Time[shift]) { nextalerttime=0; alertcount=0; } if(!needalert) return(false); alertbartime=Time[shift]; if(TimeCurrent()>nextalerttime) { if(alertcount<Max_Alerts) { alertcount++; nextalerttime=TimeCurrent()+Alert_Every_Minutes*60; int timetoalert=(TimeCurrent()-Time[shift]-Period()*60)/60; string alertname=SetAlertName(thisalert); if(timetoalert<0) alertmsg=(-1*timetoalert)+" minutes till "+alertname; else if(timetoalert>0) alertmsg=timetoalert+" minutes since "+alertname; else alertmsg=alertname; if(alertcount<Max_Alerts) alertmsg=alertmsg+". Next Alert at "+TimeToStr( nextalerttime+TimeLocal()-TimeCurrent(),TIME_SECONDS); else alertmsg=alertmsg+". This was the last Alert"; return(true); } } return(false); } string SetAlertName(string alertabbr) { if(alertabbr=="RT") return("Round Top"); if(alertabbr=="VT") return("V-Top"); if(alertabbr=="RB") return("Round Bottom"); if(alertabbr=="VB") return("V-Bottom"); if(alertabbr=="TC") return("Trend Continue"); if(alertabbr=="ZB") return("Zero Bounce"); return(""); } string SetPatternObjectName(int shift) { return(shortname+","+Symbol()+","+Period()+","+Time[shift]); } void ValidateAlertType() { StringUpperCase(Alert_On); StringToArray(StringTrimLeft(StringTrimRight(Alert_On)), alerttype,","); } void StringUpperCase(string& input) { for(int i=0;i<StringLen(input);i++) { int char=StringGetChar(input,i); if(char>=97&&char<=122) input=StringSetChar(input,i,char-32); } } void StringToArray(string input, string& output[],string delim) { ArrayResize(output,0); int start=0; while(start<StringLen(input)) { int delpos=StringFind(input,delim,start); if(delpos<0) { string nextelem=StringSubstr(input,start); start=StringLen(input); } else { nextelem=StringSubstr(input,start,delpos-start); start=delpos+1; } ArrayResize(output,ArraySize(output)+1); output[ArraySize(output)-1]=nextelem; } }
Sample
Analysis
Market Information Used:
Series array that contains close prices for each bar
Series array that contains open time of each bar
Indicator Curves created:
Implements a curve of type DRAW_HISTOGRAM
Implements a curve of type DRAW_LINE
Indicators Used:
Moving average indicator
Custom Indicators Used:
Order Management characteristics:
Other Features:
It issuies visual alerts to the screen
It sends emails