//+------------------------------------------------------------------+
//|                                         SS_SupportResistance.mq4 |
//|                                  Copyright © 2012, Andrew Sumner |
//|                                                                  |
//| You are allowed to copy and distribute this file as you see fit, |
//| and modify it to suit your purposes on the following condition:- |
//|                                                                  |
//| 1. You must charge no money for this indicator or any derivative |
//|    that you create from it.  It was released freely, please keep |
//|    it free.                                                      |
//|                                                                  |
//| 2. If you make alterations, please don't release a new version   |
//|    using the name "SS_SupportResistance".  Either release it     |
//|    using a new name, or contact me about getting your changes    |
//|    included in my indicator (andrewsumner@yahoo.com).            |
//|                                                                  |
//| 3. If you make a killer EA based on this indicator, please do me |
//|    a favour and send me a copy :)                                |
//|                                                                  |
//| My thanks to Enrico "brax64" Carpita for his help in completing  |
//| the zone.show.info text positioning code.                        |
//|                                                                  |
//| 08/07/2012 v04c: added email capability - brax64 at forex-tsd    |
//+------------------------------------------------------------------+


#property copyright "Copyright © 2012 Andrew Sumner"
#property link      ""

#property indicator_chart_window
#property indicator_buffers 4
#property indicator_color1 Red
#property indicator_color2 Red
#property indicator_color3 DodgerBlue
#property indicator_color4 DodgerBlue

extern int BackLimit   = 10000;
extern int TimeFrame   = 0;
extern string TimeString  = "0=Current, 60=H1, 240=H4, 1440=Day, 10080=Week, 43200=Month";

extern color colorsupportweak     = DarkSlateGray;
extern color colorsupportuntested = SeaGreen;
extern color colorsupportverified = Green;
extern color colorsupportproven   = LimeGreen;
extern color colorsupportturncoat = OliveDrab;
extern color colorresistweak      = Indigo;
extern color colorresistuntested  = Orchid;
extern color colorresistverified  = Crimson;
extern color colorresistproven    = Red;
extern color colorresistturncoat  = DarkOrange;

extern bool zoneshowweak  = false;
extern double zonefuzzfactor = 0.75;
extern bool zonesolid = false;
extern int zonelinewidth = 4;
extern int zonestyle = 0;
extern bool zoneshowinfo    = true;
extern int zonelabelshift  = 2;
extern bool zoneshowalerts  = false;
extern bool zonealertpopups = false;
extern bool zonealertsounds = false;
extern bool sendemail        = false;
extern int zonealertwaitseconds = 300; 
extern bool zonemerge = true;
extern bool zoneextend = true;

extern bool fractalsshow = false;
extern double fractalfastfactor = 3.0;
extern double fractalslowfactor = 6.0;
extern bool SetGlobals = true;

double FastDnPts[], FastUpPts[];
double SlowDnPts[], SlowUpPts[];

double zonehi[1000], zonelo[1000];
int    zonestart[1000], zonehits[1000], zonetype[1000], zonestrength[1000], zonecount = 0;
bool   zoneturn[1000];

#define ZONE_SUPPORT 1
#define ZONE_RESIST  2

#define ZONE_WEAK      0
#define ZONE_TURNCOAT  1
#define ZONE_UNTESTED  2
#define ZONE_VERIFIED  3
#define ZONE_PROVEN    4

#define UP_POINT 1
#define DN_POINT -1

int timeoffset = 0;

int init()
{
   IndicatorBuffers(4);

   SetIndexBuffer(0, SlowDnPts);
   SetIndexBuffer(1, SlowUpPts);
   SetIndexBuffer(2, FastDnPts);
   SetIndexBuffer(3, FastUpPts);

   if (fractalsshow == true)
   {
      SetIndexStyle(0, DRAW_ARROW, 0, 3);
      SetIndexStyle(1, DRAW_ARROW, 0, 3);
      SetIndexStyle(2, DRAW_ARROW, 0, 1);
      SetIndexStyle(3, DRAW_ARROW, 0, 1);
      SetIndexArrow(0, 218);
      SetIndexArrow(1, 217);
      SetIndexArrow(2, 218);
      SetIndexArrow(3, 217);
   }
   else
   {
      SetIndexStyle(0, DRAW_NONE);
      SetIndexStyle(1, DRAW_NONE);
      SetIndexStyle(2, DRAW_NONE);
      SetIndexStyle(3, DRAW_NONE);
   }

   if (TimeFrame != 1 && TimeFrame != 5 && TimeFrame != 15 &&
       TimeFrame != 60 && TimeFrame != 240 && TimeFrame != 1440 &&
       TimeFrame != 10080 && TimeFrame != 43200)
      TimeFrame = 0;

   if (TimeFrame < Period())
      TimeFrame = Period();

   return(0);
}

