//+------------------------------------------------------------------+
//|                                           TDSequentialByGP2X.mq4 |
//|                                           Copyright © 2006, GP2X |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, GP2X"
#property link      ""

#property indicator_chart_window
#property indicator_buffers 3

#define STATUS_NONE 0
#define STATUS_BUY_SETUP 1
#define STATUS_BUY_COUNTDOWN 2
#define STATUS_SELL_SETUP -1
#define STATUS_SELL_COUNTDOWN -2

#define PERFECT_SETUP 99
#define NULLIFIED 100
#define NOTQUALIFIED 101
#define OBJ_NAME "TDNumber"
#define DRAW_SPACE 8

//---- input parameters
extern int SETUP_BARS = 9;
extern int SETUP_COMPARE_BARS = 4;
extern int COUNTDOWN_BARS = 13;
extern int COUNTDOWN_COMPARE_BARS = 2;
extern int COUNTDOWN_QUALIFIER_BAR = 8;
extern color SetupColor = Red;
extern color CountDownColor = Aqua;
extern color StopLineColor = Red;

int countDown, setupEndPos, stopBarPos, drawIndex;
double setupNullifiedValue, countDownQualClose;
double setups[], countDowns[], stops[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators
   SetIndexStyle(0,DRAW_NONE);
   SetIndexBuffer(0,setups);
   SetIndexEmptyValue(0,0.0);
   SetIndexStyle(1,DRAW_NONE);
   SetIndexBuffer(1,countDowns);
   SetIndexEmptyValue(1,0.0);   
   SetIndexStyle(2,DRAW_NONE);
   SetIndexBuffer(2,stops);
   SetIndexEmptyValue(2,0.0);   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
   clearObjects();
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start() {
   int status = STATUS_NONE, space;
   setupEndPos = 0;
   countDown = 0;
   int i;
   clearObjects();
      for (i=Bars-SETUP_BARS-SETUP_COMPARE_BARS;i>=0;i--) {
         status = checkSetup(i, status);
         if (status == STATUS_BUY_SETUP)
            status = confirmBuyInterSec(i);
         if (status == STATUS_BUY_COUNTDOWN)
            status = continueBuyCountDown(i);
         if (status == STATUS_SELL_SETUP)
            status = confirmSellInterSec(i);
         if (status == STATUS_SELL_COUNTDOWN)
            status = continueSellCountDown(i);         
      }
      drawIndex = 0;
      for (i=Bars;i>=0;i--) {
         if (setups[i]!=0)
            drawNumber(setups[i], i, DRAW_SPACE, SetupColor);         
         if (countDowns[i]!=0) {
            if (setups[i]==0)
               space = DRAW_SPACE;
            else {
               if (MathAbs(setups[i])>SETUP_BARS)
                  space = 3*DRAW_SPACE;
               else
                  space = 2*DRAW_SPACE;
            }
            drawNumber(countDowns[i], i, space, CountDownColor);
         }      
      }
      
      for (i=0;i<Bars;i++) {
         if (stops[i]!=0) {
            //Draw stop line
            string objName = OBJ_NAME + drawIndex;
            int rightEnd = i-1;
            if (rightEnd<0) rightEnd=0;
            ObjectCreate(objName, OBJ_TREND, 0, Time[i+1], stops[i], Time[rightEnd], stops[i]);
            ObjectSet(objName, OBJPROP_STYLE, STYLE_DASHDOTDOT);
            ObjectSet(objName, OBJPROP_COLOR, StopLineColor);            
            drawIndex++;
            break;
         }
      }
   return(0);
}

bool isBuySetup(int barPos) {
   bool isSetup = true;
   for (int i=barPos;(i<barPos+SETUP_BARS)&&isSetup;i++) {
      isSetup = Close[i]<Close[i+SETUP_COMPARE_BARS];
   }      
   return (isSetup);
}

bool isSellSetup(int barPos) {
   bool isSetup = true;
   for (int i=barPos;(i<barPos+SETUP_BARS)&&isSetup;i++) {
      isSetup = Close[i]>Close[i+SETUP_COMPARE_BARS];
   }      
   return (isSetup);
}

bool shouldRecycle(int oldPos, int newPos) {
   double oldHigh, oldLow, newHigh, newLow;
   oldHigh = High[Highest(Symbol(), Period(), MODE_HIGH, SETUP_BARS, oldPos)];
   oldLow = Low[Lowest(Symbol(), Period(), MODE_LOW, SETUP_BARS, oldPos)];
   newHigh = High[Highest(Symbol(), Period(), MODE_HIGH, SETUP_BARS, newPos)];
   newLow = Low[Lowest(Symbol(), Period(), MODE_LOW, SETUP_BARS, newPos)];
   return (newHigh-newLow>oldHigh-oldLow);
}

int checkSetup(int barPos, int oldStatus) {
   int status = oldStatus;
   bool takeNewSetup;
   if (isBuySetup(barPos)) {
      //Found new buy setup
      takeNewSetup = oldStatus!=STATUS_BUY_SETUP && oldStatus!=STATUS_BUY_COUNTDOWN;
      if (!takeNewSetup) takeNewSetup = shouldRecycle(setupEndPos, barPos); //Recycle
      if (takeNewSetup) {
         clearPreviousSetup(barPos, oldStatus);
         setupEndPos = barPos;
         status = STATUS_BUY_SETUP;
         setupNullifiedValue = High[Highest(Symbol(), Period(), MODE_HIGH, SETUP_BARS, barPos)];
         createBuySetup();
      }
   }
   else {
      if (isSellSetup(barPos)) {
         //Found new sell setup
         takeNewSetup = oldStatus!=STATUS_SELL_SETUP && oldStatus!=STATUS_SELL_COUNTDOWN;
         if (!takeNewSetup) takeNewSetup = shouldRecycle(setupEndPos, barPos);
         if (takeNewSetup) {
            clearPreviousSetup(barPos, oldStatus);
            setupEndPos = barPos;
            status = STATUS_SELL_SETUP;
            setupNullifiedValue = Low[Lowest(Symbol(), Period(), MODE_LOW, SETUP_BARS, barPos)];
            createSellSetup();
         }
      }
   }
   return (status);
}

int confirmBuyInterSec(int barPos) {
   int status;
   bool isIs = false;
   if (High[barPos]>setupNullifiedValue) {
      status = STATUS_NONE;
      countDowns[barPos] = NULLIFIED;
      clearPreviousSetup(barPos+1, status);
   }
   else {   
      isIs = isBuyInterSec(barPos, setupEndPos+SETUP_BARS-1);
      if (!isIs && barPos==setupEndPos)
         //Also look at bar 8th
         isIs = isBuyInterSec(barPos+1, barPos+8);
      if (isIs) {
         countDown = 0;
         status = STATUS_BUY_COUNTDOWN;
         stopBarPos = 0;
      }
      else
         status = STATUS_BUY_SETUP;
   }
   return (status);
}


bool isBuyInterSec(int fromBar, int toBar) {
   bool result = false;
   int i;
   for (i=fromBar+3;i<=toBar;i++) {
      if (High[fromBar]>=Low[i]) {
         result = true;
         break;
      }
   }
   return (result);
}

int confirmSellInterSec(int barPos) {
   int status;
   bool isIs = false;
   if (Low[barPos]<setupNullifiedValue) {
      status = STATUS_NONE;
      countDowns[barPos] = -NULLIFIED;
      clearPreviousSetup(barPos+1, status);
   }
   else {
      isIs = isSellInterSec(barPos, setupEndPos+SETUP_BARS-1);
      if (!isIs && barPos==setupEndPos)
         //Also look at bar 8th
         isIs = isSellInterSec(barPos+1, barPos+8);
      if (isIs) {
         countDown = 0;
         status = STATUS_SELL_COUNTDOWN;
         stopBarPos = 0;
      }
      else
         status = STATUS_SELL_SETUP;
   }
   return (status);
}

bool isSellInterSec(int fromBar, int toBar) {
   bool result = false;
   int i;
   for (i=fromBar+3;i<=toBar;i++) {
      if (Low[fromBar]<=High[i]) {
         result = true;
         break;
      }
   }
   return (result);
}

int continueBuyCountDown(int barPos) {
   int status = STATUS_BUY_COUNTDOWN;
   if (High[barPos]>setupNullifiedValue) {
      status = STATUS_NONE;
      countDowns[barPos] = NULLIFIED;
      clearPreviousSetup(barPos+1, status);
   }
   else {
      if (stopBarPos==0) stopBarPos = barPos;
      else {
         if (Low[barPos]<Low[stopBarPos])
            stopBarPos = barPos;
      }
      if (Close[barPos]<=Low[barPos+COUNTDOWN_COMPARE_BARS]) {
         countDown++;
         countDowns[barPos] = countDown;
         if (countDown==COUNTDOWN_BARS) {
            setBuyStop(barPos);
            status = STATUS_NONE;
            if (Low[barPos]>=countDownQualClose) {
               //Not qualified
               countDowns[barPos] = NOTQUALIFIED;
            }
         }
         else if (countDown==COUNTDOWN_QUALIFIER_BAR)
            countDownQualClose = Close[barPos];
      }
   }
   return (status);
}

int continueSellCountDown(int barPos) {
   int status = STATUS_SELL_COUNTDOWN;
   if (Low[barPos]<setupNullifiedValue) {
      status = STATUS_NONE;
      countDowns[barPos] = -NULLIFIED;
      clearPreviousSetup(barPos+1, status);
   }
   else {
      if (stopBarPos==0) stopBarPos = barPos;
      else {
         if (High[barPos]>High[stopBarPos])
            stopBarPos = barPos;
      }
      if (Close[barPos]>=High[barPos+COUNTDOWN_COMPARE_BARS]) {
         countDown--;
         countDowns[barPos] = countDown;
         if (countDown==-COUNTDOWN_BARS) {
            setSellStop(barPos);
            status = STATUS_NONE;
            if (High[barPos]<=countDownQualClose) {
               //Not qualified
               countDowns[barPos] = -NOTQUALIFIED;
            }
         }
         else if (countDown==COUNTDOWN_QUALIFIER_BAR)
            countDownQualClose = Close[barPos];
      }
   }
   return (status);
}

void createBuySetup() {
   for (int i=1;i<=SETUP_BARS;i++) {
      setups[setupEndPos+SETUP_BARS-i] = i;
      countDowns[setupEndPos+SETUP_BARS-i] = 0;
   }
   if (MathMin(Low[setupEndPos], Low[setupEndPos+1])<MathMin(Low[setupEndPos+2], Low[setupEndPos+3]))
      setups[setupEndPos] = PERFECT_SETUP;
}

void createSellSetup() {
   for (int i=1;i<=SETUP_BARS;i++) {
      setups[setupEndPos+SETUP_BARS-i] = -i;
      countDowns[setupEndPos+SETUP_BARS-i] = 0;
   }
   if (MathMax(High[setupEndPos], High[setupEndPos+1])>MathMax(High[setupEndPos+2], High[setupEndPos+3]))
      setups[setupEndPos] = -PERFECT_SETUP;
}

double setBuyStop(int barPos) {
   double trueRange = MathMax(Close[stopBarPos+1], High[stopBarPos])-Low[stopBarPos];
   stops[barPos] = Low[stopBarPos]-trueRange;
}

double setSellStop(int barPos) {
   double trueRange = High[stopBarPos] - MathMin(Close[stopBarPos+1], Low[stopBarPos]);
   stops[barPos] = High[stopBarPos]+trueRange;
}

void drawNumber(int number, int barPos, int space, color drawColor) {
   double drawPrice;
   string drawText;
   string objName = OBJ_NAME + drawIndex;
   bool isPerfect = false;
   bool isNoQual = false;
   if (number>0) {
      drawPrice = Low[barPos]-space*Point;
   }
   else {
      drawPrice = High[barPos]+space*Point;
   }
   if (MathAbs(number)<=COUNTDOWN_BARS) {
      drawText = DoubleToStr(MathAbs(number), 0);
   }
   else if (MathAbs(number)==PERFECT_SETUP) {
      drawText = SETUP_BARS;
      isPerfect = true;
   }
   else if (MathAbs(number)==NULLIFIED) {
      drawText = "X";
   }
   else if (MathAbs(number)==NOTQUALIFIED) {
      drawText = COUNTDOWN_BARS;
      isNoQual = true;
   }
   ObjectCreate(objName, OBJ_TEXT, 0, Time[barPos], drawPrice);
   ObjectSetText(objName, drawText, 8, "Arial", drawColor);
   drawIndex++;
   if (isPerfect) {
      objName = OBJ_NAME + drawIndex;
      drawPrice = drawPrice + space*Point*(-PERFECT_SETUP/number);
      ObjectCreate(objName, OBJ_TEXT, 0, Time[barPos], drawPrice);
      ObjectSetText(objName, "P", 8, "Arial", drawColor);
      drawIndex++;
   }
   if (isNoQual) {
      objName = OBJ_NAME + drawIndex;
      drawPrice = drawPrice + space*Point*(-NOTQUALIFIED/number);
      ObjectCreate(objName, OBJ_TEXT, 0, Time[barPos], drawPrice);
      ObjectSetText(objName, "*", 8, "Arial", drawColor);
      drawIndex++;
   }
}

void clearPreviousSetup(int barPos, int status) {
   if (status!=STATUS_NONE) {
      for (int i=barPos;i<setupEndPos+SETUP_BARS;i++) {
         setups[i]=0;
         countDowns[i]=0;
      }
   }
}

void clearObjects() {
   for (int i=0;i<drawIndex;i++) {
      string objName = OBJ_NAME + i;
      if (ObjectFind(objName)==0)
         ObjectDelete(objName);
   }
}

//+------------------------------------------------------------------+