//
// "00-STC_v103.mq4" -- Schaff Trend Cycle
//    refer to http://www.fxstreet.com/education/trading-strategies/releasing-the-code-to-the-schaff-trend-cycle/
//
//    Ver. 0.01  2008/10/27(Mon)  initial version
//    Ver. 1.00  2008/11/16(Sun)  release version
//    Ver. 1.01  2008/12/23(Tue)  added bAlert
//    Ver. 1.02  2008/12/23(Tue)  added appliedPrice
//    Ver. 1.03  2008/12/23(Tue)  bugfixed: don't work in MTF mode
//
//
#property  copyright  "00 - 00mql4@gmail.com"
#property  link       "http://www.mql4.com/"

//---- indicator settings
#property  indicator_separate_window

#property  indicator_buffers  5  // 5:6184KB 4:5720KB 3:5244KB 2:4776KB 1:4304

#property  indicator_color1  Yellow       // 0: STC
#property  indicator_color2  DodgerBlue   // 1: Long trigger
#property  indicator_color3  Crimson      // 2: Short trigger
#property  indicator_color4  0x80481e     // 3: Long turn
#property  indicator_color5  0x1e0a6e     // 4: Short turn

#property  indicator_width1  1
#property  indicator_width2  1
#property  indicator_width3  1
#property  indicator_width4  1
#property  indicator_width5  1

#property  indicator_style1  STYLE_SOLID

#property indicator_minimum  -20
#property indicator_maximum  120

//---- defines
#define LONG_MARK_POS   -5
#define SHORT_MARK_POS  105

//---- indicator parameters
extern int      timeFrame      = 0;              // time frame
extern int      nFast          = 23;             // EMA fast
extern int      nSlow          = 50;             // EMA slow
extern int      nStochas       = 10;             // Stochas period
extern bool     bDoubleStochas = true;           // apply stochas twice
extern int      appliedPrice   = PRICE_TYPICAL;  // 0:CLOSE 1:OPEN 2:HIGH 3:LOW 4:MEDIAN 5:TYPICAL 6:WEIGHTED
extern double   levelLo        = 5;              // lower level for long signal
extern double   levelHi        = 95;             // upper level for short signal
extern double   clearLo        = 25;             // lower level to clear long turn
extern double   clearHi        = 75;             // upper level to clear short turn
extern bool     bAlert         = false;          // PlaySound() on signal
extern int      nMaxBars       = 2000;           // maximum number of bars to calculate, 0: no limit

//---- indicator buffers
double BufferSTC[];           // 0: STC
double BufferLongTrigger[];   // 1: Long trigger
double BufferShortTrigger[];  // 2: Short trigger
double BufferLongTurn[];      // 3: Long turn
double BufferShortTurn[];     // 4: Short turn

//---- vars
string sIndicatorName;
string sIndSelf = "00-STC_v103";
int    markLongTrigger  = 233;
int    markLongTurn     = 167;
int    markShortTrigger = 234;
int    markShortTurn    = 167;
int    lastBars;
double bufMacd[];
double bufStoK1[];
double bufStoD1[];
double bufStoK2[];
datetime tAlertLast;

//----------------------------------------------------------------------
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 + "," + nStochas + "," + nFast + "," + nSlow + ")";
    
    IndicatorShortName(sIndicatorName);
    
    SetIndexBuffer(0, BufferSTC);
    SetIndexBuffer(1, BufferLongTrigger);
    SetIndexBuffer(2, BufferShortTrigger);
    SetIndexBuffer(3, BufferLongTurn);
    SetIndexBuffer(4, BufferShortTurn);
    
    SetIndexLabel(0, "STC");
    SetIndexLabel(1, "Long trigger");
    SetIndexLabel(2, "Short trigger");
    SetIndexLabel(3, "Long turn");
    SetIndexLabel(4, "Short turn");
    
    SetIndexStyle(0, DRAW_LINE);
    SetIndexStyle(1, DRAW_ARROW);
    SetIndexStyle(2, DRAW_ARROW);
    SetIndexStyle(3, DRAW_ARROW);
    SetIndexStyle(4, DRAW_ARROW);
    
    SetIndexArrow(1, markLongTrigger);
    SetIndexArrow(2, markShortTrigger);
    SetIndexArrow(3, markLongTurn);
    SetIndexArrow(4, markShortTurn);
    
    int n = MathMax(nFast, nSlow) + nStochas;
    if (nMaxBars > 0) {
	n += Bars - MathMin(Bars, nMaxBars);
    }
    
    SetIndexDrawBegin(0, n);
    SetIndexDrawBegin(1, n);
    SetIndexDrawBegin(2, n);
    SetIndexDrawBegin(3, n);
    SetIndexDrawBegin(4, n);
    
    SetLevelValue(0, levelLo);
    SetLevelValue(1, levelHi);
}

