//
// "00-ADX_v103.mq4" -- ADX, Average Directional Movement Index
//
//    Ver. 0.01  2008/11/03(Mon)  initial version
//    Ver. 1.00  2008/12/28(Sun)  release version
//    Ver. 1.01  2008/12/29(Mon)  bugfix: MTF doesn't work
//    Ver. 1.02  2008/12/31(Wed)  bugfix: try to draw objs on window -1
//    Ver. 1.03  2010/05/17(Mon)  use buffer instead of line objects
//                                angleHeat/angleOver/bFixedSignal
//                                bAlert/bMail/bFixedSignal
//
//
#property copyright  "00mql4@gmail.com"
#property link       "http://00mql4.blogspot.com/"

//---- indicator settings
#property  indicator_separate_window

#property  indicator_buffers  5

#property  indicator_color1  LightSeaGreen  // 0: ADX
#property  indicator_color2  CLR_NONE       // 1: +DI
#property  indicator_color3  CLR_NONE       // 2: -DI
#property  indicator_color4  Yellow         // 3: heat
#property  indicator_color5  DeepPink       // 4: over

#property  indicator_width1  1
#property  indicator_width2  1
#property  indicator_width3  1
#property  indicator_width4  1
#property  indicator_width5  1

#property  indicator_style1  STYLE_DOT
#property  indicator_style2  STYLE_SOLID
#property  indicator_style3  STYLE_SOLID
#property  indicator_style4  STYLE_SOLID
#property  indicator_style5  STYLE_SOLID

#property indicator_minimum  0

#property indicator_level1  20

//---- defines

//---- input parameters
extern string  sTimeFrame    = "0";    // time frame, string or value
extern int     period        = 14;     // period
extern double  angleHeat     = 0.5;    // angle threshold for Heat
extern double  angleOver     = 1.5;    // angle threshold for Over
extern bool    bSmoothMTF    = true;   // smooth MTF
extern bool    bAlert        = true;   // alert on ADX state change
extern bool    bMail         = false;  // send E-mail
extern bool    bFixedSignal  = true;   // use fixed signal
extern int     nMaxBars      = 20000;  // maximum number of bars to calculate, 0: no limit

//---- buffers
double BufferAdx[];       // 0: ADX
double BufferPlusDi[];    // 1: +DI
double BufferMinusDi[];   // 2: -DI
double BufferHeat[];      // 3: heat
double BufferOver[];      // 4: over
double BufferPlusSdi[];   // 5: tmp
double BufferMinusSdi[];  // 6: tmp
double BufferTmp[];       // 7: tmp

//---- vars
string   sIndicatorName = "";
string   sPrefix        = "";
string   sIndSelf       = "00-ADX_v103";
int      timeFrame;

//----------------------------------------------------------------------
bool isMTF(int timeFrame)
{
    switch (timeFrame) {
    case PERIOD_M1:
    case PERIOD_M5:
    case PERIOD_M15:
    case PERIOD_M30:
    case PERIOD_H1:
    case PERIOD_H4:
    case PERIOD_D1:
    case PERIOD_W1:
    case PERIOD_MN1:
	return(timeFrame != Period());
    }
    
    return(false);
}

//----------------------------------------------------------------------
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("??");
}

//----------------------------------------------------------------------
string toUpper(string s)
{
    int n = StringLen(s);
    for (int i = 0; i < n; i++) {
	int c = StringGetChar(s, i);
	if (c >= 'a' && c <= 'z') {
	    c += 'A' - 'a';
	    s = StringSetChar(s, i, c);
	}
    }
    
    return(s);
}

