#include <Trade/Trade.mqh>
#include <Trade/OrderInfo.mqh>

enum ZONE_CLOSING_TYPE {
   NO_CLOSING = 0,
   CLOSING_OPEN_CLOSE = 1,
   CLOSING_HIGH_LOW = 2
};

enum CONTRACTION_RANGE_TYPE {
   RANGE_OPEN_CLOSE = 0,
   RANGE_HIGH_LOW = 1
};

enum ZONE_SHOWING_TYPE {
   NO_SHOWING = 0,
   SHOW_SUPPLY_AND_DEMAND = 1,
   SHOW_ALL_AS_SOLID_ZONE = 2,
   SHOW_ALL_AS_LINES_ZONE = 3,
};

class Zone{

   public: 
      double   Value;
      double   High;
      double   Low;

      int      HighestBar;
      int      LowestBar;
         
      int      BarStart;
      int      BarStop;
      datetime TimeStart;
      datetime TimeStop;
      
      bool     isCreated;

      bool     isSupplyZone;
      bool     isDemandZone;
   
      bool     isValueClosed;
      bool     isHighClosed;
      bool     isLowClosed;
      bool     isClosed;
   
      int      HighRayStop;
      int      LowRayStop;
      int      ValueRayStop;
      int      ContractionRayStop;

      datetime HighRayTimeStop;
      datetime LowRayTimeStop;
      datetime ValueRayTimeStop;
      datetime ContractionRayTimeStop;
         
      double   Volatility;
      
      string   Name;
      
      color    ContractionColor;
      color    ZoneColor;
      
      Zone(){};
};

class ZonesManager{

   private: 
      string                  inpSymbol;
      ENUM_TIMEFRAMES         inpTimeFrame;
      
      ZONE_CLOSING_TYPE       inpZoneClosingType;
      
      int                     inpATRPeriod;
      
      int                     inpMinimumBarsToCloseZone;

      double                  inpMinimumMomentumShiftFactor;
      
      int                     inpMinimumBars;
      double                  inpATRMinWidth ;
      double                  inpATRMaxWidth;
      CONTRACTION_RANGE_TYPE  intRangeType;
      
      ZONE_SHOWING_TYPE       inpZoneShowingType;
      color                   inpZonesColor;
      color                   inpDemandZonesColor;
      color                   inpSupplyZonesColor;
      
      string                  inpObjectPrefix;
      
      int                     inpBarsHistoryStart;
      int                     inpBarsHistoryStop;

      int                     ATRHandle;

      Zone                    Zones[];
      
      void                    FindZones(int paBarShift);
      void                    CheckMomentumBreakout(int paZoneIndex);
      void                    CloseZones();
      void                    ShowZones();
      double                  GetATR(int paBarShift);
      bool                    NewBar();
      
   public:
      ZonesManager(){};
      
      int      Init(string paSymbol, ENUM_TIMEFRAMES paTimeframe,
                     int paATRPeriod, int paMinimumBars, double paATRMinWidth, double paATRMaxWidth, CONTRACTION_RANGE_TYPE paRangeType,
                     double paMinimumMomentumShiftFactor,
                     ZONE_CLOSING_TYPE paClosingType, int paMinimumBarsAfterCheckClosingZone,
                     color paZonesColor, color paDemandZonesColor, color paSupplyZonesColor, ZONE_SHOWING_TYPE paZoneShowingType, string paObjectPrefix,                             
                     int paBarsHistoryStart, int paBarsHistoryStop);
      
      void     Tick();
      void     Deinit(const int reason);

};

class ZonesStrategy{

   private:
      
      ZonesManager      CurrentTFZone;
      ZonesManager      HigherTFZone;      

      CTrade            Trade;
      CPositionInfo     Position;
      CDealInfo         Deal;
      COrderInfo        Order;

      int               inpMagic;
            
   public:
     
      ZonesStrategy(){};
      

