//
// "00-MacdDiv_v104.mq4" -- MACD with Divergence display
//
//    Ver. 1.00  2008/12/09(Tue)  initial vemacdon
//    Ver. 1.01  2008/12/11(Thu)  bugfixed: longer range won't be checked,
//                                modified error check(div_err)
//    Ver. 1.02  2008/12/12(Fri)  added check of price line (div_oscErr/priceErr)
//                                getLowerLow()/getHigherHigh()
//    Ver. 1.03  2008/12/24(Wed)  bAlertDiv, bDrawObj, window
//    Ver. 1.04  2009/10/12(Mon)  MACD signal, bAlertDialog
// 
//
#property  copyright  "00 - 00mql4@gmail.com"
#property  link       "http://www.mql4.com/"

//---- defines
#define DodgerBlue2  0xaa6014
#define Crimson2     0x280d93

//---- indicator settings
#property  indicator_separate_window

#property  indicator_buffers  8

#property  indicator_color1  Yellow       // 0: Macd main line
#property  indicator_color2  Orange       // 1: Macd signal line
#property  indicator_color3  Gray         // 2: Macd OsMA line
#property  indicator_color4  DodgerBlue   // 3: Long signal
#property  indicator_color5  Crimson      // 4: Short signal
#property  indicator_color6  Crimson2     // 5: Top peak
#property  indicator_color7  DodgerBlue2  // 6: Bottom peak
#property  indicator_color8  White        // 7: Macd signal (main/signal cross)

#property  indicator_width1  1
#property  indicator_width2  1
#property  indicator_width3  1
#property  indicator_width4  1
#property  indicator_width5  1
#property  indicator_width6  1
#property  indicator_width7  1
#property  indicator_width8  1

#property  indicator_style1  STYLE_SOLID
#property  indicator_style2  STYLE_DOT
#property  indicator_style3  STYLE_SOLID
#property  indicator_style4  STYLE_SOLID
#property  indicator_style5  STYLE_SOLID
#property  indicator_style6  STYLE_SOLID
#property  indicator_style7  STYLE_SOLID
#property  indicator_style8  STYLE_SOLID

//---- indicator parameters
extern int    timeFrame     = 0;      // time frame
extern int    nCheckBars    = 120;    // number of bars to check divergence
extern int    macd_fast     = 12;     // fast EMA
extern int    macd_slow     = 26;     // slow EMA
extern int    macd_signal   = 9;      // signal SMA
extern int    div_mode      = 2;      // 0:MODE_MAIN 1:MODE_SIGNAL 2:OsMA
extern double div_oscErr    = 4.0;    // allowable error along divergence line (oscilator)
extern double div_priceErr  = 0.4;    // allowable error along divergence line (PPS), iATR(nCheckBars)*err
extern bool   bAlertDiv     = false;  // PlaySound() on divergence
extern bool   bAlertCross   = false;  // PlaySound() on Macd main/signal cross
extern bool   bAlertDialog  = false;  // display dialog on alert
extern bool   bDrawObj      = true;   // draw lines on chart/window
extern int    window        = 0;      // specify indicator window explicitly, 0: auto
extern int    nMaxBars      = 20000;  // maximum number of bars to calculate, 0: no limit

//---- indicator buffers
double BufferMacdMain[];    // 0: Macd main line
double BufferMacdSignal[];  // 1: Macd signal line
double BufferMacdOsMA[];    // 2: Macd signal line
double BufferLongDiv[];     // 3: Long signal
double BufferShortDiv[];    // 4: Short signal
double BufferTop[];         // 5: top peak
double BufferBottom[];      // 6: bottom peak
double BufferCross[];       // 7: Macd main/signal cross

//---- vars
string   sIndicatorName;
string   sIndSelf = "00-MacdDiv_v104";
string   sPrefix;
int      g_window;
int      markLong        = 233;
int      markShort       = 234;
int      markDot         = 167;
int      markCross       = 167;
color    colRegularLong  = DodgerBlue;  // ( 30, 144, 255)= 0x1e90ff
color    colRegularShort = Crimson;     // (220,  20,  60)= 0xdc143c
color    colHiddenLong   = DodgerBlue2;
color    colHiddenShort  = Crimson2;
int      wRegular        = 1;
int      wHidden         = 1;
datetime tAlertLast      = 0;