int deinit()
{
   DeleteZones();
   DeleteGlobalVars();
   return(0);
}

int start()
{
   if (NewBar() == true)
   {
      int old_zonecount = zonecount;

      FastFractals();
      SlowFractals();
      DeleteZones();
      FindZones();
      DrawZones();
      if (zonecount < old_zonecount)
         DeleteOldGlobalVars(old_zonecount);
   }

   if (zoneshowinfo == true)
   {
      for (int i=0; i<zonecount; i++)
      {
         string lbl;
         if (zonestrength[i] == ZONE_PROVEN)
            lbl = "Proven";
         else if (zonestrength[i] == ZONE_VERIFIED)
            lbl = "Verified";
         else if (zonestrength[i] == ZONE_UNTESTED)
            lbl = "Untested";
         else if (zonestrength[i] == ZONE_TURNCOAT)
            lbl = "Turncoat";
         else
            lbl = "Weak";

         if (zonetype[i] == ZONE_SUPPORT)
            lbl = lbl + " Support";
         else
            lbl = lbl + " Resistance";

         if (zonehits[i] > 0 && zonestrength[i] > ZONE_UNTESTED)
         {
            if (zonehits[i] == 1)
               lbl = lbl + ", Test Count=" + zonehits[i];
            else
               lbl = lbl + ", Test Count=" + zonehits[i];
         }

         int adjusthpos;
         int wbpc = WindowBarsPerChart();
         int k;
         
         k = Period() * 60 + (20 + StringLen(lbl));
         
         if (wbpc < 80)  
            adjusthpos = Time[0] + k * 4;
         else if (wbpc < 125)  
            adjusthpos = Time[0] + k * 8;
         else if (wbpc < 250)
            adjusthpos = Time[0] + k * 15;
         else if (wbpc < 480)
            adjusthpos = Time[0] + k * 29;
         else if (wbpc < 950)
            adjusthpos = Time[0] + k * 58;
         else
            adjusthpos = Time[0] + k * 115;
         
         //

         int shift = k * zonelabelshift;
         if (zonestrength[i] != ZONE_WEAK) {
            if (zonetype[i] == ZONE_SUPPORT)
               double vpos = zonehi[i];
            else
               vpos = zonelo[i];

            string s = "SnR#"+i+"LBL";
            ObjectCreate(s, OBJ_TEXT, 0, 0, 0);
            ObjectSet(s, OBJPROP_TIME1, adjusthpos + shift);
            ObjectSet(s, OBJPROP_PRICE1, vpos+0.1);
            ObjectSetText(s, StringRightPad(lbl, 36, " "), 8, "Courier New");
            ObjectSetInteger(0,s,OBJPROP_COLOR,clrWhite);
      if (zonetype[i] == ZONE_SUPPORT)
      {
         // support zone
         if (zonestrength[i] == ZONE_TURNCOAT)
            ObjectSet(s, OBJPROP_COLOR, colorsupportturncoat);
         else if (zonestrength[i] == ZONE_PROVEN)
            ObjectSet(s, OBJPROP_COLOR, colorsupportproven);
         else if (zonestrength[i] == ZONE_VERIFIED)
            ObjectSet(s, OBJPROP_COLOR, colorsupportverified);
         else if (zonestrength[i] == ZONE_UNTESTED)
            ObjectSet(s, OBJPROP_COLOR, colorsupportuntested);
         else
            ObjectSet(s, OBJPROP_COLOR, colorsupportweak);
      }
      else
      {
         // resistance zone
         if (zonestrength[i] == ZONE_TURNCOAT)
            ObjectSet(s, OBJPROP_COLOR, colorresistturncoat);
         else if (zonestrength[i] == ZONE_PROVEN)
            ObjectSet(s, OBJPROP_COLOR, colorresistproven);
         else if (zonestrength[i] == ZONE_VERIFIED)
            ObjectSet(s, OBJPROP_COLOR, colorresistverified);
         else if (zonestrength[i] == ZONE_UNTESTED)
            ObjectSet(s, OBJPROP_COLOR, colorresistuntested);
         else
            ObjectSet(s, OBJPROP_COLOR, colorresistweak);
      }
         }
      }
   }

   CheckAlerts();

   return(0);
}

