#property copyright "PiKG, master_255, Trade Like A Pro"
#define   EAName    "Piranha"
#define   VER       "1.5.3"
#property version   VER 
#property link      "http://forum.tradelikeapro.ru/index.php?topic=16042.0"
#property strict
#include <stderror.mqh>
#include <stdlib.mqh>

#include <Canvas\Canvas.mqh>
CCanvas canvas_PANEL;

enum  _TurnFilter  {
                  TurnFilterATR     = 0,             //TurnFilter ATR
                  TurnFilterPZ_ATR  = 1,             //TurnFilter PZ & ATR
                  None              = 2              //None
      };
      
//---
sinput   string     _0_ = "==================================================================================="; // -
extern   int               MAGIC                = 0;
extern   int               Slippage             = 5;
extern   double            Lots                 = 0.01;
extern   int               DepoPer001Lot        = 0;
extern   string            SetName              = "[Piranha][H1]";   
sinput   string     _1_ = "==================================================================================="; // -
extern   bool              UseTradeTime_1Order  = true;           //UseTradeTime for First Order
extern   bool              UseTradeTime_Next    = false;          //UseTradeTime for Next Orders
extern   int               StartHour            = 8;
extern   int               EndHour              = 21;
extern   bool              MONDAY_Enabled       = true;           //MONDAY
extern   bool              TUESDAY_Enabled      = true;           //TUESDAY
extern   bool              WEDNESDAY_Enabled    = true;           //WEDNESDAY
extern   bool              THURSDAY_Enabled     = true;           //THURSDAY
extern   bool              FRIDAY_Enabled       = true;           //FRIDAY
sinput   string     _2_ = "==================================================================================="; // -
extern   ENUM_TIMEFRAMES   timeframe            = PERIOD_CURRENT; //Рабочий таймфрейм
sinput   string    _atr = " Настройки ATR H1-Channels";           // > > >
extern   int               a__PeriodATR         = 500;            // PeriodATR
extern   int               a__MaPeriod          = 34;             // MA Period
extern   ENUM_MA_METHOD    a__MaTypr            = 3;              // MA Type
extern   double            a__Mult_Factor1      = 1.61852;        // Mult Factor 1
extern   double            a__Mult_Factor2      = 3.26704;        // Mult Factor 2
extern   double            a__Mult_Factor3      = 4.85556;        // Mult Factor 3
sinput   string   _macd = " Настройки MACD";                      // > > >
extern   int               macd_FastEma         = 24;             // FastEMA
extern   int               macd_SlowEma         = 48;             // SlowEMA
extern   int               macd_SignalEma       = 12;             // SignalSMA
extern   int               macd_atr_shift       = 0;
sinput   string _filter = " Фильтры первого ордера";              // > > >
extern   bool              FilterADX            = false;          //Filter ADX
extern   int               FilterADX_Period     = 14;
extern   int               FilterADX_Level      = 40;
sinput   string    S_5_ = "==================================================================================="; // -
extern   int               PipStep              = 15;
extern   double            LotExponent          = 2;
extern   double            LotExponent2         = 1.5;
extern   int               Profit               = 50;
extern   int               Lvl1                 = 4;
extern   int               Profit_Lvl1          = 50;
extern   int               Lvl2                 = 6;
extern   int               Profit_Lvl2          = 30;
extern   int               TimeExit_Start       = 4;
extern   int               TimeExit_End         = 99;
extern   int               TimeExit_Minutes     = 240;
extern   int               TimeExit_Profit      = 10;
sinput   string    _6_ = "==================================================================================="; // -
extern   _TurnFilter       TurnMainFilters      = 1;              //Turn Main Filters
extern   bool              TurnFilterADX        = false;          //TurnFilter ADX
extern   int               ADX_Period           = 14;
extern   int               ADX_Level            = 22;
extern   bool              TurnFilterCCI        = false;          //TurnFilter CCI
extern   int               CCI_StartAfter       = 3;
extern   int               CCI_Period           = 14;
extern   int               CCI_Level            = 90;
sinput   string    _7_ = "==================================================================================="; // -
extern   bool              VisualDebug          = true;
extern   bool              showinfopanel        = true;
extern   color             Col_info             = clrDeepSkyBlue; //Panel Color
extern   uchar             Panel_Transparency   = 220;            //Panel Transparency 

//-------------------------------------------------------------------------
int lottextpoint = 6; 

