//+------------------------------------------------------------------+
//|                                                        mt4mm.mq4 |
//|                                         Copyright © 2007, 4xTrim |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, 4xTrim"
#property link      ""

#include <WinUser32.mqh>

#import "mt4mm.dll"
   int mmOpen(int hWnd, string szSymbol, int inI[], double inF[], double cur[], double out[]);
#import

#define SLIPPAGE  100
int    TryCount   = 5;
string mt4comment = "";

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
   int inI[3];
   int i, ticket, ssl, stp;
   double inF[10];
   double cur[4];
   double out[12];
   // out[0]   -  0 - не открываем ордер, 1 - открываем ордер
   // out[1]   -  тип ордера:
   //             0 - BUY
   //             1 - SELL
   //             2 - BUY LIMIT
   //             3 - SELL LIMIT
   //             4 - BUY STOP
   //             5 - SELL STOP
   // out[2]   -  количество открываемых ордеров
   // out[3]   -  размер лота
   // out[4]   -  цена открытия
   // out[5]   -  стоп-лосс в пунктах
   // out[6]   -  0 - не устанавливаем stop-loss, > 0 - устанавливаем stop-loss
   // out[7]   -  тейк-профит в пунктах
   // out[8]   -  0 - не устанавливаем take-profit, > 0 - устанавливаем take-profit
   // out[9]   -  0 - не добавляем значение стопа к коменту, > 0 - добавляем значение стопа
   // out[10]   -  0 - не добавляем значение тейка к коменту, > 0 - добавляем значение тейка
   // out[11]   -  MAGIC
//----
   cur[0]  = MarketInfo("EURUSD", MODE_BID);
   cur[1]  = MarketInfo("GBPUSD", MODE_BID);
   cur[2]  = MarketInfo("USDCHF", MODE_BID);
   cur[3]  = MarketInfo("USDJPY", MODE_BID);
//----
   inI[0]  = Digits;
   inI[1]  = MarketInfo(Symbol(), MODE_PROFITCALCMODE);
   inI[2]  = MarketInfo(Symbol(), MODE_MARGINCALCMODE);
//----
   inF[0]  = MarketInfo(Symbol(), MODE_TICKSIZE);
   inF[1]  = MarketInfo(Symbol(), MODE_TICKVALUE);
   inF[2]  = MarketInfo(Symbol(), MODE_MINLOT);
   inF[3]  = MarketInfo(Symbol(), MODE_LOTSTEP);
   inF[4]  = MarketInfo(Symbol(), MODE_STOPLEVEL);
   inF[5]  = MarketInfo(Symbol(), MODE_MARGINREQUIRED);
   inF[6]  = Bid;
   inF[7]  = MarketInfo(Symbol(), MODE_SPREAD);
   inF[8]  = AccountBalance();
   inF[9]  = AccountEquity();
//----
   mmOpen(WindowHandle(Symbol(),Period()), Symbol(), inI, inF, cur, out);
   if (out[0] == 1)
   {
      for (i = 0; i < out[2]; i++)
      {
         ssl = 0; stp = 0;
         if (out[6] > 0 && out[9] > 0.0) ssl = out[5];
         if (out[8] > 0 && out[10]> 0.0) stp = out[7];
         ticket = SetOrder(out[1], out[4], out[3], ssl, stp , out[11]);
         if (ticket > 0 && (out[6] > 0 || out[8] > 0))
         {
            if (out[6] == 0.0) out[5] = 0.0;
            if (out[8] == 0.0) out[7] = 0.0;
            ModOrder(ticket, out[1], out[5], out[7]);
         }
      }
   }
//----
   return(0);
  }

double GetLimit(int type, double openprice, double limitp, bool GetStop)
{
   double limit = 0.0;
//----
   if (limitp > 0.0)
   {
      if (MathMod(type, 2) == 0.0)
      {
         if (GetStop)
            limit = openprice - limitp * MarketInfo(Symbol(), MODE_TICKSIZE);
         else
            limit = openprice + limitp * MarketInfo(Symbol(), MODE_TICKSIZE);
      }
      else
      {
         if (GetStop)
            limit = openprice + limitp * MarketInfo(Symbol(), MODE_TICKSIZE);
         else
            limit = openprice - limitp * MarketInfo(Symbol(), MODE_TICKSIZE);
      }
   }
   return(limit);
}