void CheckAlerts()
{
   static int lastalert = 0;

   if (zoneshowalerts == false)
      return;

   if (Time[0] - lastalert > zonealertwaitseconds)
      if (CheckEntryAlerts() == true)
         lastalert = Time[0];
}

bool CheckEntryAlerts()
{
   // check for entries
   for (int i=0; i<zonecount; i++)
   {
      if (Close[0] >= zonelo[i] && Close[0] < zonehi[i])
      {
         if (zoneshowalerts == true)
         {
            if (zonealertpopups == true)
            {
               if (zonetype[i] == ZONE_SUPPORT)
                  Alert(Symbol() + TimeFrameToString(TimeFrame) + ": Support Zone Entered");
               else
                  Alert(Symbol() + TimeFrameToString(TimeFrame) + ": Resistance Zone Entered");
            }

            if (zonealertsounds == true)
               PlaySound("alert.wav");
         }
         
         if (sendemail == true)
         {
            string dir = "";
            string msg = StringConcatenate(Symbol(), "-", TimeFrameToString(TimeFrame), " at ", TimeToStr(Time[0], TIME_DATE|TIME_SECONDS),
                                           " ", dir, " Zone Entered");
            if (zonetype[i] == ZONE_SUPPORT)
            {
               dir = "Support";
               SendMail("SS_SupRes_v04c alert", msg);
            }
            else
            {
               dir = "Resistance";
               SendMail("SS_SupRes_v04c alert", msg);
            }
         }
         return(true);
      }
   }

   return(false);
}

void DeleteGlobalVars()
{
   if (SetGlobals == false)
      return;

   GlobalVariableDel("SSSR_Count_"+Symbol()+TimeFrame);
   GlobalVariableDel("SSSR_Updated_"+Symbol()+TimeFrame);

   int old_count = zonecount;
   zonecount = 0;
   DeleteOldGlobalVars(old_count);
}

void DeleteOldGlobalVars(int old_count)
{
   if (SetGlobals == false)
      return;

   for (int i=zonecount; i<old_count; i++)
   {
      GlobalVariableDel("SSSR_HI_"+Symbol()+TimeFrame+i);
      GlobalVariableDel("SSSR_LO_"+Symbol()+TimeFrame+i);
      GlobalVariableDel("SSSR_HITS_"+Symbol()+TimeFrame+i);
      GlobalVariableDel("SSSR_STRENGTH_"+Symbol()+TimeFrame+i);
      GlobalVariableDel("SSSR_AGE_"+Symbol()+TimeFrame+i);
   }
}