      int   OnInitEvent(string paCurrentZone_Symbol, ENUM_TIMEFRAMES paCurrentZone_Timeframe,
                        int paCurrentZone_ATRPeriod, int paCurrentZone_MinimumBars, double paCurrentZone_ATRMinWidth, double paCurrentZone_ATRMaxWidth, CONTRACTION_RANGE_TYPE paCurrentZone_RangeType,
                        double paCurrentZone_MinimumMomentumShiftFactor,
                        ZONE_CLOSING_TYPE paCurrentZone_ClosingType, int paCurrentZone_MinimumBarsAfterCheckClosingZone,
                        color paCurrentZone_ZonesColor, color paCurrentZone_DemandZonesColor, color paCurrentZone_SupplyZonesColor, ZONE_SHOWING_TYPE paCurrentZone_ZoneShowingType, string paCurrentZone_ObjectPrefix,   

                        string paHigherZone_Symbol, ENUM_TIMEFRAMES paHigherZone_Timeframe,
                        int paHigherZone_ATRPeriod, int paHigherZone_MinimumBars, double paHigherZone_ATRMinWidth, double paHigherZone_ATRMaxWidth, CONTRACTION_RANGE_TYPE paHigherZone_RangeType,
                        double paHigherZone_MinimumMomentumShiftFactor,
                        ZONE_CLOSING_TYPE paHigherZone_ClosingType, int paHigherZone_MinimumBarsAfterCheckClosingZone,
                        color paHigherZone_ZonesColor, color paHigherZone_DemandZonesColor, color paHigherZone_SupplyZonesColor, ZONE_SHOWING_TYPE paHigherZone_ZoneShowingType, string paHigherZone_ObjectPrefix,                             
                        
                        int paMagic, int paBarsHistoryStart, int paBarsHistoryStop);
                          
      void  OnTickEvent();
      void  OnDeinitEvent(const int reason);
   
};


//
//----------------------------------------------------------------------------------------------------------------------------------------
//

int ZonesStrategy :: OnInitEvent(string paCurrentZone_Symbol, ENUM_TIMEFRAMES paCurrentZone_Timeframe,
                                 int paCurrentZone_ATRPeriod, int paCurrentZone_MinimumBars, double paCurrentZone_ATRMinWidth, double paCurrentZone_ATRMaxWidth, CONTRACTION_RANGE_TYPE paCurrentZone_RangeType,
                                 double paCurrentZone_MinimumMomentumShiftFactor,
                                 ZONE_CLOSING_TYPE paCurrentZone_ClosingType, int paCurrentZone_MinimumBarsAfterCheckClosingZone,
                                 color paCurrentZone_ZonesColor, color paCurrentZone_DemandZonesColor, color paCurrentZone_SupplyZonesColor, ZONE_SHOWING_TYPE paCurrentZone_ZoneShowingType, string paCurrentZone_ObjectPrefix,   
         
                                 string paHigherZone_Symbol, ENUM_TIMEFRAMES paHigherZone_Timeframe,
                                 int paHigherZone_ATRPeriod, int paHigherZone_MinimumBars, double paHigherZone_ATRMinWidth, double paHigherZone_ATRMaxWidth, CONTRACTION_RANGE_TYPE paHigherZone_RangeType,
                                 double paHigherZone_MinimumMomentumShiftFactor,
                                 ZONE_CLOSING_TYPE paHigherZone_ClosingType, int paHigherZone_MinimumBarsAfterCheckClosingZone,
                                 color paHigherZone_ZonesColor, color paHigherZone_DemandZonesColor, color paHigherZone_SupplyZonesColor, ZONE_SHOWING_TYPE paHigherZone_ZoneShowingType, string paHigherZone_ObjectPrefix,                             
                                 
                                 int paMagic, int paBarsHistoryStart, int paBarsHistoryStop){

   
   inpMagic = paMagic;
   
   Trade.SetExpertMagicNumber(inpMagic);
      
   CurrentTFZone.Init(paCurrentZone_Symbol, paCurrentZone_Timeframe,
                     paCurrentZone_ATRPeriod, paCurrentZone_MinimumBars, paCurrentZone_ATRMinWidth, paCurrentZone_ATRMaxWidth, paCurrentZone_RangeType,
                     paCurrentZone_MinimumMomentumShiftFactor,
                     paCurrentZone_ClosingType, paCurrentZone_MinimumBarsAfterCheckClosingZone,
                     paCurrentZone_ZonesColor, paCurrentZone_DemandZonesColor, paCurrentZone_SupplyZonesColor, paCurrentZone_ZoneShowingType, paCurrentZone_ObjectPrefix,
                     paBarsHistoryStart, paBarsHistoryStop);
                     
   HigherTFZone.Init(paHigherZone_Symbol, paHigherZone_Timeframe,
                     paHigherZone_ATRPeriod, paHigherZone_MinimumBars, paHigherZone_ATRMinWidth, paHigherZone_ATRMaxWidth, paHigherZone_RangeType,
                     paHigherZone_MinimumMomentumShiftFactor,
                     paHigherZone_ClosingType, paHigherZone_MinimumBarsAfterCheckClosingZone,
                     paHigherZone_ZonesColor, paHigherZone_DemandZonesColor, paHigherZone_SupplyZonesColor, paHigherZone_ZoneShowingType, paHigherZone_ObjectPrefix,                             
                     paBarsHistoryStart, paBarsHistoryStop);
         
   return(INIT_SUCCEEDED);

}