int PipsDivided = 1;
struct SetkaEnv{
   int      count;
   int      count_buy;
   int      count_sell;
   double   countlot_buy;
   double   countlot_sell;
   double   lastprice;
   int      lasttype;
   datetime lasttime;
   int      last_distance;
   double   profit;
   int      ppoint;
   double   firstlot;
   double   avg;
};
SetkaEnv env;

int stoplevel,lotsize;
double minlot,maxlot,lotstep;

datetime _TimeCurrent, _time;

struct _orders{
   int ticket;
   int type;
   double lots;
   double profit;
};
_orders orders[];

struct _stats{
   int maxcount;
   int sumcount;
   int count;
};
_stats stat;

double tickvalue;
int freezlevel;
         
string time_trade = "";
bool RealTrade = (!IsTesting() && !IsOptimization() && !IsVisualMode());

//-------------------------------------------------------------------------
int OnInit(){

   if (IsOptimization()) { 
      showinfopanel=false; VisualDebug=false; 
   } 
    
   if(showinfopanel && (RealTrade || (IsTesting() && IsVisualMode()))) { //создание заднего фона   
      canvas_PANEL.CreateBitmapLabel("Piranha|info_panel",3,15,400,400,COLOR_FORMAT_ARGB_NORMALIZE);
      int length = 190;
      canvas_PANEL.FillRectangle(0,0,150,length, ColorToARGB(Col_info,Panel_Transparency));  
      canvas_PANEL.Update();
   }
   
   stoplevel = (int)MarketInfo(_Symbol, MODE_STOPLEVEL);
   minlot = MarketInfo(_Symbol, MODE_MINLOT);
   maxlot = MarketInfo(_Symbol, MODE_MAXLOT);
   lotsize = (int)MarketInfo(_Symbol, MODE_LOTSIZE);
   lotstep = MarketInfo(_Symbol, MODE_LOTSTEP);
   freezlevel = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  if(StringFind(EAName, "Piranh", 0)==-1) return(INIT_FAILED);
   _time = iTime(NULL, timeframe, 0);
   tickvalue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
   if( Digits == 5 || Digits == 3 ){
      PipsDivided *= 10;
      Slippage *= 10;
      PipStep *= 10;
      Profit *= 10;
      Profit_Lvl1 *= 10;
      Profit_Lvl2 *= 10;
      TimeExit_Profit *= 10;
      lottextpoint *= 10;
   }
   
   HideTestIndicators(false); 
   
   if(!UseTradeTime_1Order && !UseTradeTime_Next) time_trade = "\n  Unlimite trade time";
   else time_trade = "\n  Start Trade: " + (StartHour < 10 ? "0" + (string)StartHour : (string)StartHour) + ":00"
      + "\n  End Trade:  " + (EndHour < 10 ? "0" + (string)EndHour : (string)EndHour) + ":00"; 
   
   Comment(EAName + ", ver: " + (string)VER);
   
   return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason){Comment("");}