//----------------------------------------------------------------------
string TimeFrameToStr(int timeFrame)
{
    switch (timeFrame) {
    case 1:     return("M1");
    case 5:     return("M5");
    case 15:    return("M15");
    case 30:    return("M30");
    case 60:    return("H1");
    case 240:   return("H4");
    case 1440:  return("D1");
    case 10080: return("W1");
    case 43200: return("MN");
    }
    
    return("??");
}

//----------------------------------------------------------------------
void init()
{
    if (timeFrame == 0) {
	timeFrame = Period();
    }
    
    string tf = TimeFrameToStr(timeFrame);
    sIndicatorName = sIndSelf + "(" + tf + "," + div_mode + "," + macd_fast + "," + macd_slow + "," + macd_signal + ")";
    sPrefix = sIndicatorName;
    
    IndicatorShortName(sIndicatorName);
    
    SetIndexBuffer(0, BufferMacdMain);
    SetIndexBuffer(1, BufferMacdSignal);
    SetIndexBuffer(2, BufferMacdOsMA);
    SetIndexBuffer(3, BufferLongDiv);
    SetIndexBuffer(4, BufferShortDiv);
    SetIndexBuffer(5, BufferTop);
    SetIndexBuffer(6, BufferBottom);
    SetIndexBuffer(7, BufferCross);
    
    SetIndexLabel(0, "Macd main");
    SetIndexLabel(1, "Macd signal");
    SetIndexLabel(2, "Macd OsMA");
    SetIndexLabel(3, "Long signal, divergence");
    SetIndexLabel(4, "Short signal, divergence");
    SetIndexLabel(5, "Top peak");
    SetIndexLabel(6, "Bottom peak");
    SetIndexLabel(7, "Macd main/signal cross");
    
    SetIndexStyle(0, DRAW_LINE);
    SetIndexStyle(1, DRAW_LINE);
    SetIndexStyle(2, DRAW_HISTOGRAM);
    SetIndexStyle(3, DRAW_ARROW);
    SetIndexStyle(4, DRAW_ARROW);
    SetIndexStyle(5, DRAW_ARROW);
    SetIndexStyle(6, DRAW_ARROW);
    SetIndexStyle(7, DRAW_ARROW);
    
    SetIndexArrow(3, markLong);
    SetIndexArrow(4, markShort);
    SetIndexArrow(5, markDot);
    SetIndexArrow(6, markDot);
    SetIndexArrow(7, markCross);
    
    int n = MathMax(macd_fast, macd_slow) + macd_signal;
    
    SetIndexDrawBegin(0, n);
    SetIndexDrawBegin(1, n);
    SetIndexDrawBegin(2, n);
    SetIndexDrawBegin(3, n);
    SetIndexDrawBegin(4, n);
    SetIndexDrawBegin(5, n);
    SetIndexDrawBegin(6, n);
    SetIndexDrawBegin(7, n);
    
    SetLevelValue(0, 0.0);
}

//----------------------------------------------------------------------
void deinit()
{
    int n = ObjectsTotal();
    for (int i = n - 1; i >= 0; i--) {
	string sName = ObjectName(i);
	if (StringFind(sName, sPrefix) == 0) {
	    ObjectDelete(sName);
	}
    }
}

//----------------------------------------------------------------------
void objLine(string sName, int win, datetime ts, double ps, datetime te, double pe, color col,
	     int width = 1, int style = STYLE_SOLID, bool bBack = false, bool bRay = false)
{
    ObjectCreate(sName, OBJ_TREND, win, 0, 0);
    ObjectSet(sName, OBJPROP_TIME1,  ts);
    ObjectSet(sName, OBJPROP_PRICE1, ps);
    ObjectSet(sName, OBJPROP_TIME2,  te);
    ObjectSet(sName, OBJPROP_PRICE2, pe);
    ObjectSet(sName, OBJPROP_COLOR,  col);
    ObjectSet(sName, OBJPROP_WIDTH,  width);
    ObjectSet(sName, OBJPROP_STYLE,  style);
    ObjectSet(sName, OBJPROP_BACK,   bBack);
    ObjectSet(sName, OBJPROP_RAY,    bRay);
}	    