//----------------------------------------------------------------------
int convTF(string s)
{
    s = toUpper(s);
    
    if (StringFind(s, "MN") >= 0) {
	return(PERIOD_MN1);
    }
    if (StringFind(s, "W1") >= 0) {
	return(PERIOD_W1);
    }
    if (StringFind(s, "D1") >= 0) {
	return(PERIOD_D1);
    }
    if (StringFind(s, "H4") >= 0) {
	return(PERIOD_H4);
    }
    if (StringFind(s, "H1") >= 0) {
	return(PERIOD_H1);
    }
    if (StringFind(s, "M30") >= 0) {
	return(PERIOD_M30);
    }
    if (StringFind(s, "M15") >= 0) {
	return(PERIOD_M15);
    }
    if (StringFind(s, "M5") >= 0) {
	return(PERIOD_M5);
    }
    if (StringFind(s, "M1") >= 0) {
	return(PERIOD_M1);
    }
    
    int tf = StrToDouble(s);
    
    switch (tf) {
    case 0:
    case PERIOD_M1:
    case PERIOD_M5:
    case PERIOD_M15:
    case PERIOD_M30:
    case PERIOD_H1:
    case PERIOD_H4:
    case PERIOD_D1:
    case PERIOD_W1:
    case PERIOD_MN1:
	// ok
	break;
	
    default:
	tf = PERIOD_D1;
	break;
    }
    
    return(tf);
}

//----------------------------------------------------------------------
void init()
{
    timeFrame = convTF(sTimeFrame);
    
    if (timeFrame == 0) {
	timeFrame = Period();
    }
    
    string tf = TimeFrameToStr(timeFrame);
    sIndicatorName = sIndSelf + "(" + tf + "," + period + ")";
    sPrefix = sIndicatorName;
    
    IndicatorShortName(sIndicatorName);
    
    IndicatorBuffers(8);
    
    SetIndexBuffer(0, BufferAdx);
    SetIndexBuffer(1, BufferPlusDi);
    SetIndexBuffer(2, BufferMinusDi);
    SetIndexBuffer(3, BufferHeat);
    SetIndexBuffer(4, BufferOver);
    SetIndexBuffer(5, BufferPlusSdi);
    SetIndexBuffer(6, BufferMinusSdi);
    SetIndexBuffer(7, BufferTmp);
    
    SetIndexLabel(0, "ADX");
    SetIndexLabel(1, "+DI");
    SetIndexLabel(2, "-DI");
    SetIndexLabel(3, "Heat");
    SetIndexLabel(4, "Over");
    
    SetIndexStyle(0, DRAW_LINE);
    SetIndexStyle(1, DRAW_LINE);
    SetIndexStyle(2, DRAW_LINE);
    SetIndexStyle(3, DRAW_LINE);
    SetIndexStyle(4, DRAW_LINE);
    
    int n = period;
    if (nMaxBars > 0) {
	n += Bars - MathMin(Bars, nMaxBars);
    }
    
    SetIndexDrawBegin(0, n);
    SetIndexDrawBegin(1, n);
    SetIndexDrawBegin(2, n);
    SetIndexDrawBegin(3, n);
    SetIndexDrawBegin(4, n);
}

//----------------------------------------------------------------------
double self(int mode, int shift)
{
    double v = iCustom(NULL, timeFrame, sIndSelf,
		       "0",
		       period,
		       angleHeat,
		       angleOver,
		       bSmoothMTF,
		       bAlert,
		       bMail,
		       bFixedSignal,
		       nMaxBars, mode, shift);
    
    return(v);
}

//----------------------------------------------------------------------
void msg(string sMsg, bool bMail)
{
    string tf = TimeFrameToStr(Period());
    string sSub = "[" + sIndSelf + "][" + Symbol() + " " + tf + "]";
    string s = sSub + " " + sMsg;
    Alert(s);
    
    if (bMail) {
	SendMail(sSub, sSub + "\n" + sMsg);
    }
}