//+------------------------------------------------------------------+
//
void OnTick(){
   CalcSetkaEnv();
   
   if(showinfopanel){
      string info_panel =
        "\n             " + EAName + ", ver: " + (string)VER
      + "\n           for TRADELikeAPro" 
      + "\n  ----------------------------------------------" 
      + "\n  Set: " + SetName       
      + "\n  ----------------------------------------------" 
      + "\n  Current Spread: " + DoubleToStr((Ask - Bid)/_Point/PipsDivided,1)
      + "\n  ----------------------------------------------"  
      + time_trade
      + "\n  ----------------------------------------------"
      + "\n  Orders Buy: " + IntegerToString(env.count_buy) + ". Sum Lot: " + DoubleToStr(env.countlot_buy,2)
      + "\n  Orders Sell: " + IntegerToString(env.count_sell)+ ". Sum Lot: " + DoubleToStr(env.countlot_sell,2)
      + "\n  Distance to BE: " + (env.count > 0 ? DoubleToStr(MathAbs(Bid-env.avg)/_Point/PipsDivided,1) : "0") + " pips"
      + "\n  Total profit: " + DoubleToStr(env.profit,2) + " "+ AccountCurrency()
      + "\n  ----------------------------------------------";
            
      Comment(info_panel);
   }
   
   if(env.count>0){
      _TimeCurrent = TimeCurrent();

      if(DayOfWeek()==1 && (_TimeCurrent - env.lasttime)>86400) env.lasttime += 172800;

      if(env.count >= Lvl2 && env.ppoint >= Profit_Lvl2){
         DrawWarn("Exit: ","By profit(lvl2)",clrOrange);
         Print("Close all by profit(lvl2): ",DS(env.profit)," $, ",DS(env.ppoint)," pips, ",env.count," orders");
         CloseAll();
      }else
      if(env.count >= Lvl1 && env.count < Lvl2 && env.ppoint >= Profit_Lvl1){
         DrawWarn("Exit: ","By profit(lvl1)",clrOrange);
         Print("Close all by profit(lvl1): ",DS(env.profit)," $, ",DS(env.ppoint)," pips, ",env.count," orders");
         CloseAll();
      }else
      if(env.count < Lvl1 && env.ppoint >= Profit){
         DrawWarn("Exit: ","By profit",clrOrange);
         Print("Close all by profit: ",DS(env.profit)," $, ",DS(env.ppoint)," pips, ",env.count," orders");
         CloseAll();
      }else
      if(env.count >= TimeExit_Start && env.count <= TimeExit_End && env.ppoint >= TimeExit_Profit && _TimeCurrent - env.lasttime > 60 * TimeExit_Minutes){
         DrawWarn("Exit: ","Time Filter",clrOrange);
         Print("Close all by time: ",IntegerToString((_TimeCurrent-env.lasttime)/60)," min, profit: ",DS(env.profit)," $, ",DS(env.ppoint)," pips, ",env.count," orders");
         CloseAll();
      }else{
         if(env.last_distance>=PipStep && env.lasttime<_TimeCurrent-900){
            if(UseTradeTime_Next){
               if(!HourOk()){
                  DrawWarn("Time Filter On","",clrMediumVioletRed);
                  return;
               }   
            }
            
            if(TurnMainFilters == 0){
               double upLineATR = ATR_GoldUp(0);
               double dnLineATR = ATR_GoldDn(0);
               DrawChannel("up",upLineATR,clrRoyalBlue);
               DrawChannel("down",dnLineATR,clrRoyalBlue); 
               if(env.lasttype==OP_BUY && Bid>=dnLineATR) return;
               if(env.lasttype==OP_SELL && Ask<=upLineATR) return;
               DrawWarn("Enter: ","ATR",clrLimeGreen);
            }else         
            if(TurnMainFilters == 1){
               double up=0, down=0;
               double upLinePZ = UpLineIndicator(timeframe, 0);
               double dnLinePZ = DnLineIndicator(timeframe, 0);
               double upLineATR = ATR_GoldUp(0);
               double dnLineATR = ATR_GoldDn(0);
               up = (upLineATR+upLinePZ)/2;
               down = (dnLinePZ+dnLineATR)/2;
               DrawChannel("up",up,clrRoyalBlue);
               DrawChannel("down",down,clrRoyalBlue); 
               if(env.lasttype==OP_BUY && Bid>=down) return;
               if(env.lasttype==OP_SELL && Ask<=up) return;
               DrawWarn("Enter: ","PZ & ATR",clrLimeGreen);
            }
            
            if(TurnFilterADX){
               if(iADX(NULL, timeframe, ADX_Period, PRICE_CLOSE, 0, 1) < ADX_Level) return; 
               DrawWarn("Enter: ","ADX",clrLimeGreen);
            }
            
            
            if(TurnFilterCCI && env.count>=CCI_StartAfter){
               double cci=iCCI(NULL, timeframe, CCI_Period, PRICE_CLOSE, 0);
               if(env.lasttype==OP_BUY && cci>-CCI_Level) return;
               if(env.lasttype==OP_SELL && cci<CCI_Level) return;
               DrawWarn("Enter: ","CCI",clrLimeGreen);
            }

            if(env.lasttype==OP_BUY){
               OrderSendX(OP_SELL, CalcLot(env.count), 0, 0, EAName+(string)VER+"-"+IntegerToString(env.count+1), MAGIC, clrRed);
            }else{
               OrderSendX(OP_BUY, CalcLot(env.count), 0, 0, EAName+(string)VER+"-"+IntegerToString(env.count+1), MAGIC, clrBlue);
            }
         }      
      }
   }else{
     if(_time != iTime(NULL, timeframe, 0)){
         _time = iTime(NULL, timeframe, 0);
         //Open first order
         if(UseTradeTime_1Order) if( HourOk() == false ) return;  
         
         if(iBars(_Symbol, timeframe) < a__PeriodATR+1){
            //Print("Error: Недостаточно загруженной истории для ATR");
            return;
         }  
         
         if(FilterADX){
            if(iADX(NULL, timeframe, FilterADX_Period, PRICE_CLOSE, 0, 1) < FilterADX_Level) return; 
         }
         
         int bar = macd_atr_shift;
         double upLinePZ = UpLineIndicator(timeframe, bar);
         double dnLinePZ = DnLineIndicator(timeframe, bar);
         if(upLinePZ == 0.0 || dnLinePZ == 0.0){
            if(GetLastError()==ERR_INDICATOR_CANNOT_LOAD){
               Print("Error: indicator ""!  X2 PRICE ZONE 2"" not exist");
               ExpertRemove();
            }
         }
         double ATR_GoldUp = ATR_GoldUp(bar);
         double ATR_GoldDn = ATR_GoldDn(bar);
         if(ATR_GoldUp == 0.0 || ATR_GoldDn == 0.0){
            if(GetLastError()==ERR_INDICATOR_CANNOT_LOAD){
               Print("Error: indicator ""ATR H1-Channels_with_alert"" not exist");
               ExpertRemove();
            }
         }
         
         DrawChannel("upLinePZ", upLinePZ, clrCadetBlue);
         DrawChannel("dnLinePZ", dnLinePZ, clrCadetBlue);
         DrawChannel("ATR_GoldUp", ATR_GoldUp, clrGold);
         DrawChannel("ATR_GoldDn", ATR_GoldDn, clrGold);
         
         if( iOpen(NULL, timeframe, 0) > upLinePZ )
         if( iOpen(NULL, timeframe, 0) < ATR_GoldUp )
         if( MACD(bar) > 0.0005 && MACD(bar+1) <  MACD(bar) )
         if( MACD_Signal(bar) < MACD(bar)){
            OrderSendX(OP_BUY, DinamicLot(), 0, 0, EAName+(string)VER+"-"+IntegerToString(env.count+1), MAGIC, clrBlue);
            return;
         }
         
         if( iOpen(NULL, timeframe, 0) < dnLinePZ )
         if( iOpen(NULL, timeframe, 0) > ATR_GoldDn(bar) )
         if( MACD(bar) < -0.0005 && MACD(bar+1) >  MACD(bar) )
         if( MACD_Signal(bar) > MACD(bar) ){
            OrderSendX(OP_SELL, DinamicLot(), 0, 0, EAName+(string)VER+"-"+IntegerToString(env.count+1), MAGIC, clrRed);
         }
     }
   }  
   
   int Error = GetLastError(); //поиск ошибок в завершение
   if(Error != 0) Print("OnTick() Error ",Error,": ",ErrorDescription(Error));
}

