//
//  "00-BollingerBands_v104.mq4"  -- draw Bolinger Band with range check
//
//    Ver. 0.01  2008/09/26(Fri)  initial version
//    Ver. 1.00  2008/12/23(Tue)  added range/sigmaAlt/maMethodXX
//    Ver. 1.01  2009/01/14(Wed)  added bAlertSigma/bAlertSigmaAlt/bAlertCenter, alert on crossing lines
//    Ver. 1.02  2009/02/08(Sun)  added bAlertRange
//    Ver. 1.03  2009/02/14(Sat)  bugfixed: not only owner objects are deleted on deinit()
//    Ver. 1.04  2009/02/24(Tue)  bugfixed: MTF hangs up
//
#property  copyright "00"
#property  link      "http://www.mql4.com/"

//---- defines
#define ST_NONE   0
#define ST_UPPER  1
#define ST_MID    2
#define ST_LOWER  3

//---- indicator settings
#property indicator_chart_window

#property indicator_buffers  7

#property indicator_color1  Yellow  // 0: MODE_MAIN, center                             
#property indicator_color2  Green   // 1: MODE_UPPER, upper                             
#property indicator_color3  Green   // 2: MODE_LOWER, lower                             
#property indicator_color4  Green   // 3: MODE_UPPER, upper                             
#property indicator_color5  Green   // 4: MODE_LOWER, lower                             
#property indicator_color6  Aqua    // 5: EMPTY_VALUE except abs(upper - lower) >= range
#property indicator_color7  Aqua    // 46: EMPTY_VALUE except abs(upper - lower) >= range

#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_style1  STYLE_SOLID
#property indicator_style2  STYLE_DOT
#property indicator_style3  STYLE_DOT
#property indicator_style4  STYLE_DOT
#property indicator_style5  STYLE_DOT
#property indicator_style6  STYLE_DASHDOTDOT
#property indicator_style7  STYLE_DASHDOTDOT

//---- indicator parameters
extern int    timeFrame       = 0;            // time frame
extern int    nPeriod         = 20;           // bars in timeFrame
extern double sigma           = 2.0;          // sigma #1
extern double sigmaAlt        = 3.0;          // sigma #2
extern double range           = 100.0;        // draw dots where abs(upper - lower) >= range
extern int    appliedPrice    = PRICE_CLOSE;  // 0:CLOSE 1:OPEN 2:HIGH 3:LOW 4:MEDIAN 5:TYPICAL 6:WEIGHTED
extern int    maMethodCenter  = MODE_SMA;     // ma_method for center line, 0:SMA 1:EMA 2:SMMA 3:LWMA
extern int    maMethodBand    = MODE_SMA;     // ma_method for iStd(), 0:SMA 1:EMA 2:SMMA 3:LWMA
extern bool   bAlertSigma     = false;        // alert on crossing sigma line
extern bool   bAlertSigmaAlt  = false;        // alert on crossing sigmaAlt line
extern bool   bAlertCenter    = false;        // alert on crossing center(ave) line
extern bool   bAlertRange     = false;        // alert when abs(upper - lower) >= range
extern int    markRangeUpper  = 233;          // range mark, upper
extern int    markRangeLower  = 233;          // range mark, lower
extern color  colRange        = Aqua;         // range color
extern int    wRange          = 0;            // range mark width
extern int    nMaxBars        = 2000;         // maximum number of bars to calculate, 0: no limit

//---- buffers
double BufferAve[];         // 0: MODE_MAIN, center
double BufferUpperLine1[];  // 1: MODE_UPPER, upper
double BufferLowerLine1[];  // 2: MODE_LOWER, lower
double BufferUpperLine2[];  // 1: more upper
double BufferLowerLine2[];  // 2: more lower
double BufferUpperRange[];  // 3: EMPTY_VALUE except abs(upper - lower) >= range
double BufferLowerRange[];  // 4: EMPTY_VALUE except abs(upper - lower) >= range