void ZonesStrategy::OnTickEvent(){ 
   
   CurrentTFZone.Tick();
   
   HigherTFZone.Tick();
   
   return;
}

void ZonesStrategy :: OnDeinitEvent(const int reason){

   CurrentTFZone.Deinit(reason);
   
   HigherTFZone.Deinit(reason);
   
}

//
//----------------------------------------------------------------------------------------------------------------------------------------
//

bool ZonesManager :: NewBar(){

   static datetime previous_time = 0;
   datetime current_time = iTime(inpSymbol, inpTimeFrame, 0);
   if(previous_time != current_time){
      previous_time = current_time;
      return(true);
   }
   return(false);
}

void ZonesManager :: FindZones(int paBarShift){

   double HighestPrice = 0; 
   int HighestIndex = 0;
   double LowestPrice = DBL_MAX;
   int LowestIndex = 0;

   int ZonesTotal = ArraySize(Zones);
   int LastZoneIndex = ZonesTotal - 1;
   //if(ZonesTotal == 0) LastZoneIndex = 0;

   int IndexBarStartContraction = paBarShift + inpMinimumBars - 1;
  
   // CHECK UPDATE OR CREATE CONTRACTION NEEDED 
   bool CreateNewContraction = false;
   if(ZonesTotal == 0){
      CreateNewContraction = true;
   }else{
      if(Zones[LastZoneIndex].isCreated){
         CreateNewContraction = true;
      }else{
         CreateNewContraction = false;
         IndexBarStartContraction = Zones[LastZoneIndex].BarStart; 
      }  
   }

   // CHECK CURRENT CONTRACTION HIGHEST/LOWEST PRICE  
   for(int i = paBarShift; i <= IndexBarStartContraction; i++){
   
      double Price1 = 0;
      double Price2 = 0;
      
      if(intRangeType == RANGE_OPEN_CLOSE){
         Price1 = iOpen(inpSymbol, inpTimeFrame, i);
         Price2 = iClose(inpSymbol, inpTimeFrame, i);
      }

      if(intRangeType == RANGE_HIGH_LOW){
         Price1 = iHigh(inpSymbol, inpTimeFrame, i);
         Price2 = iLow(inpSymbol, inpTimeFrame, i);
      }

      if(Price1 > HighestPrice){
         HighestPrice = Price1; 
         HighestIndex = i;
      }
         
      if(Price1 < LowestPrice){
         LowestPrice = Price1; 
         LowestIndex = i;
      }
 
      if(Price2 > HighestPrice){
         HighestPrice = Price2; 
         HighestIndex = i;
      }
         
      if(Price2 < LowestPrice){
         LowestPrice = Price2; 
         LowestIndex = i;
      }
   }
  
   double ContractionWidht = HighestPrice - LowestPrice;
   double Volatility = GetATR(IndexBarStartContraction);

  
   // CHECK FOR UPDATE LAST CONTRACTION
   if(!CreateNewContraction){
      
      double ContractionMinWidth = Zones[LastZoneIndex].Volatility * inpATRMinWidth;
      double ContractionMaxWidth = Zones[LastZoneIndex].Volatility * inpATRMaxWidth;

      if(ContractionWidht >= ContractionMinWidth && ContractionWidht <= ContractionMaxWidth){
         
         Zones[LastZoneIndex].High = HighestPrice;
         Zones[LastZoneIndex].HighestBar = HighestIndex;

         Zones[LastZoneIndex].Low = LowestPrice;
         Zones[LastZoneIndex].LowestBar = LowestIndex;

         Zones[LastZoneIndex].Value = HighestPrice - (HighestPrice - LowestPrice) / 2.0;

         Zones[LastZoneIndex].TimeStop = iTime(inpSymbol, inpTimeFrame, paBarShift - 1);
         Zones[LastZoneIndex].BarStop = paBarShift;

         Zones[LastZoneIndex].isCreated = false;

         Zones[LastZoneIndex].isValueClosed = false;
         Zones[LastZoneIndex].isHighClosed = false;
         Zones[LastZoneIndex].isLowClosed = false;
         Zones[LastZoneIndex].isClosed = false;

         Zones[LastZoneIndex].HighRayStop = INT_MIN;
         Zones[LastZoneIndex].LowRayStop = INT_MIN;
         Zones[LastZoneIndex].ValueRayStop = INT_MIN;
         Zones[LastZoneIndex].ContractionRayStop = INT_MIN;
      
         Zones[LastZoneIndex].HighRayTimeStop = TimeCurrent();
         Zones[LastZoneIndex].LowRayTimeStop = TimeCurrent();
         Zones[LastZoneIndex].ValueRayTimeStop = TimeCurrent();
         Zones[LastZoneIndex].ContractionRayTimeStop = TimeCurrent();

         Zones[LastZoneIndex].isSupplyZone = false;
         Zones[LastZoneIndex].isDemandZone = false;

         Zones[LastZoneIndex].Volatility = Volatility;
      
      }
      else{
      
         Zones[LastZoneIndex].isCreated = true;
         CheckMomentumBreakout(LastZoneIndex);
         FindZones(paBarShift);
      }
   }

   // CREATE NEW CONTRACTION
   if(CreateNewContraction){
      
      double ContractionMinWidth = Volatility * inpATRMinWidth;
      double ContractionMaxWidth = Volatility * inpATRMaxWidth;

/* for debugging        
      if(iTime(inpSymbol, inpTimeFrame, paBarShift) == StringToTime("2023.02.03 14:00")){

         Print("High = " + DoubleToString(iHigh(inpSymbol, inpTimeFrame, paBarShift)) + " Low = " + DoubleToString(iLow(inpSymbol, inpTimeFrame, paBarShift)) 
               + " Volatility = " + DoubleToString(Volatility) 
               + " HighestPrice = " + DoubleToString(HighestPrice) + " LowestPrice = " + DoubleToString(LowestPrice), 
               " ContractionWidht = " + DoubleToString(ContractionWidht) + " ContractionMinWidth = " + DoubleToString(ContractionMinWidth) + " ContractionMaxWidth = " + DoubleToString(ContractionMaxWidth) );
      }
*/      
             
      if(ContractionWidht >= ContractionMinWidth && ContractionWidht <= ContractionMaxWidth){
         
         ArrayResize(Zones, ZonesTotal + 1);
   
         Zones[LastZoneIndex + 1].High = HighestPrice;
         Zones[LastZoneIndex + 1].HighestBar = HighestIndex;

         Zones[LastZoneIndex + 1].Low = LowestPrice;
         Zones[LastZoneIndex + 1].LowestBar = LowestIndex;

         Zones[LastZoneIndex + 1].Value = HighestPrice - (HighestPrice - LowestPrice) / 2.0;

         Zones[LastZoneIndex + 1].TimeStart = iTime(inpSymbol, inpTimeFrame, IndexBarStartContraction);
         Zones[LastZoneIndex + 1].BarStart = IndexBarStartContraction;

         Zones[LastZoneIndex + 1].TimeStop = iTime(inpSymbol, inpTimeFrame, paBarShift - 1);
         Zones[LastZoneIndex + 1].BarStop = paBarShift;

         Zones[LastZoneIndex + 1].isCreated = false;
         
         Zones[LastZoneIndex + 1].Name = inpObjectPrefix + " (" + IntegerToString(LastZoneIndex + 1) + ") ";

         Zones[LastZoneIndex + 1].isValueClosed = false;
         Zones[LastZoneIndex + 1].isHighClosed = false;
         Zones[LastZoneIndex + 1].isLowClosed = false;
         Zones[LastZoneIndex + 1].isClosed = false;
         
         Zones[LastZoneIndex + 1].HighRayStop = INT_MIN;
         Zones[LastZoneIndex + 1].LowRayStop = INT_MIN;
         Zones[LastZoneIndex + 1].ValueRayStop = INT_MIN;
         Zones[LastZoneIndex + 1].ContractionRayStop = INT_MIN;
      
         Zones[LastZoneIndex + 1].HighRayTimeStop = TimeCurrent();
         Zones[LastZoneIndex + 1].LowRayTimeStop = TimeCurrent();
         Zones[LastZoneIndex + 1].ValueRayTimeStop = TimeCurrent();
         Zones[LastZoneIndex + 1].ContractionRayTimeStop = TimeCurrent();

         Zones[LastZoneIndex + 1].isSupplyZone = false;
         Zones[LastZoneIndex + 1].isDemandZone = false;
         Zones[LastZoneIndex + 1].ContractionColor = inpZonesColor;

         Zones[LastZoneIndex + 1].Volatility = Volatility;
      }
   } 
}