//+------------------------------------------------------------------+

bool HourOk(){
   
   bool work=true;
   
   switch(DayOfWeek()){
      case 1: if(!MONDAY_Enabled) work=false; break;
      case 2: if(!TUESDAY_Enabled)   work=false; break;
      case 3: if(!WEDNESDAY_Enabled) work=false; break;
      case 4: if(!THURSDAY_Enabled)  work=false; break;
      case 5: if(!FRIDAY_Enabled) work=false; break;
      default: work=false;                                              
   }
   if(work){
      if( StartHour == EndHour && Hour() == StartHour) return(true);
      
      if( StartHour < EndHour ){
         if( Hour() == StartHour && Hour() < EndHour ) return(true);
         if( Hour() >  StartHour && Hour() < EndHour ) return(true);
         if( Hour() >  StartHour && Hour() == EndHour ) return(true);
      }
      
      if( StartHour > EndHour ){
         if( Hour() == StartHour ) return(true);
         if( Hour() > StartHour ) return(true);
         if( Hour() < EndHour ) return(true);  
         if( Hour() == EndHour ) return(true);  
      }  
   }
   return(false);
}

//+------------------------------------------------------------------+

double UpLineIndicator(datetime tf, int bar){ return(iCustom(NULL, (int)tf, "!  X2 PRICE ZONE 2", 3, bar)); }
double DnLineIndicator(datetime tf, int bar){ return(iCustom(NULL, (int)tf, "!  X2 PRICE ZONE 2", 4, bar)); }
double SigLineIndicator(datetime tf, int bar){ return(iCustom(NULL, (int)tf, "!  X2 PRICE ZONE 2", 6, bar)); }  

