//+------------------------------------------------------------------+
//|                                               Copyright ReVeR27. |
//|                     https://www.mql5.com/ru/users/rever27/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright ReVeR27."
#property link      "https://www.mql5.com/ru/users/rever27/seller"
//#property version   "1.00"
#property strict

enum  _EventLogs        {
                        ALL_Logs                      = 1,        //All
                        Error_Logs                    = 2,        //Only Errors
					         NONE						         = 3	      //Off
      };
      
extern   string      S_0 = "<==== OPTIMIZATION SETTINGS ====>";   // =  =  =  =  =  =  =  =  =  =  =
sinput   int            VarOptimTest               = 0;
extern   int            VarOptimCounter            = 1;    
sinput   int            MinTrade                   = 100;
sinput   int            MaxSLQueue                 = 10;  
   
extern   string      S_1 = "<==== GENERAL SETTINGS ====>";        // =  =  =  =  =  =  =  =  =  =  =
extern   string         SetName                    = "AbbeyRoad"; //Set Name
extern   string         OrdersComment              = "AbbeyRoad"; //Orders Comment
extern   int            MagicNumber                = 2017;        //Magic Number
extern   double         Lot                        = 0.01;        //Fixed Lot
extern   double         Auto_MM                    = 0;           //Auto MM
extern   double         TakeProfit                 = 210.0;       //Take Profit
extern   double         StopLoss                   = 70.0;        //Stop Loss
extern   int            ATR_Period                 = 24;
extern   double         TakeProfitATRMulti         = 2;           
extern   double         StopLossATRMulti           = 1;           

extern   string      S_11 = "<====  ====>";        // =  =  =  =  =  =  =  =  =  =  =
extern   bool           UseDSS                     = false;
extern   int            DSS_Level                  = 20;
extern   double         MaxGapSize                 = 20;

extern   string      S_2 = "<==== INDICATORS SETTINGS ====>";        // =  =  =  =  =  =  =  =  =  =  =
extern   double         TrailingStartPercent       = 0;           //Trailing Start Percent
extern   double         Step                       = 0.02;        //Parabolic SAR: Step
extern   double         Maximum                    = 0.2;         //Parabolic SAR: Maximum

extern   string      S_6 = "<==== OTHER SETTINGS  ====>";         // =  =  =  =  =  =  =  =  =  =  = 
extern   bool           showinfopanel              = true;        //Show Panel
extern   color          Col_info                   = clrCornflowerBlue; //Panel Color
extern   uchar          Panel_Transparency         = 220;         //Panel Transparency 
extern   bool           VisualDebug                = true;        //Visual Debug
extern   _EventLogs     LogMode                    = 1;           //Logging Mode


//+------------------------------------------------------------------+
double Lots = 0;

int PipsDivided = 1;

double StopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL);

string ShortName = ""; 
bool RealTrade = (!IsTesting() && !IsOptimization() && !IsVisualMode());

datetime lastbarwrite;   
datetime Update_Time;

int SignalType = -1, FilterType = -1;
double Filters1, Filters2, Signal1, Signal2;

//+--- > Indicators <------------------------------------------------+