//----------------------------------------------------------------------
void checkAlert(double angle,
		double heat0, double heat1, double heat2,
		double over0, double over1, double over2)
{
    static int alertCodeLast;
    
    bool bHeat2 = (heat2 != EMPTY_VALUE);
    bool bOver2 = (over2 != EMPTY_VALUE);
    
    bool bHeat1 = (heat1 != EMPTY_VALUE);
    bool bOver1 = (over1 != EMPTY_VALUE);
    
    bool bHeat0 = (heat0 != EMPTY_VALUE);
    bool bOver0 = (over0 != EMPTY_VALUE);
    
    int alertCode = 0;
    if (bHeat1) alertCode |= 1;
    if (bOver1) alertCode |= 2;
    
    if (!bHeat0 && !bOver0) alertCode = 0;
    
    datetime t0 = Time[0];
    if (alertCode != alertCodeLast) {
	alertCodeLast = alertCode;
	if (bOver0) {
	    msg("Over(" + DoubleToStr(angleOver, 2) + "), angle= " + DoubleToStr(angle, 2), bMail);
	} else {
	    if (bHeat0) {
		msg("Heat(" + DoubleToStr(angleHeat, 2) + "), angle= " + DoubleToStr(angle, 2), bMail);
	    } else {
		msg("trend vanished", bMail);
	    }
	}
    }
}