double ATR_GoldUp(int bar){ return(iCustom(NULL, timeframe, "ATR H1-Channels_with_alert", a__PeriodATR, a__MaPeriod,  a__MaTypr, a__Mult_Factor1, a__Mult_Factor2, a__Mult_Factor3, false, false, false, 1, bar)); }
double ATR_GoldDn(int bar){ return(iCustom(NULL, timeframe, "ATR H1-Channels_with_alert", a__PeriodATR, a__MaPeriod,  a__MaTypr, a__Mult_Factor1, a__Mult_Factor2, a__Mult_Factor3, false, false, false, 2, bar)); }
double ATR_AquaUp(int bar){ return(iCustom(NULL, timeframe, "ATR H1-Channels_with_alert", a__PeriodATR, a__MaPeriod,  a__MaTypr, a__Mult_Factor1, a__Mult_Factor2, a__Mult_Factor3, false, false, false, 5, bar)); }
double ATR_AquaDn(int bar){ return(iCustom(NULL, timeframe, "ATR H1-Channels_with_alert", a__PeriodATR, a__MaPeriod,  a__MaTypr, a__Mult_Factor1, a__Mult_Factor2, a__Mult_Factor3, false, false, false, 6, bar)); }

double MACD(int bar){ return iMACD(NULL,timeframe,macd_FastEma, macd_SlowEma, macd_SignalEma,PRICE_CLOSE,0,bar); }
double MACD_Signal(int bar){ return iMACD(NULL,timeframe,macd_FastEma, macd_SlowEma, macd_SignalEma,PRICE_CLOSE,1,bar); }

//+------------------------------------------------------------------+