//----------------------------------------------------------------------
void objArrow(string sName, int win, datetime t, double p, int arrow, color col, int width = 1)
{
    ObjectCreate(sName, OBJ_ARROW, win, 0, 0);
    ObjectSet(sName, OBJPROP_TIME1,     t);
    ObjectSet(sName, OBJPROP_PRICE1,    p);
    ObjectSet(sName, OBJPROP_ARROWCODE, arrow);
    ObjectSet(sName, OBJPROP_COLOR,     col);
    ObjectSet(sName, OBJPROP_WIDTH,     width);
}

//----------------------------------------------------------------------
string divModeToStr(int divMode)
{
    switch (div_mode) {
	
    case MODE_MAIN:
	return("MAIN");
	
    case MODE_SIGNAL:
	return("SIGNAL");
    }
    
    return("OsMA");
}

//----------------------------------------------------------------------
double getVal(int i)
{
    switch (div_mode) {
	
    case MODE_MAIN:
	return(BufferMacdMain[i]);
	
    case MODE_SIGNAL:
	return(BufferMacdSignal[i]);
    }
    
    return(BufferMacdOsMA[i]);
}

//----------------------------------------------------------------------
double getLowerPrice(int i)
{
    double hi = High[i];
    double lo = Low[i];
    
    return(MathMin(hi, lo));
}

//----------------------------------------------------------------------
double getHigherPrice(int i)
{
    double hi = High[i];
    double lo = Low[i];
    
    return(MathMax(hi, lo));
}

//----------------------------------------------------------------------
double macdDiv(bool bDrawObj, int mode, int x)
{
    double v = iCustom(NULL, timeFrame, sIndSelf,
		       0,
		       nCheckBars,
		       macd_fast,
		       macd_slow,
		       macd_signal,
		       div_mode,
		       div_oscErr,
		       div_priceErr,
		       false,
		       false,
		       false,
		       bDrawObj,
		       window,
		       nMaxBars,
		       mode, x);
    
    return(v);
}

//----------------------------------------------------------------------
void checkAlert()
{
    if (bAlertDiv || bAlertCross) {
	bool bLongDiv = (BufferLongDiv[0] != EMPTY_VALUE);
	bool bShortDiv = (BufferShortDiv[0] != EMPTY_VALUE);
	bool bFireDiv = (bLongDiv || bShortDiv);
	bool bFireCross = (BufferCross[0] != EMPTY_VALUE);	    
	bool bFire = ((bAlertDiv && bFireDiv) ||
		      (bAlertCross && bFireCross));
	if (bFire && tAlertLast != Time[0]) {
	    if (!bAlertDialog) {
		PlaySound("alert.wav");
	    } else {
		string s = "";
		if (bFireDiv) {
		    string sMode = divModeToStr(div_mode);
		    if (bLongDiv) {
			s = s + "MACD(" + sMode + ") divergence, Long";
		    } else if (bShortDiv) {
			s = s + "MACD(" + sMode + ") divergence, Short";
		    }
		}
		if (bFireCross) {
		    if (s != "") {
			s = s + ", ";
		    }
		    if (BufferMacdMain[1] >= BufferMacdSignal[1]) {
			s = s + "MACD Golden Cross";
		    } else {
			s = s + "MACD Dead Cross";
		    }
		}
		Alert("[" + sIndSelf + "] " + s);
	    }
	    tAlertLast = Time[0];
	}
    }
}