//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   if (Digits == 3 || Digits == 5) { 
      PipsDivided = 10;    
      StopLoss *= 10;
      TakeProfit *= 10;
      MaxGapSize *= 10;
   }
   
       
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   if(Update_Time != iTime(NULL,PERIOD_CURRENT,0)) { //обновлять данные всех индикаторов раз в период
      Update_Time = iTime(NULL,PERIOD_CURRENT,0);
      
      Filters1 = iCustom(NULL,PERIOD_CURRENT,"Ultra-Filter",0,1);
      Filters2 = iCustom(NULL,PERIOD_CURRENT,"Ultra-Filter",0,2);
      
      if(!UseDSS) FilterType = -1;
      if(Filters1 == 0 && Filters2 == 1) FilterType = OP_BUY;
      else if(Filters1 == 1 && Filters2 == 0) FilterType = OP_SELL;
      
      if(CountOrder(-1) > 0) {
         CheckExit();
         if(TrailingStartPercent > 0) TrailingPSAR(); //Трайлинг стоп 1 раз в свечу 
      }
      else {

         Signal1 = iCustom(NULL,PERIOD_CURRENT,"Ultra-Signal",0,1);
         Signal2 = iCustom(NULL,PERIOD_CURRENT,"Ultra-Signal",0,2);
         
         if(Signal1 > 10000 && Signal2 < 10000) SignalType = OP_BUY;
         else if (Signal1 < 10000 && Signal2 > 10000) SignalType = OP_SELL;
      
         double DSSUp = iCustom(NULL,PERIOD_CURRENT,"ScalpingTrend_Bressert",8,13,1,1);
         double DSSDown = iCustom(NULL,PERIOD_CURRENT,"ScalpingTrend_Bressert",8,13,2,1);
         
         //Print(SignalType + "/"+ DSSUp + "/"+ DSSDown);
         
         if(MathAbs(Open[1] - Close[2] > MaxGapSize*Point)) {
            DrawWarn("GAP Filter", "", clrOrange, true);
            return;
         }
         
         double ATR = iATR(NULL,PERIOD_CURRENT,ATR_Period,1);
         double TP = (TakeProfitATRMulti > 0 ? ATR*TakeProfitATRMulti : MathMax(StopLevel,TakeProfit)*Point);
         double SL = (StopLossATRMulti > 0 ? ATR*StopLossATRMulti : MathMax(StopLevel,StopLoss)*Point);
         
         if(SignalType == OP_BUY && FilterType == OP_BUY && (!UseDSS ||(DSSUp <= DSS_Level && DSSDown == 0))) {
            int ticket1 = OpenOrders(OP_BUY,Ask,clrBlue); //открытие ордера
            if(OrderSelect(ticket1, SELECT_BY_TICKET, MODE_TRADES)) {
               OrdersModify(ticket1, Ask, OrderOpenPrice()-SL, OrderOpenPrice()+TP, 0, clrGreen); 
            }
         }  
         else if(SignalType == OP_SELL && FilterType == OP_SELL && (!UseDSS ||(DSSDown >= 100-DSS_Level && DSSUp == 0))) {
            int ticket2 = OpenOrders(OP_SELL,Bid,clrRed); //открытие ордера
            if(OrderSelect(ticket2, SELECT_BY_TICKET, MODE_TRADES)) {
               OrdersModify(ticket2, Bid, OrderOpenPrice()+SL, OrderOpenPrice()-TP, 0, clrGreen); 
            }
         }
         
      }      
       
   }
   
   
   string PrintLastError = fMyErDesc(); //поиск ошибок в завершение
   if(PrintLastError != "") PrintMassage_and_Errors(__FUNCTION__ + ": " + PrintLastError, Error_Logs,0);
   
  }
//+------------------------------------------------------------------+
void CheckExit() {

   for(int i=OrdersTotal()-1;i>=0;i--){
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)==false) continue;
      if(OrderSymbol()!=_Symbol || OrderMagicNumber() != MagicNumber) continue;
      
      if(OrderType() == OP_BUY) {
         if((Filters1 == 1 && Filters2 == 0)) OrdersClose(OrderTicket(),OrderLots(),Bid,clrBlue); //(Signal1 < 10000 && Signal2 > 10000) || 
      }
      else if(OrderType() == OP_SELL) {
         if((Filters1 == 0 && Filters2 == 1)) OrdersClose(OrderTicket(),OrderLots(),Ask,clrRed); //(Signal1 > 10000 && Signal2 < 10000) || 
      }
   }
   
}

//---------------
bool OrdersClose(int Ticket, double lot, double _Price, color col) {
 
   for(int n = 0; n<=10; n++) {
      if(!OrderClose(Ticket, lot,_Price, 5*PipsDivided, col)) {
         while (IsTradeContextBusy()) Sleep(1000*5);
         PrintMassage_and_Errors(__FUNCTION__ + ": Не удалось закрыть существующий ордер #" + IntegerToString(OrderTicket()) + ". " + fMyErDesc(), Error_Logs,0);
         Sleep(3000); //сон 3 секунды
      }
      else {
         PrintMassage_and_Errors(__FUNCTION__ + ": Ордер #" + IntegerToString(OrderTicket()) + " успешно закрыт.", ALL_Logs,0); 
         Update_Time = 0; 
         DrawWarn("Exit Filter", "", clrOrange, true);    
         return(true);
      }
   }
   
   return(false);
}