//---- vars
string   sIndicatorName  = "";
string   sIndSelf        = "00-BollingerBands_v104";
string   sPrefix         = "";
int      stSigma         = ST_NONE;
int      stSigmaAlt      = ST_NONE;
int      stCenter        = ST_NONE;
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 + "," + nPeriod + ")";
    sPrefix = sIndicatorName;
    
    IndicatorShortName(sIndicatorName);
    
    SetIndexBuffer(0, BufferAve);
    SetIndexBuffer(1, BufferUpperLine1);
    SetIndexBuffer(2, BufferLowerLine1);
    SetIndexBuffer(3, BufferUpperLine2);
    SetIndexBuffer(4, BufferLowerLine2);
    SetIndexBuffer(5, BufferUpperRange);
    SetIndexBuffer(6, BufferLowerRange);
    
    SetIndexStyle(0, DRAW_LINE);
    SetIndexStyle(1, DRAW_LINE);
    SetIndexStyle(2, DRAW_LINE);
    SetIndexStyle(3, DRAW_LINE);
    SetIndexStyle(4, DRAW_LINE);
    SetIndexStyle(5, DRAW_LINE);
    SetIndexStyle(6, DRAW_LINE);
    
    int n = nPeriod * timeFrame / Period();
    SetIndexDrawBegin(0, n);
    SetIndexDrawBegin(1, n);
    SetIndexDrawBegin(2, n);
    SetIndexDrawBegin(3, n);
    SetIndexDrawBegin(4, n);
    SetIndexDrawBegin(5, n);
    SetIndexDrawBegin(6, n);
}

//----------------------------------------------------------------------
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 checkAlert()
{
    double ave = BufferAve[0];
    double upper1 = BufferUpperLine1[0];
    double lower1 = BufferLowerLine1[0];
    double upper2 = BufferUpperLine2[0];
    double lower2 = BufferLowerLine2[0];
    
    if (bAlertSigma || bAlertSigmaAlt || bAlertCenter) {
	double p = Close[0];
	//double ave = BufferAve[0];
	int st;
	bool bFire1, bFire2, bFire3;
	
	if (!bAlertSigma) {
	    bFire1 = false;
	} else {
	    if (p >= upper1) {
		st = ST_UPPER;
	    } else if (p > lower1) {
		st = ST_MID;
	    } else {
		st = ST_LOWER;
	    }
	    bFire1 = (st != stSigma);
	    stSigma = st;
	}
	
	if (!bAlertSigmaAlt) {
	    bFire2 = false;
	} else {
	    if (p >= upper2) {
		st = ST_UPPER;
	    } else if (p > lower2) {
		st = ST_MID;
	    } else {
		st = ST_LOWER;
	    }
	    bFire2 = (st != stSigmaAlt);
	    stSigmaAlt = st;
	}
	
	if (!bAlertCenter) {
	    bFire3 = false;
	} else {
	    if (p >= ave) {
		st = ST_UPPER;
	    } else {
		st = ST_LOWER;
	    }
	    bFire3 = (st != stCenter);
	    stCenter = st;
	}
	
	bool bFire = (bFire1 || bFire2 || bFire3);
	if (bFire) {
	    PlaySound("alert.wav");
	}
    }
}

//----------------------------------------------------------------------
void objArrow(string sName, int win, datetime t, double p, int arrow, color col, int width = 1)
{
    sName = sPrefix + sName;
    
    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);
}