//----------------------------------------------------------------------
void start()
{
    if (window > 0) {
	g_window = window;
    } else {
	g_window = WindowFind(sIndicatorName);
    }
    
    int limit;
    int counted_bars = IndicatorCounted();
    
    if (counted_bars > 0) {
	counted_bars--;
    }
    
    limit = Bars - counted_bars;
    int limit0 = limit;
    if (nMaxBars > 0) {
	limit = MathMin(limit, nMaxBars);
    }
    
    // clear beyond limits
    for (int i = limit0 - 1; i >= limit; i--) {
	BufferMacdMain[i]   = 0;
	BufferMacdSignal[i] = 0;
	BufferMacdOsMA[i]   = 0;
	BufferLongDiv[i]    = EMPTY_VALUE;
	BufferShortDiv[i]   = EMPTY_VALUE;
	BufferTop[i]        = EMPTY_VALUE;
	BufferBottom[i]     = EMPTY_VALUE;
	BufferCross[i]      = 0;
    }
    
    if (timeFrame != Period()) {
	// MTF
	limit = MathMax(limit, timeFrame / Period());
	for (i = limit - 1; i >= 0; i--) {
	    int x = iBarShift(NULL, timeFrame, Time[i]);
	    BufferMacdMain[i]   = macdDiv(bDrawObj,  0, x);
	    BufferMacdSignal[i] = macdDiv(false, 1, x);
	    BufferMacdOsMA[i]   = macdDiv(false, 2, x);
	    BufferLongDiv[i]    = macdDiv(false, 3, x);
	    BufferShortDiv[i]   = macdDiv(false, 4, x);
	    BufferTop[i]        = macdDiv(false, 5, x);
	    BufferBottom[i]     = macdDiv(false, 6, x);
	    BufferCross[i]      = macdDiv(false, 7, x);
	}
	
	checkAlert();
	
	return;
    }
    
    // timeFrame == Period()
    
    double val1, val2, val3, val4;
    for (i = limit - 1; i >= 0; i--) {
	BufferMacdMain[i]   = iMACD(NULL, 0, macd_fast, macd_slow, macd_signal, PRICE_CLOSE, MODE_MAIN, i);
	BufferMacdSignal[i] = iMACD(NULL, 0, macd_fast, macd_slow, macd_signal, PRICE_CLOSE, MODE_SIGNAL, i);
	BufferMacdOsMA[i]   = BufferMacdMain[i] - BufferMacdSignal[i];
	BufferTop[i]        = EMPTY_VALUE;
	BufferBottom[i]     = EMPTY_VALUE;
	BufferCross[i]      = EMPTY_VALUE;
	
	double main2 = BufferMacdMain[i + 2];
	double main1 = BufferMacdMain[i + 1];
	double sig2  = BufferMacdSignal[i + 2];
	double sig1  = BufferMacdSignal[i + 1];
	
	if ((main1 >= sig1 && main2 < sig2) ||
	    (main1 < sig1 && main2 >= sig2)) {
	    BufferCross[i] = (main1 + main2 + sig1 + sig2) * 0.25;
	}
	
	// check top/bottom peak
	val4 = getVal(i + 4);
	val3 = getVal(i + 3);
	val2 = getVal(i + 2);
	val1 = getVal(i + 1);
	
	// top peak
	if (val4 <= val3 && val3 <= val2 && val2 > val1) {
	    BufferTop[i + 2] = val2;
	}
	
	// bottom peak
	if (val4 > val3 && val3 > val2 && val2 <= val1) {
	    BufferBottom[i + 2] = val2;
	}
    }
    
    // search divergence on bottom
    for (int i1 = limit - 1; i1 >= 0; i1--) {
	if (BufferBottom[i1] == EMPTY_VALUE) {
	    continue;
	}
	
	for (int hidden = 0; hidden <= 1; hidden++) {
	    val1 = getVal(i1);
	    double p1 = Close[i1];
	    int i2 = -1;
	    for (int j = 1; j <= nCheckBars; j++) {
		x = i1 + j;
		if (BufferBottom[x] == EMPTY_VALUE) {
		    continue;
		}
		val2 = getVal(x);
		double p2 = Close[x];
		if ((!hidden && (val2 > val1 || p2 < p1)) ||
		    (hidden  && (val2 < val1 || p2 > p1))) {
		    continue;
		}
		bool bExceed = false;
		// check div_oscErr
		for (int k = i1 + 1; k < x; k++) {
		    double s = getVal(k);
		    double pred = val1 + (val2 - val1) * (k - i1) / (x - i1);
		    if (pred - s >= div_oscErr * Point) {
			bExceed = true;
			break;
		    }
		}
		// check div_priceErr
		for (k = i1 + 1; k < x; k++) {
		    s = getLowerPrice(k);
		    pred = p1 + (p2 - p1) * (k - i1) / (x - i1);
		    if (pred - s >= iATR(NULL, 0, nCheckBars, k) * div_priceErr) {
			bExceed = true;
			break;
		    }
		}
		if (!bExceed) {
		    i2 = x;
		}
	    }
	    if (i2 >= 0) {
		val1 = getVal(i1);
		val2 = getVal(i2);
		datetime ts = Time[i1];
		datetime tsSig = Time[i1 - 2];
		datetime te = Time[i2];
		double os = getVal(i1);
		double oe = getVal(i2);
		double ps = Close[i1];
		double psSig = Open[i1 - 2];
		double pe = Close[i2];
		color colLine, colArrow;
		int w;
		if (!hidden) {
		    colLine = colRegularLong;
		    colArrow = colRegularLong;
		    w = wRegular;
		} else {
		    colLine = colHiddenLong;
		    colArrow = colRegularLong;
		    w = wHidden;
		}
		if (bDrawObj) {
		    objLine(sPrefix + "line bo" + ts, g_window, ts, os, te, oe, colLine, w);
		    objLine(sPrefix + "line bp" + ts, 0, ts, ps, te, pe, colLine, w);
		    objArrow(sPrefix + "arrow bp" + tsSig, 0, tsSig, psSig - Point, markLong, colArrow);
		}
		BufferLongDiv[i1 - 2] = os;
	    }
	}
    }
    
    // search divergence on top
    for (i1 = limit - 1; i1 >= 0; i1--) {
	if (BufferTop[i1] == EMPTY_VALUE) {
	    continue;
	}
	
	for (hidden = 0; hidden <= 1; hidden++) {
	    val1 = getVal(i1);
	    p1 = Close[i1];
	    i2 = -1;
	    for (j = 1; j <= nCheckBars; j++) {
		x = i1 + j;
		if (BufferTop[x] == EMPTY_VALUE) {
		    continue;
		}
		val2 = getVal(x);
		p2 = Close[x];
		if ((!hidden && (val2 > val1 || p2 < p1)) ||
		    (hidden  && (val2 < val1 || p2 > p1))) {
		    continue;
		}
		bExceed = false;
		// check div_oscErr
		for (k = i1 + 1; k < x; k++) {
		    s = getVal(k);
		    pred = val1 + (val2 - val1) * (k - i1) / (x - i1);
		    if (s - pred >= div_oscErr * Point) {
			bExceed = true;
			break;
		    }
		}
		// check div_priceErr
		for (k = i1 + 1; k < x; k++) {
		    s = getHigherPrice(k);
		    pred = p1 + (p2 - p1) * (k - i1) / (x - i1);
		    if (s - pred >= iATR(NULL, 0, nCheckBars, k) * div_priceErr) {
			bExceed = true;
			break;
		    }
		}
		if (!bExceed) {
		    i2 = x;
		}
	    }
	    if (i2 >= 0) {
		val1 = getVal(i1);
		val2 = getVal(i2);
		ts = Time[i1];
		tsSig = Time[i1 - 2];
		te = Time[i2];
		os = getVal(i1);
		oe = getVal(i2);
		ps = Close[i1];
		psSig = Open[i1 - 2];
		pe = Close[i2];
		if (!hidden) {
		    colLine = colRegularShort;
		    colArrow = colRegularShort;
		    w = wRegular;
		} else {
		    colLine = colHiddenShort;
		    colArrow = colRegularShort;
		    w = wHidden;
		}
		if (bDrawObj) {
		    objLine(sPrefix + "line to" + ts, g_window, ts, os, te, oe, colLine, w);
		    objLine(sPrefix + "line tp" + ts, 0, ts, ps, te, pe, colLine, w);
		    objArrow(sPrefix + "arrow tp" + tsSig, 0, tsSig, psSig + Point, markShort, colArrow);
		}
		BufferShortDiv[i1 - 2] = os;
	    }
	}
    }
    
    checkAlert();
}