void CalcSetkaEnv(){
   ZeroMemory(env);
   
   double lotdiff=0;

   for(int order=OrdersTotal()-1; order>=0; order--){
      if(!OrderSelect(order, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderSymbol() != _Symbol) continue;
      if(MAGIC>0 && OrderMagicNumber() != MAGIC) continue;
      
      env.count++;
      env.profit+=OrderProfit()+OrderCommission()+OrderSwap();
      if(OrderLots()<env.firstlot || env.firstlot==0) env.firstlot=OrderLots();
      
      if(OrderType()==OP_BUY)  {
         env.count_buy++;
         env.countlot_buy+=OrderLots();
         lotdiff+=OrderLots();
      }   
      
      if(OrderType()==OP_SELL) {
         env.count_sell++;
         env.countlot_sell+=OrderLots();
         lotdiff-=OrderLots();
      }
      
      if(OrderOpenTime() > env.lasttime){
         env.lasttime = OrderOpenTime();
         env.lastprice = OrderOpenPrice();
         env.lasttype = OrderType(); 
      }
   }
   
   if(env.count>0){
      if(env.count>1){
         double newtickvalue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
         if(newtickvalue>0) tickvalue=newtickvalue;
         if(tickvalue==0) tickvalue=1;
         if(lotdiff>0) env.avg = ND(Bid-(env.profit/lotdiff/tickvalue*_Point));
         else
         if(lotdiff<0) env.avg = ND(Ask-(env.profit/lotdiff/tickvalue*_Point));

         draw_level("be_level", env.avg, clrDarkGoldenrod);
      }else
         env.avg = env.lastprice;
      
      if(env.lasttype==OP_BUY){  
         env.last_distance = (int)NormalizeDouble((env.lastprice - Ask) / _Point,0);
         if(env.avg>0) env.ppoint = (int)NormalizeDouble((Bid - env.avg) / _Point,0);
      }else 
      if(env.lasttype==OP_SELL){
         env.last_distance = (int)NormalizeDouble((Bid - env.lastprice) / _Point,0);
         if(env.avg>0) env.ppoint = (int)NormalizeDouble((env.avg - Ask) / _Point,0);
      }
   }
   
   if(env.count<2) draw_level("be_level", 0, clrNONE);
}

double ND(double val, int dig = EMPTY ){ if(dig == EMPTY) dig = Digits; return( NormalizeDouble(val, dig) ); }
string DS(double d){return DoubleToString(d,2);}

//----------
double DinamicLot(){
   double iLots=Lots;
   if(DepoPer001Lot>0){
      iLots = NormalizeDouble((AccountInfoDouble(ACCOUNT_MARGIN_FREE)/DepoPer001Lot)/100,2);
   }
   CheckVolumeValue(iLots);
   return iLots;
}

//----------
double CalcLot(int Trades){
   double iLots=Lots;
   iLots=NormalizeDouble(env.firstlot * MathPow(env.count<2 ? LotExponent : LotExponent2, Trades), 2); 
   CheckVolumeValue(iLots);
   return iLots;
}

//----------
void CloseAll() {
   _time = iTime(NULL, timeframe, 0);
   ArrayResize(orders,0);
   
   //Get orders by lot
   int tickets[], nTickets = GetOrderByLots(tickets)-1;
   for(int iTicket = 0; iTicket <= nTickets; iTicket++) if (OrderSelect(tickets[iTicket], SELECT_BY_TICKET)){
      int asize=ArraySize(orders);
      ArrayResize(orders, asize+1);
      orders[asize].lots=OrderLots();
      orders[asize].type=OrderType();
      orders[asize].ticket=OrderTicket();
   }
   
   //Calc lots after close by
   int size=ArraySize(orders)-1;
   for(int i=size; i>=0; i--){
      if(orders[i].lots==0) continue;
      for(int j=size; j>=0; j--){
         if(orders[j].lots==0) continue;
         if(orders[i].lots <= orders[j].lots && orders[i].type!=orders[j].type){
            orders[j].lots-=orders[i].lots;
            orders[i].lots=0;
            break;
         }
      }
   }
   
   //Close biggest lots
   for(int i=0; i<size; i++){
      if(orders[i].lots==0) continue;
      OrderCloseX(orders[i].ticket, orders[i].lots, orders[i].type, Slippage, orders[i].type==OP_BUY ? clrBlue : clrRed);
   }
   
   //CloseBy
   bool exit_loop = false;
   int retry = 10;
   int cnt1   = 0;
   while(!exit_loop && cnt1<retry){
      if(CloseByAll()<2) exit_loop = false;
      cnt1++;
   }
   
   //Close All not closed
   ArrayResize(tickets,0); nTickets = GetOrderByLots(tickets)-1;
   for(int iTicket = 0; iTicket <= nTickets; iTicket++) if (OrderSelect(tickets[iTicket], SELECT_BY_TICKET)){
      OrderCloseX(OrderTicket(), OrderLots(), OrderType(), Slippage, OrderType()==OP_BUY ? clrBlue : clrRed);     
   }
   
   //Stat
   stat.count++;
   stat.sumcount+=env.count;
   if(env.count>stat.maxcount) stat.maxcount=env.count;
}
//----------
int CloseByAll(){
   ArrayResize(orders,0);
   
   int tickets[], nTickets = GetOrderByLots(tickets)-1;
   for(int iTicket = 0; iTicket <= nTickets; iTicket++) if (OrderSelect(tickets[iTicket], SELECT_BY_TICKET)){
      int asize=ArraySize(orders);
      ArrayResize(orders, asize+1);
      orders[asize].lots=OrderLots();
      orders[asize].type=OrderType();
      orders[asize].ticket=OrderTicket();
   }
   
   int size=ArraySize(orders);
   for(int i=0; i<size; i++){
      if(orders[i].lots==0) continue;
      for(int j=0; j<size; j++){
         if(orders[j].lots==0) continue;
         if(orders[i].lots >= orders[j].lots && orders[i].type!=orders[j].type){
            if(OrderCloseBy(orders[i].ticket, orders[j].ticket, clrGray)){
               orders[i].lots=0;
               orders[j].lots=0;
               nTickets-=2;
               break;
            }else{
               int err=GetLastError();
               Print("Error: ",ErrorDescription(err));
               if(err==3) return 0;
            }
         }
      }
   }
   
   return nTickets;
}

//----------
int GetOrderByLots(int& tickets[]){  
   int nOrders = 0, iOrders=0; double OPs[];
   for(int iPos=OrdersTotal()-1; iPos >= 0; iPos--) 
   if (OrderSelect(iPos, SELECT_BY_POS, MODE_TRADES) && OrderSymbol()==_Symbol && OrderType()<=OP_SELL && (MAGIC==0 || OrderMagicNumber() == MAGIC)){
      int nextTkt = OrderTicket(); double nextOP = OrderLots();
      nOrders++; ArrayResize(tickets,nOrders); ArrayResize(OPs,nOrders);
      for (iOrders=nOrders - 1; iOrders > 0; iOrders--){  // Insertn sort.
         double prevOP = OPs[iOrders-1];
         if ((prevOP - nextOP) >= 0) break;
         int prevTkt = tickets[iOrders-1];
         tickets[iOrders] = prevTkt; OPs[iOrders] = prevOP;
      }
      tickets[iOrders] = nextTkt; OPs[iOrders] = nextOP; // Insert.
   }
   return(nOrders);
}

//----------
void DrawWarn(string condition, string text, color col){ //писать текст с уведомлениями на графике
   if(!VisualDebug) return;
   if(RealTrade || (IsTesting() && IsVisualMode())){
   
      string obid="Piranha|"+condition+TimeToStr(Time[0]);
      if(ObjectFind(obid) < 0) {
         ENUM_ANCHOR_POINT anc;
         double price;
      
         if((WindowPriceMax()+WindowPriceMin())/2 < Bid) {
            price=MathMin(Low[0],Low[1])-lottextpoint*2*_Point;
            anc=ANCHOR_RIGHT;
         }
         else {
            price=MathMax(High[0],High[1])+lottextpoint*2*_Point;
            anc=ANCHOR_LEFT;
         }
         
         ObjectCreate(obid, OBJ_TEXT, 0, Time[0], price);
         ObjectSet(obid, OBJPROP_ANGLE, 90);
         ObjectSet(obid,OBJPROP_ANCHOR,anc);
         ObjectSet(obid,OBJPROP_BACK,true);
      }
      ObjectSetText(obid,condition+text,7,"Arial",col);
   }
}

//----------
void DrawChannel(string dir, double pr1, color clr=clrYellow){
   if(!VisualDebug) return;
   string name=_Symbol+dir+TimeToString(Time[0]);
   string name_prev=_Symbol+dir+TimeToString(Time[1]);

   double pr2=pr1;
   if(ObjectFind(0,name) < 0){
      if(ObjectFind(0,name_prev) == 0) pr2=ObjectGetDouble(0,name_prev,OBJPROP_PRICE,1);
   
      ObjectCreate(0,name,OBJ_TREND,0,Time[1],pr2,Time[0],pr1);
      ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false); 
      ObjectSetInteger(0,name, OBJPROP_COLOR, clr);
   }
}