void ZonesManager :: CloseZones(){

   int ZonesTotal = ArraySize(Zones);

   for(int i = 0; i < ZonesTotal; i++){

      if(Zones[i].isCreated && !Zones[i].isClosed){ 

         for(int j = (Zones[i].BarStop - inpMinimumBarsToCloseZone - 1); j > 0; j--){

            double PriceHigh = 0.0;
            double PriceLow = 0.0; 
            
            if(inpZoneClosingType == CLOSING_OPEN_CLOSE){
               if(iClose(inpSymbol, inpTimeFrame, j) >= iOpen(inpSymbol, inpTimeFrame, j)){ 
                  PriceHigh = iClose(inpSymbol, inpTimeFrame, j);
                  PriceLow = iOpen(inpSymbol, inpTimeFrame, j);
               }else{ 
                  PriceHigh = iOpen(inpSymbol, inpTimeFrame, j);
                  PriceLow = iClose(inpSymbol, inpTimeFrame, j);
               }
            }
            
            if(inpZoneClosingType == CLOSING_HIGH_LOW){
               PriceHigh = iHigh(inpSymbol, inpTimeFrame, j);
               PriceLow = iLow(inpSymbol, inpTimeFrame, j);
            }
            
            if(!Zones[i].isHighClosed && Zones[i].High <= PriceHigh && Zones[i].High >= PriceLow){

               Zones[i].isHighClosed = true;
               Zones[i].HighRayStop = j;            
               Zones[i].HighRayTimeStop = iTime(inpSymbol, inpTimeFrame, j);            

            }
            
            if(!Zones[i].isLowClosed && Zones[i].Low <= PriceHigh && Zones[i].Low >= PriceLow){

               Zones[i].isLowClosed = true;
               Zones[i].LowRayStop = j;            
               Zones[i].LowRayTimeStop = iTime(inpSymbol, inpTimeFrame, j);            
            }

            if(!Zones[i].isValueClosed && Zones[i].Value <= PriceHigh && Zones[i].Value >= PriceLow){

               Zones[i].isValueClosed = true;
               Zones[i].ValueRayStop = j;            
               Zones[i].ValueRayTimeStop = iTime(inpSymbol, inpTimeFrame, j);               
            }

            if(!Zones[i].isClosed && Zones[i].isLowClosed && Zones[i].isHighClosed){

               Zones[i].isClosed = true;
               Zones[i].ContractionRayStop = j;            
               Zones[i].ContractionRayTimeStop = iTime(inpSymbol, inpTimeFrame, j);  
            }             
         }    
      }
   } 
}