//----------------------------------------------------------------------
void arrayMove(double array[], int to, int from)
{
    static double tmp[];
    int n = ArraySize(tmp);
    if (n < ArraySize(array)) {
	ArrayResize(tmp, ArraySize(array));
	n = ArraySize(tmp);
    }
    int d = to - from;
    ArrayCopy(tmp, array, 0, 0, n - d);
    ArrayCopy(array, tmp, to, from, n - d);
}

//----------------------------------------------------------------------
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);
    }
    
    // setup work buffers
    if (lastBars != Bars) {
	int n = Bars;
	if (nMaxBars > 0) {
	    n = nMaxBars;
	}
	if (ArraySize(bufMacd) != n) {
	    ArrayResize(bufMacd, n);
	    ArrayResize(bufStoK1, n);
	    ArrayResize(bufStoD1, n);
	    ArrayResize(bufStoK2, n);
	}
	n = Bars - lastBars;
	arrayMove(bufMacd, n, 0);
	arrayMove(bufStoK1, n, 0);
	arrayMove(bufStoD1, n, 0);
	arrayMove(bufStoK2, n, 0);
	lastBars = Bars;
    }
    
    // clear beyond limits
    for (int i = limit0 - 1; i >= limit; i--) {
	BufferSTC[i]          = 0;
	BufferLongTrigger[i]  = EMPTY_VALUE;
	BufferShortTrigger[i] = EMPTY_VALUE;
	BufferLongTurn[i]     = EMPTY_VALUE;
	BufferShortTurn[i]    = EMPTY_VALUE;
    }
    
    if (timeFrame != Period()) {
	// MTF
	limit = MathMax(limit, timeFrame / Period());
	for (i = limit - 1; i >= 0; i--) {
	    int shift = iBarShift(NULL, timeFrame, Time[i]);
	    BufferSTC[i]          = iCustom(NULL, timeFrame, sIndSelf, 0, nFast, nSlow, nStochas, bDoubleStochas,
					    appliedPrice, levelLo, levelHi, clearLo, clearHi, false, nMaxBars, 0, shift);
	    BufferLongTrigger[i]  = iCustom(NULL, timeFrame, sIndSelf, 0, nFast, nSlow, nStochas, bDoubleStochas,
					    appliedPrice, levelLo, levelHi, clearLo, clearHi, false, nMaxBars, 1, shift);
	    BufferShortTrigger[i] = iCustom(NULL, timeFrame, sIndSelf, 0, nFast, nSlow, nStochas, bDoubleStochas,
					    appliedPrice, levelLo, levelHi, clearLo, clearHi, false, nMaxBars, 2, shift);
	    BufferLongTurn[i]     = iCustom(NULL, timeFrame, sIndSelf, 0, nFast, nSlow, nStochas, bDoubleStochas,
					    appliedPrice, levelLo, levelHi, clearLo, clearHi, false, nMaxBars, 3, shift);
	    BufferShortTurn[i]    = iCustom(NULL, timeFrame, sIndSelf, 0, nFast, nSlow, nStochas, bDoubleStochas,
					    appliedPrice, levelLo, levelHi, clearLo, clearHi, false, nMaxBars, 4, shift);
	    
	    if (bAlert && i == 0) {
		bool bFire = (BufferLongTrigger[0] != EMPTY_VALUE || BufferShortTrigger[0] != EMPTY_VALUE);
		if (bFire && tAlertLast != Time[0]) {
		    PlaySound("alert.wav");
		    tAlertLast = Time[0];
		}
	    }
	}
	
	return;
    }
    
    // timeFrame == Period()
    for (i = limit - 1; i >= 0; i--) {
	BufferLongTrigger[i]  = EMPTY_VALUE;
	BufferShortTrigger[i] = EMPTY_VALUE;
	BufferLongTurn[i]     = EMPTY_VALUE;
	BufferShortTurn[i]    = EMPTY_VALUE;
	
	// stochas on MACD
	bufMacd[i] = iMACD(NULL, 0, nFast, nSlow, 1, appliedPrice, MODE_MAIN, i);
	double min = bufMacd[ArrayMinimum(bufMacd, nStochas, i)];
	double range = bufMacd[ArrayMaximum(bufMacd, nStochas, i)] - min;
	
	if (range > 0) {
	    bufStoK1[i] = ((bufMacd[i] - min) / range) * 100.0;
	} else {
	    bufStoK1[i] = bufStoK1[i + 1];
	}
	
	if (!bDoubleStochas) {
	    // single stochas
	    // STC value
	    BufferSTC[i] = BufferSTC[i + 1] + 0.5 * (bufStoK1[i] - BufferSTC[i + 1]);
	} else {
	    // double stochas, stochas on stochas
	    bufStoD1[i] = bufStoD1[i + 1] + 0.5 * (bufStoK1[i] - bufStoD1[i + 1]);
	    min = bufStoD1[ArrayMinimum(bufStoD1, nStochas, i)];
	    range = bufStoD1[ArrayMaximum(bufStoD1, nStochas, i)] - min;
	
	    if (range > 0) {
		bufStoK2[i] = ((bufStoD1[i] - min) / range) * 100.0;
	    } else {
		bufStoK2[i] = bufStoK2[i + 1];
	    }
	    
	    // STC value
	    BufferSTC[i] = BufferSTC[i + 1] + 0.5 * (bufStoK2[i] - BufferSTC[i + 1]);
	}
	
	// check signal
	bFire = false;
	double stc2 = BufferSTC[i + 2];
	double stc1 = BufferSTC[i + 1];
	if (stc2 <= levelLo && stc1 > levelLo) {
	    // long signal
	    bFire = true;
	    BufferLongTrigger[i]  = LONG_MARK_POS;
	    BufferLongTurn[i]     = LONG_MARK_POS;
	    BufferShortTurn[i + 1] = EMPTY_VALUE;
	} else if (stc2 >= levelHi && stc1 < levelHi) {
	    // short signal
	    bFire = true;
	    BufferShortTrigger[i] = SHORT_MARK_POS;
	    BufferShortTurn[i]    = SHORT_MARK_POS;
	    BufferLongTurn[i + 1] = EMPTY_VALUE;
	}
	
	if (BufferLongTrigger[i + 1] != EMPTY_VALUE || BufferLongTurn[i + 1] != EMPTY_VALUE) {
	    BufferLongTurn[i] = LONG_MARK_POS;
	}
	if (BufferShortTrigger[i + 1] != EMPTY_VALUE || BufferShortTurn[i + 1] != EMPTY_VALUE) {
	    BufferShortTurn[i] = SHORT_MARK_POS;
	}
	
	if (stc1 < stc2 && stc1 < clearLo) {
	    BufferLongTurn[i] = EMPTY_VALUE;
	}
	if (stc1 > stc2 && stc1 > clearHi) {
	    BufferShortTurn[i] = EMPTY_VALUE;
	}
	
	if (bAlert) {
	    if (i == 0 && bFire && tAlertLast != Time[0]) {
		PlaySound("alert.wav");
		tAlertLast = Time[0];
	    }
	}
    }
}