//+------------------------------------------------------------------+
int OpenOrders(int _OrderType, double _Price, color col) { //открытие ордеров
  
   Lots = (Auto_MM > 0 ? AutoMM_Count() : Lot); //расчет торгового лота
    
   int Ticket = 0;
   for(int i = 0; i<=8; i++) {
      Ticket = OrderSend(Symbol(),_OrderType,Lots,_Price,5*PipsDivided,0,0, OrdersComment + "|" + IntegerToString(MagicNumber),MagicNumber,0,col);
      if(Ticket < 0) {
         PrintMassage_and_Errors(__FUNCTION__ + ": Не удалось открыть ордер. " + fMyErDesc(), Error_Logs,0);
         Sleep(10000);
      }
      else {
      	string q = __FUNCTION__ + ": ордер был открыт по цене: " + DoubleToStr(_Price,Digits) + "/" + DoubleToStr((_OrderType == OP_BUY ? Ask : Bid),Digits) + ". Спред: " + DoubleToStr(MarketInfo(Symbol(), MODE_SPREAD)/PipsDivided,1);
			PrintMassage_and_Errors(q, ALL_Logs,0);
         Sleep(1000);
         break; //завершение цикла открытия ордеров
      }   
   } 
   
   return(Ticket);
}

//---------------
bool OrdersModify(int Ticket, double _Price, double SL, double TP, datetime Expiration, color col) {

   for(int n = 0; n<=5; n++) {
      if(!OrderModify(Ticket, _Price, SL, TP, Expiration, col)) {
         PrintMassage_and_Errors(__FUNCTION__ + ": Не удалось выставить ТП/СЛ. " + fMyErDesc(), Error_Logs,0);
         Sleep(10000);
      }
      else return(true);
   }

   return(false);
}

//---------------
bool OrdersClose() {
   
   int _OrdersTotal = OrdersTotal()-1;
   for(int pos = _OrdersTotal; pos >= 0; pos--){
      
      if(!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)) {
         if (LogMode < 3) {
				string q = __FUNCTION__ + ": не удалось выделить ордер! " + fMyErDesc();
				PrintMassage_and_Errors(q, ALL_Logs,0);
			}   
      }
      else {
         if(OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
         
         for(int n = 0; n<=10; n++) {
            if(!OrderClose(OrderTicket(), OrderLots(), (OrderType() == OP_BUY ? Bid : Ask), 5*PipsDivided)) {
               while (IsTradeContextBusy()) Sleep(1000*5);
               PrintMassage_and_Errors(__FUNCTION__ + ": Не удалось закрыть существующий ордер #" + IntegerToString(OrderTicket()) + ". " + fMyErDesc(), Error_Logs,0);
               Sleep(3000); //сон 3 секунды
            }
            else {
               PrintMassage_and_Errors(__FUNCTION__ + ": Ордер #" + IntegerToString(OrderTicket()) + " успешно закрыт.", ALL_Logs,0);      
               return(true);
            }
         }
      }
   }      
   return(false);
}

//+------------------------------------------------------------------+
int CountOrder(int Order_Type) {
   int orders=0;
   
   for(int i=OrdersTotal()-1;i>=0;i--){
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)==false) continue;
      if(OrderSymbol()!=_Symbol || OrderMagicNumber() != MagicNumber) continue;
      
      if(Order_Type == OrderType() || Order_Type == -1) orders++;
   }
   return orders;
}


//+------------------------------------------------------------------+
void TrailingPSAR() { // Трал позиций/ордеров по параболику
   
   double PSAR = iSAR(NULL, PERIOD_CURRENT, Step, Maximum, 1);
   
   int Order_total = OrdersTotal();
   for(int i=Order_total-1; i>=0; i--) {
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
         if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber || OrderType() > 1) continue;
         
         double TrailingStart = MathAbs((OrderTakeProfit()-OrderOpenPrice())*TrailingStartPercent/100);
         
         if (OrderType() == OP_BUY) { //если ордер на покупку    
            if(Bid > OrderOpenPrice()+TrailingStart) { 
               if(PSAR+StopLevel*Point < Bid && PSAR > OrderStopLoss()) {
                  bool Order_Trail = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(PSAR,Digits), OrderTakeProfit(), 0);
               }
            }
         }
         else if (OrderType() == OP_SELL) { //если ордер на продажу 
            if (Ask < OrderOpenPrice()-TrailingStart) {
               //Print(TrailingStart);
               if(PSAR-StopLevel*Point > Ask && PSAR < OrderStopLoss()) {
                  bool Order_Trail = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(PSAR,Digits), OrderTakeProfit(), 0);
               }
            }
         }     
      }
   }
}