void ZonesManager :: CheckMomentumBreakout(int paZoneIndex){

   int StartZoneBar = Zones[paZoneIndex].BarStop;
   double Volatility = Zones[paZoneIndex].Volatility;
   
   if(iClose(inpSymbol, inpTimeFrame, StartZoneBar - 1) > iOpen(inpSymbol, inpTimeFrame, StartZoneBar - 1))
      if(iClose(inpSymbol, inpTimeFrame, StartZoneBar - 2) > iOpen(inpSymbol, inpTimeFrame, StartZoneBar - 2))
         if(iClose(inpSymbol, inpTimeFrame, StartZoneBar - 3) > iOpen(inpSymbol, inpTimeFrame, StartZoneBar - 3)){
   
            double MomentumShift = iHigh(inpSymbol, inpTimeFrame, StartZoneBar - 3) - iLow(inpSymbol, inpTimeFrame, StartZoneBar - 1);
            
            if(MomentumShift >= Volatility * inpMinimumMomentumShiftFactor){
               Zones[paZoneIndex].isDemandZone = true;
               Zones[paZoneIndex].ZoneColor = inpDemandZonesColor;
            }
         }
   
   if(iClose(inpSymbol, inpTimeFrame, StartZoneBar - 1) < iOpen(inpSymbol, inpTimeFrame, StartZoneBar - 1))
      if(iClose(inpSymbol, inpTimeFrame, StartZoneBar - 2) < iOpen(inpSymbol, inpTimeFrame, StartZoneBar - 2))
         if(iClose(inpSymbol, inpTimeFrame, StartZoneBar - 3) < iOpen(inpSymbol, inpTimeFrame, StartZoneBar - 3)){
   
            double MomentumShift = iHigh(inpSymbol, inpTimeFrame, StartZoneBar - 1) - iLow(inpSymbol, inpTimeFrame, StartZoneBar - 3);
            
            if(MomentumShift >= Volatility * inpMinimumMomentumShiftFactor){
               Zones[paZoneIndex].isSupplyZone = true;
               Zones[paZoneIndex].ZoneColor = inpSupplyZonesColor;
            }
         }
}