//+---------------------------------------------------------------------------+
//| Открытие ордера                                                           |
//| ---------------                                                           |
//|   type        - тип открываемого ордера                                   |
//|   openprice   - цена, по которой необходимо открыть ордер                 |
//|   lot         - размер лота                                               |
//|   slp         - если > 0, то записываем стоп-лосс в комментарий           |
//|   tpp         - если > 0, то записываем тейк-профит в комментарий         |
//|   mn          - MAGIC                                                     |
//| Возвращаемое значение:                                                    |
//|   В случае успешного открытия ордера функция возвращает его тикет.        |
//|   В противном случае функция возвращает -1                                |
//+---------------------------------------------------------------------------+
int SetOrder(int type, double openprice, double lot, double slp, double tpp, double mn)
{
   int ticket = 0;
   int i = 0;
   string com;
//----
   if (lot < MarketInfo(Symbol(), MODE_MINLOT))
     lot = MarketInfo(Symbol(), MODE_MINLOT);
   if (lot > MarketInfo(Symbol(), MODE_MAXLOT))
     lot = MarketInfo(Symbol(), MODE_MAXLOT);
   GetLastError();   // Обнуляем переменную last_error
   while(i < TryCount)
   {
      com = mt4comment;
      if (slp > 0.0 || tpp > 0.0)
      {
         if (StringLen(mt4comment) > 0)
            com = com + " ";
         com = com + "(";
         if (slp > 0.0)
         {
            com = com + "SL: " + DoubleToStr(GetLimit(type, openprice, slp, true), Digits);
            if (tpp > 0.0)
               com = com + " / ";
         }
         if (tpp > 0.0)
            com = com + "TP: " + DoubleToStr(GetLimit(type, openprice, tpp, false), Digits);
         com = com + ")";
      }
      ticket = OrderSend(Symbol(), type, lot, openprice, SLIPPAGE, 0, 0, com, mn);
      if(ticket > 0)
         return(ticket);
      else
      {
         i++;
         // Если ошибка не серьезная
         if(ProcessError(GetLastError()))
         {
            RefreshRates();
            switch (type)
            {
            case OP_BUY:
               openprice = Ask;
               break;
            case OP_SELL:
               openprice = Bid;
               break;
            }
         }
         // получили серьезную ошибку
         else
            return(-1);
      }
   }
   Print("В течение ", TryCount, " попыток ордер по символу \"", Symbol(), "\" открыть не удалось");
   return(-1);
}

//+---------------------------------------------------------------------------+
//| Модификация ордера                                                        |
//| ------------------                                                        |
//|   ticket   - тикет модифицируемого ордера                                 |
//|   type     - тип модифицируемого ордера                                   |
//|   slp      - стоп-лосс в пунктах                                          |
//|   tpp      - тейк-профит в пунктах                                        |
//| Возвращаемое значение:                                                    |
//|   В случае успешной модификации ордера функция возвращает TRUE.           |
//|   В противном случае - FALSE.                                             |
//+---------------------------------------------------------------------------+
bool ModOrder(int ticket, double type, double slp, double tpp)
{
   if (OrderSelect(ticket, SELECT_BY_TICKET) == true)
   {
      double sl = 0.0, tp = 0.0;
      int i = 0;
//----
      if (MathMod(type, 2) == 0.0)
      {
         if (slp > 0.0) sl = OrderOpenPrice() - slp * MarketInfo(Symbol(), MODE_TICKSIZE);
         if (tpp > 0.0) tp = OrderOpenPrice() + tpp * MarketInfo(Symbol(), MODE_TICKSIZE);
      }
      else
      {
         if (slp > 0.0) sl = OrderOpenPrice() + slp * MarketInfo(Symbol(), MODE_TICKSIZE);
         if (tpp > 0.0) tp = OrderOpenPrice() - tpp * MarketInfo(Symbol(), MODE_TICKSIZE);
      }
      GetLastError();
      while (i < TryCount)
      {
         if (OrderModify(OrderTicket(), OrderOpenPrice(), sl, tp, 0))
            return(true);
         else
         {
            if (!ProcessError(GetLastError()))
               return(false);
            i++;
         }
      }
      Print("В течение ", TryCount, " попыток ордер № ", OrderTicket(), " модифицировать не удалось");
   }
   return(false);
}