//----------------------------------------------------------------------
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--) {
	BufferAve[i]        = EMPTY_VALUE;
	BufferUpperLine1[i] = EMPTY_VALUE;
	BufferLowerLine1[i] = EMPTY_VALUE;
	BufferUpperLine2[i] = EMPTY_VALUE;
	BufferLowerLine2[i] = EMPTY_VALUE;
	BufferUpperRange[i] = EMPTY_VALUE;
	BufferLowerRange[i] = EMPTY_VALUE;
    }
    
    if (timeFrame != Period()) {
	// MTF
	limit = MathMax(limit, timeFrame / Period());
	for (i = limit - 1; i >= 0; i--) {
	    int x = iBarShift(NULL, timeFrame, Time[i]);
	    BufferAve[i]        = iCustom(NULL, timeFrame, sIndSelf, 0, nPeriod, sigma, sigmaAlt, range,
					  appliedPrice, maMethodCenter, maMethodBand, false, false, false, false,
					  markRangeUpper, markRangeLower, colRange, wRange, nMaxBars, 0, x);
	    BufferUpperLine1[i] = iCustom(NULL, timeFrame, sIndSelf, 0, nPeriod, sigma, sigmaAlt, range,
					  appliedPrice, maMethodCenter, maMethodBand, false, false, false, false,
					  markRangeUpper, markRangeLower, colRange, wRange, nMaxBars, 1, x);
	    BufferLowerLine1[i] = iCustom(NULL, timeFrame, sIndSelf, 0, nPeriod, sigma, sigmaAlt, range,
					  appliedPrice, maMethodCenter, maMethodBand, false, false, false, false,
					  markRangeUpper, markRangeLower, colRange, wRange, nMaxBars, 2, x);
	    BufferUpperLine2[i] = iCustom(NULL, timeFrame, sIndSelf, 0, nPeriod, sigma, sigmaAlt, range,
					  appliedPrice, maMethodCenter, maMethodBand, false, false, false, false,
					  markRangeUpper, markRangeLower, colRange, wRange, nMaxBars, 3, x);
	    BufferLowerLine2[i] = iCustom(NULL, timeFrame, sIndSelf, 0, nPeriod, sigma, sigmaAlt, range,
					  appliedPrice, maMethodCenter, maMethodBand, false, false, false, false,
					  markRangeUpper, markRangeLower, colRange, wRange, nMaxBars, 4, x);
	    BufferUpperRange[i] = iCustom(NULL, timeFrame, sIndSelf, 0, nPeriod, sigma, sigmaAlt, range,
					  appliedPrice, maMethodCenter, maMethodBand, false, false, false, false,
					  markRangeUpper, markRangeLower, colRange, wRange, nMaxBars, 5, x);
	    BufferLowerRange[i] = iCustom(NULL, timeFrame, sIndSelf, 0, nPeriod, sigma, sigmaAlt, range,
					  appliedPrice, maMethodCenter, maMethodBand, false, false, false, false,
					  markRangeUpper, markRangeLower, colRange, wRange, nMaxBars, 6, x);
	}
	
	checkAlert();
	
	return;
    }
    
    for(i = limit - 1; i >= 0; i--) {
	BufferUpperLine1[i] = EMPTY_VALUE;
	BufferLowerLine1[i] = EMPTY_VALUE;
	BufferUpperLine2[i] = EMPTY_VALUE;
	BufferLowerLine2[i] = EMPTY_VALUE;
	BufferUpperRange[i] = EMPTY_VALUE;
	BufferLowerRange[i] = EMPTY_VALUE;
	
	datetime t = Time[i];
	double ave = iMA(NULL, timeFrame, nPeriod, 0, maMethodCenter, appliedPrice, i);
	double sd = iStdDev(NULL, timeFrame, nPeriod, 0, maMethodBand, appliedPrice, i);
	
	BufferAve[i] = ave;
	
	if (sigma > 0) {
	    double upper1 = ave + sigma * sd;
	    double lower1 = ave - sigma * sd;
	    BufferUpperLine1[i] = upper1;
	    BufferLowerLine1[i] = lower1;
	    
	    if (range > 0) {
		if (upper1 - lower1 >= range * Point) {
		    BufferUpperRange[i] = upper1;
		    BufferLowerRange[i] = lower1;
		    // check range
		    if (i == 0) {
			datetime t0 = Time[0];
			if (markRangeUpper != 0) {
			    objArrow("upper", 0, t0, upper1, markRangeUpper, colRange, wRange);
			}
			if (markRangeLower != 0) {
			    objArrow("lower", 0, t0, lower1, markRangeLower, colRange, wRange);
			}
			if (bAlertRange && BufferUpperRange[1] == EMPTY_VALUE && tAlertLast != t0) {
			    PlaySound("alert.wav");
			    tAlertLast = t0;
			}
		    }
		}
	    }
	}
	
	if (sigmaAlt > 0) {
	    double upper2 = ave + sigmaAlt * sd;
	    double lower2 = ave - sigmaAlt * sd;
	    BufferUpperLine2[i] = upper2;
	    BufferLowerLine2[i] = lower2;
	}
    }
    
    checkAlert();
}