//----------
void draw_level(string n, double price, color clr){
   if(!VisualDebug) return;
   if(price<=0){
      if(ObjectFind(0,n) != -1) ObjectDelete(0,n);
   }else{
      if(ObjectFind(0,n) < 0) ObjectCreate(0,n, OBJ_HLINE, 0, 0, price); else ObjectSetDouble(0,n, OBJPROP_PRICE, price);
      ObjectSetInteger(0,n, OBJPROP_STYLE, STYLE_DOT);
      ObjectSetInteger(0,n, OBJPROP_COLOR, clr);
   }
}

//----------
bool OrderSendX(ENUM_ORDER_TYPE pType, double pLots, double sl, double tp, string pComment, int pMagic, color pColor=CLR_NONE){
   int  err = ERR_NO_ERROR;
   bool exit_loop = false;
   double pLevel=0;
   uint start=GetTickCount();
   
   int retry = 10;
   int cnt1   = 0;
   int ticket = -1;

   double free_margin=AccountFreeMarginCheck(_Symbol,pType,pLots);
   if(free_margin<0){
      err = GetLastError();
      Print(Hour(),":",Minute()," | ",EnumToString(pType)," | ",pLots, " | ",ErrorDescription(err));
      return false;
   }

   while(!exit_loop && cnt1<retry){
     if(pType==OP_BUY)  pLevel=Ask; else if(pType==OP_SELL) pLevel=Bid; else return false;
     start=GetTickCount();
     ticket = OrderSend(_Symbol, pType, pLots, pLevel, Slippage, sl, tp, pComment, pMagic, 0, pColor);
     if(ticket<0){
        err = GetLastError();
        switch(err){
          case ERR_INVALID_PRICE:
               RefreshRates();
               break;
          case ERR_INVALID_TRADE_VOLUME:
               if(pLots>maxlot) pLots = maxlot;
               break;
          case ERR_REQUOTE:
               RefreshRates();
               break;
          case ERR_OFF_QUOTES:
               RefreshRates();
               break;
          case ERR_BROKER_BUSY:
               break;
          default:
               exit_loop = true;
               break;
        }
        Sleep(1000);
        
        cnt1++;
     }else{
         err = 0;
         exit_loop = true;
     }
   }
   
   if(ticket>=0){
      uint time1=GetTickCount()-start;
      exit_loop = false;
      retry = 100;
      cnt1 = 0;
      
      while(!exit_loop && cnt1<retry){
         exit_loop = OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
         if(exit_loop){
            double Slp = NormalizeDouble((OrderType()==OP_BUY ? pLevel - OrderClosePrice() : OrderClosePrice() - pLevel)/_Point,1); 
            Print(Hour(),":",Minute()," | ",__FUNCTION__,": ",(OrderType()==OP_BUY ? "BUY" : "SELL")," price / cmd / slp / delay = ",OrderClosePrice()," / ",NormalizeDouble(pLevel,Digits)," / ",DoubleToString(Slp,1)+" point / ",time1," ms");
            //BrokerStatWrite(ticket,"open","ea",Slp,time1,"0");
         }else
            Sleep(200);
         cnt1++; 
      }
      if(!exit_loop) Print(Hour(),":",Minute()," | ",__FUNCTION__," Error: ",ErrorDescription(err));
      
      return true;
   }
   
   if(err != ERR_NO_ERROR && err != ERR_NO_RESULT) Print("Error open order: " + ErrorDescription(err));
   return false;
}

