MACD_Colored_v104_001





//+------------------------------------------------------------------+
//|                                                  Custom MACD.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  Lime
#property  indicator_color2  Red
#property  indicator_color3  Red
#property  indicator_style3  STYLE_DOT
#property  indicator_color4  Silver
#property  indicator_style4  STYLE_SOLID
	
#property  indicator_level1 15
#property  indicator_level2 -15

#property  indicator_levelcolor  Gray
#property  indicator_levelstyle  STYLE_DOT
//---- indicator parameters
extern string Alert_On="15,-15";
extern bool AlertOn = true;
extern bool EMail_Alert=false;
extern int Max_Alerts=1;
extern int Alert_Before_Minutes=5;
extern int Alert_Every_Minutes=5;
extern bool ShowSignal=true;
extern int FastEMA=12;
extern int SlowEMA=26;
extern int SignalSMA=9;
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[]={"15","-15"};
int minlevel[]={15,-15};
int hourlevel[]={15,-15};
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

double MACDNow, MACDPrevious;
static datetime prevtime = 0;

// ======= Alert =========

   if(AlertOn)
   {
      if(prevtime == Time[0]) 
      {
         return(0);
      }
      prevtime = Time[0];
   
      MACDNow = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, MODE_MAIN, 0);
      MACDPrevious = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, MODE_MAIN, 1);
   


      if((MACDNow < -15) && (MACDPrevious > -15)) {
         Alert(Symbol(), " M", Period(), " MACD has crossed below -15");
   
   }
   
      if((MACDNow > 15) && (MACDPrevious < 15)) {
         Alert(Symbol(), " M", Period(), " MACD has crossed above 15");
   
   }
   IndicatorShortName("MACD("+FastEMA+","+SlowEMA+","+SignalSMA+") (Alert on)");
   }
   
// ======= Alert Ends =========


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("");
	if(alertabbr=="15")
		return("MACD above 15");
	return("");
		if(alertabbr=="-15")
		return("MACD below -15");
	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
MACD Histogram


Custom Indicators Used:

Order Management characteristics:

Other Features:

It issuies visual alerts to the screen
It sends emails