void ZonesManager :: ShowZones(){

   int ZonesTotal = ArraySize(Zones);

   for(int i = 0; i < ZonesTotal; i++){

      if(inpZoneShowingType == SHOW_ALL_AS_SOLID_ZONE){

         string ContractionAreaName = Zones[i].Name + " | Contraction Area";
         if(ObjectFind(0, ContractionAreaName) < 0){
            ObjectCreate(0, ContractionAreaName, OBJ_RECTANGLE, 0, Zones[i].TimeStart, Zones[i].High, Zones[i].TimeStop, Zones[i].Low);  
            ObjectSetInteger(0,ContractionAreaName ,OBJPROP_FILL, true);           
            ObjectSetInteger(0,ContractionAreaName ,OBJPROP_COLOR, Zones[i].ContractionColor);
         }else{
            ObjectSetInteger(0,ContractionAreaName,OBJPROP_TIME, 0, Zones[i].TimeStart);        
            ObjectSetDouble(0,ContractionAreaName,OBJPROP_PRICE, 0, Zones[i].High);                   
            ObjectSetInteger(0,ContractionAreaName,OBJPROP_TIME, 1, Zones[i].TimeStop);        
            ObjectSetDouble(0,ContractionAreaName,OBJPROP_PRICE, 1, Zones[i].Low);        
         }

         string ContractionZoneName = Zones[i].Name + " | Contraction Zone";
         if(ObjectFind(0, ContractionZoneName) < 0){
            ObjectCreate(0, ContractionZoneName, OBJ_RECTANGLE, 0, Zones[i].TimeStop, Zones[i].High, Zones[i].ContractionRayTimeStop, Zones[i].Low);  
            ObjectSetInteger(0,ContractionZoneName ,OBJPROP_FILL, true);           
            ObjectSetInteger(0,ContractionZoneName ,OBJPROP_COLOR, Zones[i].ContractionColor);
         }else{
            ObjectSetInteger(0,ContractionZoneName,OBJPROP_TIME, 0, Zones[i].TimeStop);        
            ObjectSetDouble(0,ContractionZoneName,OBJPROP_PRICE, 0, Zones[i].High);                   
            ObjectSetInteger(0,ContractionZoneName,OBJPROP_TIME, 1, Zones[i].ContractionRayTimeStop);        
            ObjectSetDouble(0,ContractionZoneName,OBJPROP_PRICE, 1, Zones[i].Low);        
         }
      }
      
      if(inpZoneShowingType == SHOW_SUPPLY_AND_DEMAND){
         
         if(Zones[i].isDemandZone || Zones[i].isSupplyZone){

            string ContractionAreaName = Zones[i].Name + " | Contraction Area";
            if(ObjectFind(0, ContractionAreaName) < 0){
               ObjectCreate(0, ContractionAreaName, OBJ_RECTANGLE, 0, Zones[i].TimeStart, Zones[i].High, Zones[i].TimeStop, Zones[i].Low);  
               ObjectSetInteger(0,ContractionAreaName ,OBJPROP_FILL, true);           
               ObjectSetInteger(0,ContractionAreaName ,OBJPROP_COLOR, Zones[i].ContractionColor);
            }else{
               ObjectSetInteger(0,ContractionAreaName,OBJPROP_TIME, 0, Zones[i].TimeStart);        
               ObjectSetDouble(0,ContractionAreaName,OBJPROP_PRICE, 0, Zones[i].High);                   
               ObjectSetInteger(0,ContractionAreaName,OBJPROP_TIME, 1, Zones[i].TimeStop);        
               ObjectSetDouble(0,ContractionAreaName,OBJPROP_PRICE, 1, Zones[i].Low);        
            }

            string ContractionZoneName = Zones[i].Name + " | SupplyDemand Zone";
            if(ObjectFind(0, ContractionZoneName) < 0){
               ObjectCreate(0, ContractionZoneName, OBJ_RECTANGLE, 0, Zones[i].TimeStop, Zones[i].High, Zones[i].ContractionRayTimeStop, Zones[i].Low);  
               ObjectSetInteger(0,ContractionZoneName ,OBJPROP_FILL, true);           
               ObjectSetInteger(0,ContractionZoneName ,OBJPROP_COLOR, Zones[i].ZoneColor);
            }else{
               ObjectSetInteger(0,ContractionZoneName,OBJPROP_TIME, 0, Zones[i].TimeStop);        
               ObjectSetDouble(0,ContractionZoneName,OBJPROP_PRICE, 0, Zones[i].High);                   
               ObjectSetInteger(0,ContractionZoneName,OBJPROP_TIME, 1, Zones[i].ContractionRayTimeStop);        
               ObjectSetDouble(0,ContractionZoneName,OBJPROP_PRICE, 1, Zones[i].Low);        
            }
         }
      }

      if(inpZoneShowingType == SHOW_ALL_AS_LINES_ZONE){


         string ContractionAreaName = Zones[i].Name + " | Contraction Area";
         if(ObjectFind(0, ContractionAreaName) < 0){
            ObjectCreate(0, ContractionAreaName, OBJ_RECTANGLE, 0, Zones[i].TimeStart, Zones[i].High, Zones[i].TimeStop, Zones[i].Low);  
            ObjectSetInteger(0,ContractionAreaName ,OBJPROP_FILL, true);           
            ObjectSetInteger(0,ContractionAreaName ,OBJPROP_COLOR, Zones[i].ContractionColor);
         }else{
            ObjectSetInteger(0,ContractionAreaName,OBJPROP_TIME, 0, Zones[i].TimeStart);        
            ObjectSetDouble(0,ContractionAreaName,OBJPROP_PRICE, 0, Zones[i].High);                   
            ObjectSetInteger(0,ContractionAreaName,OBJPROP_TIME, 1, Zones[i].TimeStop);        
            ObjectSetDouble(0,ContractionAreaName,OBJPROP_PRICE, 1, Zones[i].Low);        
         }
   
         string ValueLineName = Zones[i].Name + " | Value";
         if(ObjectFind(0, ValueLineName) < 0){
            ObjectCreate(0, ValueLineName, OBJ_TREND, 0, Zones[i].TimeStop, Zones[i].Value, Zones[i].ValueRayTimeStop, Zones[i].Value);  
            ObjectSetInteger(0,ValueLineName, OBJPROP_COLOR,  Zones[i].ContractionColor);
         }else{
            ObjectSetInteger(0,ValueLineName,OBJPROP_TIME, 0, Zones[i].TimeStop);        
            ObjectSetDouble(0,ValueLineName,OBJPROP_PRICE, 0, Zones[i].Value);        
            ObjectSetInteger(0,ValueLineName,OBJPROP_TIME, 1, Zones[i].ValueRayTimeStop);        
            ObjectSetDouble(0,ValueLineName,OBJPROP_PRICE, 1, Zones[i].Value);        
         }

         string HighLineName = Zones[i].Name + " | High";
         if(ObjectFind(0, HighLineName) < 0){
            ObjectCreate(0, HighLineName, OBJ_TREND, 0, Zones[i].TimeStop, Zones[i].High, Zones[i].HighRayTimeStop, Zones[i].High);  
            ObjectSetInteger(0,HighLineName, OBJPROP_COLOR,  Zones[i].ContractionColor);
            ObjectSetInteger(0,HighLineName, OBJPROP_STYLE, STYLE_DOT);
         }else{
            ObjectSetInteger(0,HighLineName,OBJPROP_TIME, 0, Zones[i].TimeStop);        
            ObjectSetDouble(0,HighLineName,OBJPROP_PRICE, 0, Zones[i].High);        
            ObjectSetInteger(0,HighLineName,OBJPROP_TIME, 1, Zones[i].HighRayTimeStop);        
            ObjectSetDouble(0,HighLineName,OBJPROP_PRICE, 1, Zones[i].High);        
         }
     
         string LowLineName = Zones[i].Name + " | Low";
         if(ObjectFind(0, LowLineName) < 0){
            ObjectCreate(0, LowLineName, OBJ_TREND, 0, Zones[i].TimeStop, Zones[i].Low, Zones[i].LowRayTimeStop, Zones[i].Low);  
            ObjectSetInteger(0,LowLineName, OBJPROP_COLOR,  Zones[i].ContractionColor);
            ObjectSetInteger(0,LowLineName, OBJPROP_STYLE, STYLE_DOT);
         }else{
            ObjectSetInteger(0,LowLineName,OBJPROP_TIME, 0, Zones[i].TimeStop);        
            ObjectSetDouble(0,LowLineName,OBJPROP_PRICE, 0, Zones[i].Low);        
            ObjectSetInteger(0,LowLineName,OBJPROP_TIME, 1, Zones[i].LowRayTimeStop);        
            ObjectSetDouble(0,LowLineName,OBJPROP_PRICE, 1, Zones[i].Low);        
         }
      }
   }   
   ChartRedraw(0);
}