//----------------------------------------------------------------------
void start()
{
    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--) {
	BufferAdx[i]      = EMPTY_VALUE;
	BufferPlusDi[i]   = EMPTY_VALUE;
	BufferMinusDi[i]  = EMPTY_VALUE;
	BufferHeat[i]     = EMPTY_VALUE;
	BufferOver[i]     = EMPTY_VALUE;
	BufferPlusSdi[i]  = 0;
	BufferMinusSdi[i] = 0;
	BufferTmp[i]      = 0;
    }
    
    double adx3, adx2, adx1, adx0;
    bool bFire;
    
    if (isMTF(timeFrame)) {
	// MTF
	limit = MathMax(limit, timeFrame / Period());
	if (!bSmoothMTF) {
	    for (i = limit - 1; i >= 0; i--) {
		int x = iBarShift(NULL, timeFrame, Time[i]);
		BufferAdx[i]     = self(0, x);
		BufferPlusDi[i]  = self(1, x);
		BufferMinusDi[i] = self(2, x);
		BufferHeat[i]    = self(3, x);
		BufferOver[i]    = self(4, x);
	    }
	} else {
	    static double bufAdx[];
	    static double bufPlusDi[];
	    static double bufMinusDi[];
	    static bool bFirst = true;
	    if (bFirst) {
		bFirst = false;
		ArrayResize(bufAdx, nMaxBars);
		ArrayResize(bufPlusDi, nMaxBars);
		ArrayResize(bufMinusDi, nMaxBars);
		ArraySetAsSeries(bufAdx, true);
		ArraySetAsSeries(bufPlusDi, true);
		ArraySetAsSeries(bufMinusDi, true);
	    }
	    int n = timeFrame / Period();
	    for (i = limit + n - 1; i >= 0; i--) {
		x = iBarShift(NULL, timeFrame, Time[i]);
		bufAdx[i]     = self(0, x);
		bufPlusDi[i]  = self(1, x);
		bufMinusDi[i] = self(2, x);
	    }
	    for (i = limit - 1; i >= 0; i--) {
		BufferAdx[i]     = iMAOnArray(bufAdx, 0, n, 0, MODE_SMA, i);
		BufferPlusDi[i]  = iMAOnArray(bufPlusDi, 0, n, 0, MODE_SMA, i);
		BufferMinusDi[i] = iMAOnArray(bufMinusDi, 0, n, 0, MODE_SMA, i);
	    }
	    for (i = limit - 1; i >= 0; i--) {
		int i1 = i + 1;
		int i0 = i;
		adx1 = BufferAdx[i1];
		adx0 = BufferAdx[i0];
		double v = (adx0 - adx1) * timeFrame / Period();
		if (v >= angleHeat) {
		    BufferHeat[i1] = adx1;
		    BufferHeat[i0] = adx0;
		} else {
		    BufferHeat[i0] = EMPTY_VALUE;
		}
		if (v >= angleOver) {
		    BufferOver[i1] = adx1;
		    BufferOver[i0] = adx0;
		} else {
		    BufferOver[i0] = EMPTY_VALUE;
		}
	    }
	}
	
	int delay;
	if (bFixedSignal) {
	    delay = 1;
	} else {
	    delay = 0;
	}
	
	int x0 = iBarShift(NULL, timeFrame, Time[0]);
	
	double heat0 = self(3, x0 + delay + 0);
	double heat1 = self(3, x0 + delay + 1);
	double heat2 = self(3, x0 + delay + 2);
	
	double over0 = self(4, x0 + delay + 0);
	double over1 = self(4, x0 + delay + 1);
	double over2 = self(4, x0 + delay + 2);
	
	double angle = BufferAdx[0] - BufferAdx[1];
	
	checkAlert(angle, heat0, heat1, heat2, over0, over1, over2);
	
	return;
    }
    
    // timeFrame == Period()
    for (i = limit - 1; i >= 0; i--) {
	double hi1 = High[i + 1];
	double hi0 = High[i + 0];
	double lo1 = Low[i + 1];
	double lo0 = Low[i + 0];
	double close1 = Close[i + 1];
	
	double pdm = MathMax(hi0 - hi1, 0);
	double mdm = MathMax(lo1 - lo0, 0);
	
	if (pdm == mdm) {
	    pdm = 0;
	    mdm = 0;
	} else if (pdm < mdm) {
	    pdm = 0;
	} else {
	    mdm = 0;
	}
	
	double v1 = MathAbs(hi0 - lo0);
	double v2 = MathAbs(hi0 - close1);
	double v3 = MathAbs(lo0 - close1);
	
	double vMax = MathMax(MathMax(v1, v2), v3);
	if (vMax == 0) {
	    BufferPlusSdi[i] = 0;
	    BufferMinusSdi[i] = 0;
	} else {
	    BufferPlusSdi[i] = pdm / vMax * 100;
	    BufferMinusSdi[i] = mdm / vMax * 100;
	}
    }
    
    for (i = limit - 1; i >= 0; i--) {
	BufferPlusDi[i] = iMAOnArray(BufferPlusSdi, 0, period, 0, MODE_EMA, i);
	BufferMinusDi[i] = iMAOnArray(BufferMinusSdi, 0, period, 0, MODE_EMA, i);
	
	double t = MathAbs(BufferPlusDi[i] + BufferMinusDi[i]);
	if (t == 0.0) {
	    BufferTmp[i] = 0;
	} else {
	    BufferTmp[i] = MathAbs(BufferPlusDi[i] - BufferMinusDi[i]) / t * 100;
	}
    }
    
    for (i = limit - 1; i >= 0; i--) {
	BufferAdx[i] = iMAOnArray(BufferTmp, 0, period, 0, MODE_EMA, i);
    }
    
    for (i = limit - 1; i >= 0; i--) {
	i1 = i + 1;
	i0 = i;
	adx1 = BufferAdx[i1];
	adx0 = BufferAdx[i0];
	v = adx0 - adx1;
	if (v >= angleHeat) {
	    BufferHeat[i1] = adx1;
	    BufferHeat[i0] = adx0;
	} else {
	    BufferHeat[i0] = EMPTY_VALUE;
	}
	if (v >= angleOver) {
	    BufferOver[i1] = adx1;
	    BufferOver[i0] = adx0;
	} else {
	    BufferOver[i0] = EMPTY_VALUE;
	}
    }
    
    // alert
    if (bAlert) {
	if (bFixedSignal) {
	    delay = 1;
	} else {
	    delay = 0;
	}
	
	heat0 = BufferHeat[delay + 0] ;
	heat1 = BufferHeat[delay + 1] ;
	heat2 = BufferHeat[delay + 2] ;
	
	over0 = BufferOver[delay + 0];
	over1 = BufferOver[delay + 1];
	over2 = BufferOver[delay + 2];
	
	angle = BufferAdx[0] - BufferAdx[1];
	
	checkAlert(angle, heat0, heat1, heat2, over0, over1, over2);
    }
}