//+---------------------------------------------------------------------------+
//| Обработка ошибок                                                          |
//| ----------------                                                          |
//|   err - номер возникшей ошибки                                            |
//| Возвращаемое значение:                                                    |
//|   В случае возникновения некритической ошибки, позволяющей продолжить     |
//|   выполнение советника, функция возвращает TRUE.                          |
//|   В противном случае - FALSE.                                             |
//+---------------------------------------------------------------------------+
bool ProcessError(int err)
{
   int iteration;
//----
   PrintError(err);
   switch(err)
   {
   // Некритические ошибки
   case 0:     // ERR_NO_ERROR
   case 135:   // ERR_PRICE_CHANGED
   case 138:   // ERR_REQUOTE
      return(true);
   // Некритические ошибки, требующие таймаута
   case 4:     // ERR_SERVER_BUSY
   case 128:   // ERR_TRADE_TIMEOUT
      Sleep(1000 * 60);
      return(true);   
   case 129:   // ERR_INVALID_PRICE
   case 130:   // ERR_INVALID_STOPS
   case 136:   // ERR_OFF_QUOTES
      Sleep(1000 * 5);
      return(true);   
   case 137:   // ERR_BROKER_BUSY
   case 145:   // ERR_TRADE_MODIFY_DENIED
      Sleep(1000 * 15);
      return(true);   
   // Нет связи с сервером. Пытаемся восстановить связь
   case 6:     // ERR_NO_CONNECTION
   {
      bool connect = false;
      iteration = 0;
      while((!connect) || (iteration < 60)) // в течение 5 минут пытаемся подконнектиться к серверу
      {
         Sleep(1000 * 5);
         connect = IsConnected();
         if(connect)
         {
            Print("Связь восстановлена");
            return(true);
         }
         iteration++;
         Print("Связь не восстановлена, прошло ", iteration * 5, " секунд.");
      }
      Print("Соединение в течение ", iteration * 5, " секунд восстановить не удалось.");
      return(false);
   }
   // Подсистема торговли занята. Ждем, пока освободится
   case 146:   // ERR_TRADE_CONTEXT_BUSY
   {
      bool tradecontextbusy = true;
      iteration = 0;
      while((tradecontextbusy) || (iteration < 60)) // в течение 5 минут ждем освобождения подсистемы торговли
      {
         Sleep(1000 * 5);
         tradecontextbusy = IsTradeContextBusy();
         if(!tradecontextbusy)
         {
            Print("Подсистема торговли освободилась.");
            return(true);
         }
         iteration++;
         Print("Подсистема торговли все еще занята, прошло ", iteration * 5, " секунд.");
      }
      Print("Подсистема торговли в течение ", iteration * 5, " секунд не освободилась.");
      return(false);
   }
   // Критические ошибки. Прекращаем попытки открытия ордера
   case 1:     // ERR_NO_RESULT
   case 2:     // ERR_COMMON_ERROR
   case 3:     // ERR_INVALID_TRADE_PARAMETERS
   case 5:     // ERR_OLD_VERSION
   case 7:     // ERR_NOT_ENOUGH_RIGHTS
   case 8:     // ERR_TOO_FREQUENT_REQUESTS
   case 9:     // ERR_MALFUNCTIONAL_TRADE
   case 64:    // ERR_ACCOUNT_DISABLED
   case 65:    // ERR_INVALID_ACCOUNT
   case 131:   // ERR_INVALID_TRADE_VOLUME
   case 132:   // ERR_MARKET_CLOSED
   case 133:   // ERR_TRADE_DISABLED
   case 134:   // ERR_NOT_ENOUGH_MONEY
   case 139:   // ERR_ORDER_LOCKED
   case 140:   // ERR_LONG_POSITIONS_ONLY_ALLOWED
   case 141:   // ERR_TOO_MANY_REQUESTS
   case 142:
   case 143:
   case 144:
   case 147:   // ERR_TRADE_EXPIRATION_DENIED
   case 148:   // ERR_TRADE_TOO_MANY_ORDERS
   case 4109:  // ERR_TRADE_NOT_ALLOWED
   case 4110:  // ERR_LONGS_NOT_ALLOWED
   case 4111:  // ERR_SHORTS_NOT_ALLOWED
      return(false);
   }
   return(false);
}
//+---------------------------------------------------------------------------+
//| Печать номера и описания ошибки в лог                                     |
//| --------------------------------------                                    |
//|   err - номер возникшей ошибки                                            |
//+---------------------------------------------------------------------------+
int PrintError(int err)
{
   Print("ERROR ", err, ":  ", err2str(err));
   return(0);
}
//+---------------------------------------------------------------------------+
//| Получение описания ошибки по ее номеру                                    |
//| --------------------------------------                                    |
//|   err - номер возникшей ошибки                                            |
//| Возвращаемое значение:                                                    |
//|   Функция возвращает текстовое описание ошибки                            |
//+---------------------------------------------------------------------------+
string err2str(int err)
{
   switch(err)
   {
   case 0:
      return("Нет ошибки");
   case 1:
      return("Функция модификации ордера пытается изменить уже установленные значения такими же значениями");
   case 2:
      return("Общая ошибка");
   case 3:
      return("В торговую функцию переданы неправильные параметры");
   case 4:
      return("Торговый сервер занят");
   case 5:
      return("Старая версия клиентского терминала");
   case 6:
      return("Нет связи с торговым сервером");
   case 7:
      return("Недостаточно прав");
   case 8:
      return("Слишком частые запросы");
   case 9:
      return("Недопустимая операция нарушающая функционирование сервера");
   case 64:
      return("Счет заблокирован");
   case 65:
      return("Неправильный номер счета");
   case 128:
      return("Истек срок ожидания совершения сделки");
   case 129:
      return("Неправильная цена");
   case 130:
      return("Неправильные стопы");
   case 131:
      return("Неправильный объем сделки");
   case 132:
      return("Рынок закрыт");
   case 133:
      return("Торговля запрещена. Возможно, Вы работаете на счете под паролем инвестора");
   case 134:
      return("Недостаточно денег для совершения операции");
   case 135:
      return("Цена изменилась");
   case 136:
      return("Нет цен");
   case 137:
      return("Брокер занят");
   case 138:
      return("Реквот! Новые цены");
   case 139:
      return("Ордер заблокирован и уже обрабатывается");
   case 140:
      return("Разрешена только покупка");
   case 141:
      return("Слишком много запросов");
   case 142:
      return("Ордер поставлен в очередь");
   case 143:
      return("Ордер принят дилером к исполнению");
   case 144:
      return("Ордер аннулирован самим клиентом при ручном подтверждении сделки");
   case 145:
      return("Модификация запрещена, так как ордер слишком близок к рынку");
   case 146:
      return("Подсистема торговли занята");
   case 147:
      return("Использование даты истечения ордера запрещено брокером");
   case 148:
      return("Количество открытых и отложенных ордеров достигло предела, установленного брокером");
   case 4109:
      return("Торговля не разрешена. Возможно не включена опция \"Разрешить советнику торговать\" в свойствах эксперта");
   case 4110:
      return("Длинные позиции не разрешены");
   case 4111:
      return("Короткие позиции не разрешены");
   }
   return("Описание ошибки не известно");
}
//+---------------------------------------------------------------------------+