void FindZones()
{
   int i, j, shift, bustcount=0, testcount = 0;
   double hival, loval;
   bool turned = false, hasturned = false;

   double temphi[1000], templo[1000];
   int    tempstart[1000], temphits[1000], tempstrength[1000], tempcount = 0;
   bool   tempturn[1000], tempmerge[1000];
   int merge1[1000], merge2[1000], mergecount = 0;

   // iterate through zones from oldest to youngest (ignore recent 5 bars),
   // finding those that have survived through to the present...
   for (shift=MathMin(iBars(NULL, TimeFrame)-1, BackLimit); shift>5; shift--)
   {
      double atr = iATR(NULL, TimeFrame, 7, shift);
      double fu = atr/2 * zonefuzzfactor;
      bool isWeak;
      bool touchOk = false;
      bool isBust = false;
      double close = iClose(NULL, TimeFrame, shift);
      double high  = iHigh(NULL, TimeFrame, shift);
      double low   = iLow(NULL, TimeFrame, shift);
      double hi_i;
      double lo_i;

      if (FastUpPts[shift] > 0.001)
      {
         // a zigzag high point
         isWeak = true;
         if (SlowUpPts[shift] > 0.001)
            isWeak = false;

         hival = high;
         if (zoneextend == true)
            hival += fu;

         loval = MathMax(MathMin(close, high-fu), high-fu*2);
         turned = false;
         hasturned = false;
         isBust = false;

         bustcount = 0;
         testcount = 0;

         for (i=shift-1; i>=0; i--)
         {
            hi_i = iHigh(NULL, TimeFrame, i);
            lo_i = iLow(NULL, TimeFrame, i);

            if ((turned == false && FastUpPts[i] >= loval && FastUpPts[i] <= hival) ||
                (turned == true && FastDnPts[i] <= hival && FastDnPts[i] >= loval))
            {
               // Potential touch, just make sure its been 10+candles since the prev one
               touchOk = true;
               for (j=i+1; j<i+11; j++)
               {
                  if ((turned == false && FastUpPts[j] >= loval && FastUpPts[j] <= hival) ||
                      (turned == true && FastDnPts[j] <= hival && FastDnPts[j] >= loval))
                  {
                     touchOk = false;
                     break;
                  }
               }

               if (touchOk == true)
               {
                  // we have a touch.  If its been busted once, remove bustcount
                  // as we know this level is still valid & has just switched sides
                  bustcount = 0;
                  testcount++;
               }
            }

            if ((turned == false && hi_i > hival) ||
                (turned == true && lo_i < loval))
            {
               // this level has been busted at least once
               bustcount++;

               if (bustcount > 1 || isWeak == true)
               {
                  // busted twice or more
                  isBust = true;
                  break;
               }

               if (turned == true)
                  turned = false;
               else if (turned == false)
                  turned = true;

               hasturned = true;

               // forget previous hits
               testcount = 0;
            }
         }

         if (isBust == false)
         {
            // level is still valid, add to our list
            temphi[tempcount] = hival;
            templo[tempcount] = loval;
            tempturn[tempcount] = hasturned;
            temphits[tempcount] = testcount;
            tempstart[tempcount] = shift;
            tempmerge[tempcount] = false;
            
            if (testcount > 3)
               tempstrength[tempcount] = ZONE_PROVEN;
            else if (testcount > 0)
               tempstrength[tempcount] = ZONE_VERIFIED;
            else if (hasturned == true)
               tempstrength[tempcount] = ZONE_TURNCOAT;
            else if (isWeak == false)
               tempstrength[tempcount] = ZONE_UNTESTED;
            else
               tempstrength[tempcount] = ZONE_WEAK;

            tempcount++;
         }
      }
      else if (FastDnPts[shift] > 0.001)
      {
         // a zigzag low point
         isWeak = true;
         if (SlowDnPts[shift] > 0.001)
            isWeak = false;

         loval = low;
         if (zoneextend == true)
            loval -= fu;

         hival = MathMin(MathMax(close, low+fu), low+fu*2);
         turned = false;
         hasturned = false;

         bustcount = 0;
         testcount = 0;
         isBust = false;

         for (i=shift-1; i>=0; i--)
         {
            hi_i = iHigh(NULL, TimeFrame, i);
            lo_i = iLow(NULL, TimeFrame, i);

            if ((turned == true && FastUpPts[i] >= loval && FastUpPts[i] <= hival) ||
                (turned == false && FastDnPts[i] <= hival && FastDnPts[i] >= loval))
            {
               // Potential touch, just make sure its been 10+candles since the prev one
               touchOk = true;
               for (j=i+1; j<i+11; j++)
               {
                  if ((turned == true && FastUpPts[j] >= loval && FastUpPts[j] <= hival) ||
                      (turned == false && FastDnPts[j] <= hival && FastDnPts[j] >= loval))
                  {
                     touchOk = false;
                     break;
                  }
               }

               if (touchOk == true)
               {
                  // we have a touch.  If its been busted once, remove bustcount
                  // as we know this level is still valid & has just switched sides
                  bustcount = 0;
                  testcount++;
               }
            }

            if ((turned == true && hi_i > hival) ||
                (turned == false && lo_i < loval))
            {
               // this level has been busted at least once
               bustcount++;

               if (bustcount > 1 || isWeak == true)
               {
                  // busted twice or more
                  isBust = true;
                  break;
               }

               if (turned == true)
                  turned = false;
               else if (turned == false)
                  turned = true;

               hasturned = true;

               // forget previous hits
               testcount = 0;
            }
         }

         if (isBust == false)
         {
            // level is still valid, add to our list
            temphi[tempcount] = hival;
            templo[tempcount] = loval;
            tempturn[tempcount] = hasturned;
            temphits[tempcount] = testcount;
            tempstart[tempcount] = shift;
            tempmerge[tempcount] = false;

            if (testcount > 3)
               tempstrength[tempcount] = ZONE_PROVEN;
            else if (testcount > 0)
               tempstrength[tempcount] = ZONE_VERIFIED;
            else if (hasturned == true)
               tempstrength[tempcount] = ZONE_TURNCOAT;
            else if (isWeak == false)
               tempstrength[tempcount] = ZONE_UNTESTED;
            else
               tempstrength[tempcount] = ZONE_WEAK;

            tempcount++;
         }
      }
   }

   // look for overlapping zones...
   if (zonemerge == true)
   {
      mergecount = 1;
      int iterations = 0;
      while (mergecount > 0 && iterations < 3)
      {
         mergecount = 0;
         iterations++;

         for (i = 0; i < tempcount; i++)
            tempmerge[i] = false;

         for (i = 0; i < tempcount-1; i++)
         {
            if (temphits[i] == -1 || tempmerge[j] == true)
               continue;

            for (j = i+1; j < tempcount; j++)
            {
               if (temphits[j] == -1 || tempmerge[j] == true)
                  continue;

               if ((temphi[i] >= templo[j] && temphi[i] <= temphi[j]) ||
                   (templo[i] <= temphi[j] && templo[i] >= templo[j]) ||
                   (temphi[j] >= templo[i] && temphi[j] <= temphi[i]) ||
                   (templo[j] <= temphi[i] && templo[j] >= templo[i]))
               {
                  merge1[mergecount] = i;
                  merge2[mergecount] = j;
                  tempmerge[i] = true;
                  tempmerge[j] = true;
                  mergecount++;
               }
            }
         }

         // ... and merge them ...
         for (i=0; i<mergecount; i++)
         {
            int target = merge1[i];
            int source = merge2[i];

            temphi[target] = MathMax(temphi[target], temphi[source]);
            templo[target] = MathMin(templo[target], templo[source]);
            temphits[target] += temphits[source];
            tempstart[target] = MathMax(tempstart[target], tempstart[source]);
            tempstrength[target] = MathMax(tempstrength[target], tempstrength[source]);
            if (temphits[target] > 3)
               tempstrength[target] = ZONE_PROVEN;

            if (temphits[target] == 0 && tempturn[target] == false)
            {
               temphits[target] = 1;
               if (tempstrength[target] < ZONE_VERIFIED)
                  tempstrength[target] = ZONE_VERIFIED;
            }

            if (tempturn[target] == false || tempturn[source] == false)
               tempturn[target] = false;
            if (tempturn[target] == true)
               temphits[target] = 0;

            temphits[source] = -1;
         }
      }
   }

   // copy the remaining list into our official zones arrays
   zonecount = 0;
   for (i=0; i<tempcount; i++)
   {
      if (temphits[i] >= 0 && zonecount < 1000)
      {
         zonehi[zonecount]       = temphi[i];
         zonelo[zonecount]       = templo[i];
         zonehits[zonecount]     = temphits[i];
         zoneturn[zonecount]     = tempturn[i];
         zonestart[zonecount]    = tempstart[i];
         zonestrength[zonecount] = tempstrength[i];
         
         if (zonehi[zonecount] < Close[4])
            zonetype[zonecount] = ZONE_SUPPORT;
         else if (zonelo[zonecount] > Close[4])
            zonetype[zonecount] = ZONE_RESIST;
         else
         {
            for (j=5; j<1000; j++)
            {
               if (iClose(NULL, TimeFrame, j) < zonelo[zonecount])
               {
                  zonetype[zonecount] = ZONE_RESIST;
                  break;
               }
               else if (iClose(NULL, TimeFrame, j) > zonehi[zonecount])
               {
                  zonetype[zonecount] = ZONE_SUPPORT;
                  break;
               }
            }

            if (j == 1000)
               zonetype[zonecount] = ZONE_SUPPORT;
         }

         zonecount++;
      }
   }
}