double ZonesManager :: GetATR(int paBarShift){
      
   double ATRArray[];
   CopyBuffer(ATRHandle, 0, paBarShift, 1, ATRArray);
   return ATRArray[0];
}

int ZonesManager :: Init(string paSymbol, ENUM_TIMEFRAMES paTimeframe,
                           int paATRPeriod, int paMinimumBars, double paATRMinWidth, double paATRMaxWidth, CONTRACTION_RANGE_TYPE paRangeType,
                           double paMinimumMomentumShiftFactor,
                           ZONE_CLOSING_TYPE paClosingType, int paMinimumBarsAfterCheckClosingZone,
                           color paZonesColor, color paDemandZonesColor, color paSupplyZonesColor, ZONE_SHOWING_TYPE paZoneShowingType, string paObjectPrefix,                             
                           int paBarsHistoryStart, int paBarsHistoryStop){
                                 

   inpSymbol = paSymbol;
   inpTimeFrame = paTimeframe;
         
   inpZoneClosingType = paClosingType;
         
   inpATRPeriod = paATRPeriod;
         
   inpMinimumBarsToCloseZone = paMinimumBarsAfterCheckClosingZone;
   
   inpMinimumMomentumShiftFactor = paMinimumMomentumShiftFactor;
         
   inpMinimumBars = paMinimumBars;
   inpATRMinWidth = paATRMinWidth;
   inpATRMaxWidth = paATRMaxWidth;
   intRangeType = paRangeType;
         
   inpZonesColor = paZonesColor;
   inpDemandZonesColor = paDemandZonesColor;
   inpSupplyZonesColor = paSupplyZonesColor;
   inpZoneShowingType = paZoneShowingType;
   inpObjectPrefix = paObjectPrefix;
         
   inpBarsHistoryStart = paBarsHistoryStart;
   inpBarsHistoryStop = paBarsHistoryStop;
   
   ATRHandle = iATR(inpSymbol, inpTimeFrame, paATRPeriod);

   int HistoryBarsStart = paBarsHistoryStart;
   
   if(HistoryBarsStart > iBars(inpSymbol, inpTimeFrame)) HistoryBarsStart = iBars(inpSymbol, inpTimeFrame) - 1;
   
   for(int i = HistoryBarsStart; i >= paBarsHistoryStop; i--)
      FindZones(i);
   
   if(inpZoneClosingType != NO_CLOSING) CloseZones();
   
   if(inpZoneShowingType != NO_SHOWING) ShowZones();

   return (INIT_SUCCEEDED);
}

void ZonesManager :: Tick(){


   if(!NewBar()) return;

   FindZones(1);
   
   if(inpZoneClosingType != NO_CLOSING) CloseZones();
   
   if(inpZoneShowingType != NO_SHOWING) ShowZones();

}

void ZonesManager :: Deinit(const int reason){

   ObjectsDeleteAll(0, inpObjectPrefix); 
   ArrayFree(Zones);
}