//+------------------------------------------------------------------+
double AutoMM_Count() { //Расчет лота при указании процента риска в настройках.

   double TickValue = (MarketInfo(Symbol(), MODE_TICKVALUE) == 0 ? 1 : MarketInfo(Symbol(), MODE_TICKVALUE));
   double Balance = MathMin(AccountEquity(),AccountBalance());
   double lot = ((Balance - AccountCredit()) * (Auto_MM / 100)) / StopLoss / TickValue; // + MarketInfo(Symbol(), MODE_SPREAD)
   lot = MathFloor(lot/MarketInfo(Symbol(), MODE_LOTSTEP))* MarketInfo(Symbol(), MODE_LOTSTEP); //округление полученного лота вниз
   lot = MathMin(MathMax(lot, MarketInfo(Symbol(), MODE_MINLOT)), MarketInfo(Symbol(), MODE_MAXLOT)); //сравнение полученнго лота с минимальным/максимальным.
   
   return (lot);
}

//+------------------------------------------------------------------+
void DrawWarn(string condition, string text, color col, bool reverse){ //писать вертикальный текст над/под свечей на графике
   if(!VisualDebug) return;
   if(RealTrade || (IsTesting() && IsVisualMode())){
   
      string obid=ShortName+condition+TimeToStr(Time[0]);
      if(ObjectFind(obid) < 0) {
         ENUM_ANCHOR_POINT anc;
         double price;
      
         if((WindowPriceMax()+WindowPriceMin())/2 < Bid || reverse) {
            price=MathMin(Low[0],Low[1]);
            //price=channel_lower-Channel_Delta*2*_Point;
            anc=ANCHOR_RIGHT;
         }
         else {
            price=MathMax(High[0],High[1]);
            //price=channel_upper+Channel_Delta*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 PrintMassage_and_Errors(string q, int EventLogs, int _lastbar) {
	if (LogMode <= EventLogs && (_lastbar == 0 || lastbarwrite != iTime(NULL,PERIOD_CURRENT,0))) { 
	   Print(q);
	   if(_lastbar != 0) lastbarwrite = iTime(NULL,PERIOD_CURRENT,0);
   }
}	


//+------------------------------------------------------------------+
string fMyErDesc() { //Коды ошибок
   int aErrNum = GetLastError();

   string pref="Ошибка №: "+IntegerToString(aErrNum)+" - ";
   switch(aErrNum){
      case 0:   return("");
      case 1:   return(pref+"Нет ошибки, но результат неизвестен. (OrderModify пытается изменить уже установленные значения такими же значениями. " +
                             "Необходимо изменить одно или несколько значений и повторить попытку.)");
      case 2:   return(pref+"Общая ошибка. Прекратить все попытки торговых операций до выяснения обстоятельств. Возможно перезагрузить операционную систему и клиентский терминал.");
      case 3:   return(pref+"Неправильные параметры. В торговую функцию переданы неправильные параметры, например, неправильный символ, неопознанная торговая " +
                             "операция, отрицательное допустимое отклонение цены, несуществующий номер тикета и т.п. Необходимо изменить логику программы.");
      case 4:   return(pref+"Торговый сервер занят. Можно повторить попытку через достаточно большой промежуток времени (от нескольких минут).");
      case 5:   return(pref+"Старая версия клиентского терминала. Необходимо установить последнюю версию клиентского терминала.");
      case 6:   return(pref+"Нет связи с торговым сервером.  Необходимо убедиться, что связь не нарушена (например, при помощи функции IsConnected) и " +
                             "через небольшой промежуток времени (от 5 секунд) повторить попытку.");
      case 7:   return(pref+"Недостаточно прав.");
      case 8:   return(pref+"Слишком частые запросы. Необходимо уменьшить частоту запросов, изменить логику программы.");
      case 9:   return(pref+"Недопустимая операция, нарушающая функционирование сервера");
      case 64:  return(pref+"Счет заблокирован. Необходимо прекратить все попытки торговых операций.");
      case 65:  return(pref+"Неправильный номер счета. Необходимо прекратить все попытки торговых операций.");
      case 128: return(pref+"Истек срок ожидания совершения сделки. Прежде, чем производить повторную попытку (не менее, чем через 1 минуту), необходимо убедиться, что торговая операция" +
                            "действительно не прошла (новая позиция не была открыта, либо существующий ордер не был изменён или удалён, либо существующая позиция не была закрыта)");
      case 129: return(pref+"Неправильная цена bid или ask, возможно, ненормализованная цена. Необходимо после задержки от 5 секунд обновить " +
                             "данные при помощи функции RefreshRates и повторить попытку. Если ошибка не исчезает, необходимо прекратить все попытки торговых операций и изменить логику программы.");
      case 130: return(pref+"Неправильные стопы. Слишком близкие стопы или неправильно рассчитанные или ненормализованные цены в стопах (или в " +
                             "цене открытия отложенного ордера). Попытку можно повторять только в том случае, если ошибка произошла из-за устаревания " +
                             "цены. Необходимо после задержки от 5 секунд обновить данные при помощи функции RefreshRates и повторить попытку. Если " +
                             "ошибка не исчезает, необходимо прекратить все попытки торговых операций и изменить логику программы.");
      case 131: return(pref+"Неправильный объем, ошибка в грануляции объема. Необходимо прекратить все попытки торговых операций и изменить логику программы.");
      case 132: return(pref+"Рынок закрыт. Можно повторить попытку через достаточно большой промежуток времени (от нескольких минут).");
      case 133: return(pref+"Торговля запрещена. Необходимо прекратить все попытки торговых операций.");
      case 134: return(pref+"Недостаточно средств для совершения операции. Повторять сделку с теми же параметрами нельзя. Попытку можно повторить после " +
                             "задержки от 5 секунд, уменьшив объем, но надо быть уверенным в достаточности средств для совершения операции.");
      case 135: return(pref+"Цена изменилась. Можно без задержки обновить данные при помощи функции RefreshRates и повторить попытку.");
      case 136: return(pref+"Нет цен. Брокер по какой-то причине (например, в начале сессии цен нет, неподтвержденные цены, быстрый рынок) не дал цен или " +
                             "отказал. Необходимо после задержки от 5 секунд обновить данные при помощи функции RefreshRates и повторить попытку.");
      case 137: return(pref+"Брокер занят");
      case 138: return(pref+"Новые цены. Запрошенная цена устарела, либо перепутаны bid и ask. Можно без задержки обновить данные при помощи функции " +
                             "RefreshRates и повторить попытку. Если ошибка не исчезает, необходимо прекратить все попытки торговых операций и изменить логику программы.");
      case 139: return(pref+"Ордер заблокирован и уже обрабатывается. . Необходимо прекратить все попытки торговых операций и изменить логику программы.");
      case 140: return(pref+"Разрешена только покупка. Повторять операцию SELL нельзя.");
      case 141: return(pref+"Слишком много запросов. Необходимо уменьшить частоту запросов, изменить логику программы.");
      case 142: return(pref+"Ордер поставлен в очередь. Это не ошибка, а один из кодов взаимодействия между клиентским терминалом и торговым сервером. Этот код может быть получен в редком случае, " +
                             "когда во время выполнения торговой операции произошёл обрыв и последующее восстановление связи. Необходимо обрабатывать так же как и ошибку 128.");
      case 143: return(pref+"Ордер принят дилером к исполнению. Один из кодов взаимодействия между клиентским терминалом и торговым сервером. Может " +
                             "возникнуть по той же причине, что и код 142. Необходимо обрабатывать так же как и ошибку 128.");
      case 144: return(pref+"Ордер аннулирован самим клиентом при ручном подтверждении сделки. Один из кодов взаимодействия между клиентским терминалом и торговым сервером.");
      case 145: return(pref+"Модификация запрещена, так как ордер слишком близок к рынку и заблокирован из-за возможного скорого исполнения. " +
                             "Можно не ранее, чем через 15 секунд, обновить данные при помощи функции RefreshRates и повторить попытку.");
      case 146: return(pref+"Подсистема торговли занята. Повторить попытку только после того, как функция IsTradeContextBusy вернет FALSE.");
      case 147: return(pref+"Использование даты истечения ордера запрещено брокером. Операцию можно повторить только в том случае, если обнулить параметр expiration.");
      case 148: return(pref+"Количество открытых и отложенных ордеров достигло предела, установленного брокером. Новые открытые позиции и " +
                             "отложенные ордера возможны только после закрытия или удаления существующих позиций или ордеров.");
      case 149: return(pref+"Попытка открыть противоположную позицию к уже существующей в случае, если хеджирование запрещено. Сначала необходимо " +
                             "закрыть существующую противоположную позицию, либо отказаться от всех попыток таких торговых операций, либо изменить логику программы.");
      case 150: return(pref+"Попытка закрыть позицию по инструменту в противоречии с правилом FIFO");
      //---- Коды ошибок выполнения MQL4-программы (советника)
      case 4000: return(pref+"Нет ошибки");
      case 4001: return(pref+"Неправильный указатель функции");
      case 4002: return(pref+"Индекс массива - вне диапазона");
      case 4003: return(pref+"Нет памяти для стека функций");
      case 4004: return(pref+"Переполнение стека после рекурсивного вызова");
      case 4005: return(pref+"На стеке нет памяти для передачи параметров");
      case 4006: return(pref+"Нет памяти для строкового параметра");
      case 4007: return(pref+"Нет памяти для временной строки");
      case 4008: return(pref+"Неинициализированная строка");
      case 4009: return(pref+"Неинициализированная строка в массиве");
      case 4010: return(pref+"Нет памяти для строкового массива");
      case 4011: return(pref+"Слишком длинная строка");
      case 4012: return(pref+"Остаток от деления на ноль");
      case 4013: return(pref+"Деление на ноль");
      case 4014: return(pref+"Неизвестная команда");
      case 4015: return(pref+"Неправильный переход");
      case 4016: return(pref+"Неинициализированный массив");
      case 4017: return(pref+"Вызовы DLL не разрешены");
      case 4018: return(pref+"Невозможно загрузить библиотеку");
      case 4019: return(pref+"Невозможно вызвать функцию");
      case 4020: return(pref+"Вызовы внешних библиотечных функций не разрешены");
      case 4021: return(pref+"Недостаточно памяти для строки, возвращаемой из функции");
      case 4022: return(pref+"Система занята");
      case 4050: return(pref+"Неправильное количество параметров функции");
      case 4051: return(pref+"Недопустимое значение параметра функции");
      case 4052: return(pref+"Внутренняя ошибка строковой функции");
      case 4053: return(pref+"Ошибка массива");
      case 4054: return(pref+"Неправильное использование массива-таймсерии");
      case 4055: return(pref+"Ошибка пользовательского индикатора");
      case 4056: return(pref+"Массивы несовместимы");
      case 4057: return(pref+"Ошибка обработки глобальныех переменных");
      case 4058: return(pref+"Глобальная переменная не обнаружена");
      case 4059: return(pref+"Функция не разрешена в тестовом режиме");
      case 4060: return(pref+"Функция не разрешена");
      case 4061: return(pref+"Ошибка отправки почты");
      case 4062: return(pref+"Ожидается параметр типа string");
      case 4063: return(pref+"Ожидается параметр типа integer");
      case 4064: return(pref+"Ожидается параметр типа double");
      case 4065: return(pref+"В качестве параметра ожидается массив");
      case 4066: return(pref+"Запрошенные исторические данные в состоянии обновления");
      case 4067: return(pref+"Ошибка при выполнении торговой операции");
      case 4099: return(pref+"Конец файла");
      case 4100: return(pref+"Ошибка при работе с файлом");
      case 4101: return(pref+"Неправильное имя файла");
      case 4102: return(pref+"Слишком много открытых файлов");
      case 4103: return(pref+"Невозможно открыть файл");
      case 4104: return(pref+"Несовместимый режим доступа к файлу");
      case 4105: return(pref+"Ни один ордер не выбран");
      case 4106: return(pref+"Неизвестный символ");
      case 4107: return(pref+"Неправильный параметр цены для торговой функции");
      case 4108: return(pref+"Неверный номер тикета");
      case 4109: return(pref+"Торговля не разрешена. Необходимо включить опцию <Разрешить советнику торговать> в свойствах эксперта");
      case 4110: return(pref+"Длинные позиции не разрешены - необходимо проверить свойства эксперта");
      case 4111: return(pref+"Короткие позиции не разрешены - необходимо проверить свойства эксперта");
      case 4200: return(pref+"Объект уже существует");
      case 4201: return(pref+"Запрошено неизвестное свойство объекта");
      case 4202: return(pref+"Объект не существует");
      case 4203: return(pref+"Неизвестный тип объекта");
      case 4204: return(pref+"Нет имени объекта");
      case 4205: return(pref+"Ошибка координат объекта");
      case 4206: return(pref+"Не найдено указанное подокно");
      case 4207: return(pref+"Ошибка при работе с объектом");
      default:   return(pref+"Несуществующий номер ошибки");
   }
}

//+------------------------------------------------------------------+
double OnTester() {//определение коэффициента для оптимизации для оптимизации по Custom
     
   double dNumOfTrades = TesterStatistics(STAT_TRADES);
   double dLongestSLQueue = TesterStatistics(STAT_MAX_CONLOSS_TRADES); //STAT_MAX_CONLOSS_TRADES); //STAT_CONLOSSMAX_TRADES);
   double dRecoveryFactor = (TesterStatistics(STAT_BALANCE_DD) == 0 ? -350 : NormalizeDouble(TesterStatistics(STAT_PROFIT) / TesterStatistics(STAT_BALANCE_DD),2));

   if(dNumOfTrades < 20) return(-350);
   if(dNumOfTrades < MinTrade) return(-300);
   if(dLongestSLQueue > MaxSLQueue) return(NormalizeDouble(-dLongestSLQueue+dRecoveryFactor/10,2));
   return(NormalizeDouble(dRecoveryFactor*100 - dLongestSLQueue,2));

} 

//+------------------------------------------------------------------+
void _ReadParametrs() { //функция для подставления внешних параметров из scv файла для проведения форварда.
   string FileName = "test.csv";
   int handle = FileOpen(FileName,FILE_READ||FILE_CSV);//,''); 
   if (handle < 1) return;
   FileSeek(handle,0,SEEK_SET);
   int str = StrToInteger(FileReadString(handle)); 
   int data = StrToInteger(FileReadString(handle)); 
   if (data < VarOptimCounter) { Alert("Wrong number of test result in test.csv file"); return; } //Введено некорректное количество тестовых проходов

   for(int y=1;y<=VarOptimCounter;y++) {
      for(int x=1;x<=str;x++) {
         string s = FileReadString(handle); 
         string ds = FileReadString(handle); 
         if (y != VarOptimCounter) continue;
         s = StringTrimLeft(s);s = StringTrimRight(s);
         ds = StringTrimLeft(ds);ds = StringTrimRight(ds);
         int i = StrToInteger(ds);
         double d = StrToDouble(ds);

         if (s == "MagicNumber") { MagicNumber = i;  continue; }
         

      }
   }
   FileClose(handle);
  return;
}

//+------------------------------------------------------------------+
void _WriteSet() { //функция для создания сет файлов во время проведения форварда.

   string TFPeriod = "";
   switch(Period()) { //определение текущего периода для записи имени файла
      case 1:     TFPeriod = "M1"; break;
      case 5:     TFPeriod = "M5"; break;
      case 15:    TFPeriod = "M15"; break;
      case 30:    TFPeriod = "M30"; break;
      case 60:    TFPeriod = "H1"; break;
      case 240:   TFPeriod = "H4"; break; 
      case 1440:  TFPeriod = "D1"; break; 
      case 10080: TFPeriod = "W1"; break;
      case 43200: TFPeriod = "MN"; break;
      default: TFPeriod = "Unknown TF";
   }
   
   string FileName = IntegerToString(VarOptimCounter) + " " + WindowExpertName() + " " + Symbol() + " " + TFPeriod  + ".set";
   string _SetName = WindowExpertName() + " " + Symbol() + " " + TFPeriod;
   int handle = FileOpen((FileName),FILE_WRITE|FILE_CSV);
   if (handle < 1) return;

   FileWrite(handle,"MagicNumber="+ (string)MagicNumber);
  

   FileClose(handle);
   return;
}