//----------
bool CheckVolumeValue(double &volume){
   double volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
   int ratio=(int)MathRound(volume/volume_step);
   if(MathAbs(ratio*volume_step-volume)>0.0000001){
      Print("Message: Lot step correction ",volume," to ",ratio*volume_step); 
      volume = ratio*volume_step;
   }
   
   if(volume < minlot){ 
      Print("Message: Lot = ",volume," < min = ",minlot,", lot correction to min lot"); 
      volume = minlot;
   }
   if(volume > maxlot){ 
      Print("Message: Lot = ",volume," > max = ",maxlot,", lot correction to max lot"); 
      volume = maxlot;
   }
     
   return true;
}

//----------
void OrderCloseX(int ticket1, double lots, int otype, int slp, color clr=CLR_NONE){
    int err = 0; double price = 0;
    bool exit_loop = false;

    int retry = 10;
    int cnt1   = 0;
    
    lots = NormalizeDouble(lots,2);
    if(lots<0.01) return;
    
    while(!exit_loop && cnt1<retry){
        if(otype == OP_BUY)  price=Bid;
        if(otype == OP_SELL) price=Ask;
        if(!OrderClose(ticket1, lots, price, slp, clr)){
           err = GetLastError();
           switch(err){
             case ERR_INVALID_PRICE:
                  RefreshRates();
                  break;
             case ERR_REQUOTE:
                  RefreshRates();
                  break;
             case ERR_OFF_QUOTES:
                  RefreshRates();
                  break;
             case ERR_BROKER_BUSY:
                  break;
             default:
                  exit_loop = true;
                  break;
           }
           Sleep(1000);
           cnt1++;
        }else{
           err = 0;
           exit_loop = true;
        }
   }
      
   if(err != ERR_NO_ERROR && err != ERR_NO_RESULT) Print ("Ошибка закрытия ордера #" + IntegerToString(ticket1) + ": " + ErrorDescription(err));
}

//----------
void OrderModifyX(int ticket1, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=CLR_NONE){
    int err = 0;
    bool exit_loop = false;

    int retry = 10;
    int cnt1   = 0;

   if(freezlevel>0){
      if((int)(takeprofit-Bid/_Point) <= freezlevel) return; 
      if((int)(Bid-stoploss/_Point) <= freezlevel) return;
   
      if((int)(Ask-takeprofit/_Point) <= freezlevel) return; 
      if((int)(stoploss-Ask/_Point) <= freezlevel) return;
   }
    
    while(!exit_loop && cnt1<retry){
        if(!OrderModify(ticket1, price, stoploss, takeprofit, expiration, arrow_color)){
           err = GetLastError();
           switch(err){
             case ERR_INVALID_STOPS:
                  if(OrderSelect(ticket1, SELECT_BY_TICKET)){
                     RefreshRates();
                     stoplevel=(int)MarketInfo(_Symbol,MODE_STOPLEVEL);
                     if(takeprofit>0 && !CompareDoubles(takeprofit,OrderTakeProfit())) { 
                        if(OrderType()==OP_BUY)  takeprofit=ND(Ask+stoplevel*_Point);
                        if(OrderType()==OP_SELL) takeprofit=ND(Bid-stoplevel*_Point);
                     }
                  }
                  break;
             case ERR_REQUOTE:
                  RefreshRates();
                  break;
             case ERR_OFF_QUOTES:
                  RefreshRates();
                  break;
             case ERR_BROKER_BUSY:
                  break;
             default:
                  exit_loop = true;
                  break;
           }
           Sleep(1000);
           cnt1++;
        }else{
           err = 0;
           exit_loop = true;
        }
   }
      
   if(err != ERR_NO_ERROR && err != ERR_NO_RESULT) Print ("Error modify #" + IntegerToString(ticket1) + ": " + ErrorDescription(err));
}

//----------
double OnTester(){
   Print("Stat series: max: ",stat.maxcount,", avg: ",(stat.sumcount/stat.count));
   
  double Res = 0;
  double MaxDD = TesterStatistics(STAT_EQUITY_DD);
  if (MaxDD != 0)
      Res = TesterStatistics(STAT_PROFIT) / MaxDD;
  return Res;
}