void DrawZones()
{
   if (SetGlobals == true)
   {
      GlobalVariableSet("SSSR_Count_"+Symbol()+TimeFrame, zonecount);
      GlobalVariableSet("SSSR_Updated_"+Symbol()+TimeFrame, TimeCurrent());
   }

   for (int i=0; i<zonecount; i++)
   {
      if (zonestrength[i] == ZONE_WEAK && zoneshowweak == false)
         continue;

      string s = "SnR#"+i+" Strength=";
      if (zonestrength[i] == ZONE_PROVEN)
         s = s + "Proven, Test Count=" + zonehits[i];
      else if (zonestrength[i] == ZONE_VERIFIED)
         s = s + "Verified, Test Count=" + zonehits[i];
      else if (zonestrength[i] == ZONE_UNTESTED)
         s = s + "Untested";
      else if (zonestrength[i] == ZONE_TURNCOAT)
         s = s + "Turncoat";
      else
         s = s + "Weak";
/*
      ObjectCreate(s, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
      ObjectSet(s, OBJPROP_TIME1, iTime(NULL, TimeFrame, zonestart[i]));
      ObjectSet(s, OBJPROP_TIME2, TimeCurrent());
      ObjectSet(s, OBJPROP_PRICE1, zonehi[i]);
      ObjectSet(s, OBJPROP_PRICE2, zonelo[i]);
      ObjectSet(s, OBJPROP_BACK, zonesolid);
      ObjectSet(s, OBJPROP_WIDTH, zonelinewidth);
      ObjectSet(s, OBJPROP_STYLE, zonestyle);
*/

      ObjectCreate(s, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
      ObjectSet(s, OBJPROP_TIME1, iTime(NULL, TimeFrame, zonestart[i]));
      ObjectSet(s, OBJPROP_TIME2, TimeCurrent());
      if (zonetype[i] == ZONE_SUPPORT) {
         ObjectSet(s, OBJPROP_PRICE1, zonehi[i]);
         ObjectSet(s, OBJPROP_PRICE2, zonehi[i]);
      } else {
         ObjectSet(s, OBJPROP_PRICE1, zonelo[i]);
         ObjectSet(s, OBJPROP_PRICE2, zonelo[i]);
      }
      ObjectSet(s, OBJPROP_BACK, zonesolid);
      ObjectSet(s, OBJPROP_WIDTH, zonelinewidth);
      ObjectSet(s, OBJPROP_STYLE, zonestyle);

      if (zonetype[i] == ZONE_SUPPORT)
      {
         // support zone
         if (zonestrength[i] == ZONE_TURNCOAT)
            ObjectSet(s, OBJPROP_COLOR, colorsupportturncoat);
         else if (zonestrength[i] == ZONE_PROVEN)
            ObjectSet(s, OBJPROP_COLOR, colorsupportproven);
         else if (zonestrength[i] == ZONE_VERIFIED)
            ObjectSet(s, OBJPROP_COLOR, colorsupportverified);
         else if (zonestrength[i] == ZONE_UNTESTED)
            ObjectSet(s, OBJPROP_COLOR, colorsupportuntested);
         else
            ObjectSet(s, OBJPROP_COLOR, colorsupportweak);
      }
      else
      {
         // resistance zone
         if (zonestrength[i] == ZONE_TURNCOAT)
            ObjectSet(s, OBJPROP_COLOR, colorresistturncoat);
         else if (zonestrength[i] == ZONE_PROVEN)
            ObjectSet(s, OBJPROP_COLOR, colorresistproven);
         else if (zonestrength[i] == ZONE_VERIFIED)
            ObjectSet(s, OBJPROP_COLOR, colorresistverified);
         else if (zonestrength[i] == ZONE_UNTESTED)
            ObjectSet(s, OBJPROP_COLOR, colorresistuntested);
         else
            ObjectSet(s, OBJPROP_COLOR, colorresistweak);
      }



      if (SetGlobals == true)
      {
         GlobalVariableSet("SSSR_HI_"+Symbol()+TimeFrame+i, zonehi[i]);
         GlobalVariableSet("SSSR_LO_"+Symbol()+TimeFrame+i, zonelo[i]);
         GlobalVariableSet("SSSR_HITS_"+Symbol()+TimeFrame+i, zonehits[i]);
         GlobalVariableSet("SSSR_STRENGTH_"+Symbol()+TimeFrame+i, zonestrength[i]);
         GlobalVariableSet("SSSR_AGE_"+Symbol()+TimeFrame+i, zonestart[i]);
      }
   }
}

bool Fractal(int M, int P, int shift)
{
   if (TimeFrame > P)
      P = TimeFrame;
   
   P = P / TimeFrame*2 + MathCeil(P / TimeFrame / 2);
   
   if (shift < P)
      return(false);

   if (shift > iBars(Symbol(), TimeFrame)-P)
      return(false); 
   
   for (int i=1; i<=P; i++)
   {
      if (M == UP_POINT)
      {
         if (iHigh(NULL, TimeFrame, shift+i) > iHigh(NULL, TimeFrame, shift))
            return(false);
         if (iHigh(NULL, TimeFrame, shift-i) >= iHigh(NULL, TimeFrame, shift))
            return(false);     
      }
      if (M == DN_POINT)
      {
         if (iLow(NULL, TimeFrame, shift+i) < iLow(NULL, TimeFrame, shift))
            return(false);
         if (iLow(NULL, TimeFrame, shift-i) <= iLow(NULL, TimeFrame, shift))
            return(false);
      }        
   }
   return(true);   
}  

void FastFractals()
{
   int shift;
   int limit = MathMin(Bars-1, BackLimit);
   int P = TimeFrame * fractalfastfactor;

   FastUpPts[0] = 0.0; FastUpPts[1] = 0.0;
   FastDnPts[0] = 0.0; FastDnPts[1] = 0.0;

   for (shift=limit; shift>1; shift--)
   {
      if (Fractal(UP_POINT, P, shift) == true)
         FastUpPts[shift] = iHigh(NULL, TimeFrame, shift);
      else
         FastUpPts[shift] = 0.0;

      if (Fractal(DN_POINT, P, shift) == true)
         FastDnPts[shift] = iLow(NULL, TimeFrame, shift);
      else
         FastDnPts[shift] = 0.0;
   }
}

void SlowFractals()
{
   int shift;
   int limit = MathMin(iBars(Symbol(), TimeFrame) - 1, BackLimit);
   int P = TimeFrame * fractalslowfactor;

   SlowUpPts[0] = 0.0; SlowUpPts[1] = 0.0;
   SlowDnPts[0] = 0.0; SlowDnPts[1] = 0.0;

   for (shift=limit; shift>1; shift--)
   {
      if (Fractal(UP_POINT, P, shift) == true)
         SlowUpPts[shift] = iHigh(NULL, TimeFrame, shift);
      else
         SlowUpPts[shift] = 0.0;

      if (Fractal(DN_POINT, P, shift) == true)
         SlowDnPts[shift] = iLow(NULL, TimeFrame, shift);
      else
         SlowDnPts[shift] = 0.0;
   }
}

bool NewBar()
{
   static datetime LastTime = 0;
   if (iTime(NULL, TimeFrame, 0) != LastTime)
   {
      LastTime = iTime(NULL, TimeFrame, 0)+timeoffset;
      return (true);
   }
   else
      return (false);
}

void DeleteZones()
{
   int len = 5;
   int i;

   while (i < ObjectsTotal())
   {
      string objName = ObjectName(i);
      if (StringSubstr(objName, 0, len) != "SnR#")
      {
         i++;
         continue;
      }
      ObjectDelete(objName);
   }
}

string TimeFrameToString(int tf) //code by TRO
{
   string tfs;

   switch(tf)
   {
      case PERIOD_M1:
         tfs = "M1"  ;
         break;
      case PERIOD_M5:
         tfs = "M5"  ;
         break;
      case PERIOD_M15:
         tfs = "M15" ;
         break;
      case PERIOD_M30:
         tfs = "M30" ;
         break;
      case PERIOD_H1:
         tfs = "H1"  ;
         break;
      case PERIOD_H4:
         tfs = "H4"  ;
         break;
      case PERIOD_D1:
         tfs = "D1"  ;
         break;
      case PERIOD_W1:
         tfs = "W1"  ;
         break;
      case PERIOD_MN1:
         tfs = "MN";
   }

   return(tfs);
}

string StringRepeat(string str, int n = 1)
{
  string outstr = "";
  for(int i = 0; i < n; i++) outstr = outstr + str;
  return(outstr);
}

string StringRightPad(string str, int n=1, string str2=" ")
{
  return(str + StringRepeat(str2,n-StringLen(str)));
}