#property copyright "Scritong"
#property link      "scriptong@mail.ru"

//---- input parameters
extern double    Lots=0.1;
extern string    A1 = "Параметры быстрого стохастика";
extern int       FastK = 5;
extern int       FastD = 3;
extern int       FastSlowing = 3;
extern string    A2 = "===============================================";
extern string    A3 = "Параметры медленного стохастика";
extern int       SlowK = 20;
extern int       SlowD = 10;
extern int       SlowSlowing = 20;
extern string    A4 = "===============================================";
extern string    A5 = "Множитель для уровня профита";
extern double    Factor = 5;
extern string    A6 = "===============================================";
extern string    OpenOrderSound = "ok.wav";
extern int       MagicNumber = 10015;

int Signal;

bool Activate, FreeMarginAlert, FatalError;
double Tick, Spread, StopLevel, MinLot, MaxLot, LotStep, FreezeLevel, StopLoss, LastLot;
datetime LastBar;

//+-------------------------------------------------------------------------------------+
//| Функция инициализации эксперта                                                      |
//+-------------------------------------------------------------------------------------+
int init()
  {
   FatalError = False;
// - 1 - == Сбор информации об условиях торговли ========================================   
   Tick = MarketInfo(Symbol(), MODE_TICKSIZE);                         // минимальный тик    
   Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point);                 // текущий спрэд
   StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point);  // текущий уровень стопов
   FreezeLevel = ND(MarketInfo(Symbol(), MODE_FREEZELEVEL)*Point);   // уровень заморозки
   MinLot = MarketInfo(Symbol(), MODE_MINLOT);    // минимальный разрешенный объем сделки
   MaxLot = MarketInfo(Symbol(), MODE_MAXLOT);   // максимальный разрешенный объем сделки
   LotStep = MarketInfo(Symbol(), MODE_LOTSTEP);          // шаг приращения объема сделки
// - 1 - == Окончание блока =============================================================

// - 2 - == Приведение объема сделки к допустимому и проверка корректности объема =======   
   Lots = LotRound(Lots);                  // округление объема до ближайшего допустимого
// - 2 - == Окончание блока =============================================================

// - 3 - ================= Проверка корректности входных параметров =====================
   if (FastK < 1)
     {
      Comment("Период FastK должен быть больше нуля! Советник отключен!");
      Print("Период FastK должен быть больше нуля! Советник отключен!");
      return(0);
     }
   if (FastD < 1)
     {
      Comment("Период FastD должен быть больше нуля! Советник отключен!");
      Print("Период FastD должен быть больше нуля! Советник отключен!");
      return(0);
     }
   if (FastSlowing < 1)
     {
      Comment("Период FastSlowing должен быть больше нуля! Советник отключен!");
      Print("Период FastSlowing должен быть больше нуля! Советник отключен!");
      return(0);
     }
   if (SlowK < 1)
     {
      Comment("Период SlowK должен быть больше нуля! Советник отключен!");
      Print("Период SlowK должен быть больше нуля! Советник отключен!");
      return(0);
     }
   if (SlowD < 1)
     {
      Comment("Период SlowD должен быть больше нуля! Советник отключен!");
      Print("Период SlowD должен быть больше нуля! Советник отключен!");
      return(0);
     }
   if (SlowSlowing < 1)
     {
      Comment("Период SlowSlowing должен быть больше нуля! Советник отключен!");
      Print("Период SlowSlowing должен быть больше нуля! Советник отключен!");
      return(0);
     }
// - 3 - =========================== Окончание блока ====================================
   
   LastBar = 0;
   Activate = True; // Все проверки успешно завершены, возводим флаг активизации эксперта
   
//----
   return(0);
  }
  
//+-------------------------------------------------------------------------------------+
//| Функция деинициализации эксперта                                                      |
//+-------------------------------------------------------------------------------------+
int deinit()
{
 Comment("");
 return(0);
}
  

//+-------------------------------------------------------------------------------------+
//| Проверка объема на корректность и округление                                        |
//+-------------------------------------------------------------------------------------+
double LotRound(double L)
{
 return(MathRound(MathMin(MathMax(L, MinLot), MaxLot)/LotStep)*LotStep);
}

//+-------------------------------------------------------------------------------------+
//| Приведение значений к точности одного пункта                                        |
//+-------------------------------------------------------------------------------------+
double ND(double A)
{
 return(NormalizeDouble(A, Digits));
}  

//+-------------------------------------------------------------------------------------+
//| Расшифровка сообщения об ошибке                                                     |
//+-------------------------------------------------------------------------------------+
string ErrorToString(int Error)
{
 switch(Error)
   {
    case 2: return("зафиксирована общая ошибка, обратитесь в техподдержку."); 
    case 5: return("у вас старая версия терминала, обновите ее."); 
    case 6: return("нет связи с сервером, попробуйте перезагрузить терминал."); 
    case 64: return("счет заблокирован, обратитесь в техподдержку.");
    case 132: return("рынок закрыт."); 
    case 133: return("торговля запрещена."); 
    case 149: return("запрещено локирование."); 
   }
}

//+-------------------------------------------------------------------------------------+
//| Ожидание торгового потока. Если поток свободен, то результат True, иначе - False    |
//+-------------------------------------------------------------------------------------+  
bool WaitForTradeContext()
{
 int P = 0;
 // цикл "пока"
 while(IsTradeContextBusy() && P < 5)
   {
    P++;
    Sleep(1000);
   }
 // -------------  
 if(P == 5)
   return(False);
 return(True);    
}

//+-------------------------------------------------------------------------------------+
//| "Правильное" открытие позиции                                                       |
//| В отличие от OpenOrder проверяет соотношение текущих уровней и устанавливаемых      |
//| Возвращает:                                                                         |
//|   0 - нет ошибок                                                                    |
//|   1 - Ошибка открытия                                                               |
//|   2 - Ошибка значения Price                                                         |
//|   3 - Ошибка значения SL                                                            |
//|   4 - Ошибка значения TP                                                            |
//|   5 - Ошибка значения Lot                                                           |
//+-------------------------------------------------------------------------------------+
int OpenOrderCorrect(int Type, double Lot, double Price, double SL, double TP, 
                     bool Redefinition = True)
// Redefinition - при True доопределять параметры до минимально допустимых
//                при False - возвращать ошибку
{
// - 1 - == Проверка достаточности свободных средств ====================================
 if(AccountFreeMarginCheck(Symbol(), OP_BUY, Lot) <= 0 || GetLastError() == 134) 
  {
   if(!FreeMarginAlert)
    {
     Print("Недостаточно средств для открытия позиции. Free Margin = ", 
           AccountFreeMargin());
     FreeMarginAlert = True;
    } 
   return(5);  
  }
 FreeMarginAlert = False;  
// - 1 - == Окончание блока =============================================================

// - 2 - == Корректировка значений Price, SL и TP или возврат ошибки ====================   
 RefreshRates();
 switch (Type)
   {
    case OP_BUY: 
                string S = "BUY"; 
                if (MathAbs(Price-Ask)/Point > 3)
                  if (Redefinition) Price = ND(Ask);
                  else              return(2);
                if (ND(TP-Bid) <= StopLevel && TP != 0)
                  if (Redefinition) TP = ND(Bid+StopLevel+Tick);
                  else              return(4);
                if (ND(Bid-SL) <= StopLevel)
                  if (Redefinition) SL = ND(Bid-StopLevel-Tick);
                  else              return(3);
                break;
    case OP_SELL: 
                 S = "SELL"; 
                 if (MathAbs(Price-Bid)/Point > 3)
                   if (Redefinition) Price = ND(Bid);
                   else              return(2);
                 if (ND(Ask-TP) <= StopLevel) 
                   if (Redefinition) TP = ND(Ask-StopLevel-Tick);
                   else              return(4);
                 if (ND(SL-Ask) <= StopLevel && SL != 0)
                   if (Redefinition) SL = ND(Ask+StopLevel+Tick);
                   else              return(3);
                 break;
    case OP_BUYSTOP: 
                    S = "BUYSTOP";
                    if (ND(Price-Ask) <= StopLevel)
                      if (Redefinition) Price = ND(Ask+StopLevel+Tick);
                      else              return(2);
                    if (ND(TP-Price) <= StopLevel && TP != 0)
                      if (Redefinition) TP = ND(Price+StopLevel+Tick);
                      else              return(4);
                    if (ND(Price-SL) <= StopLevel)
                      if (Redefinition) SL = ND(Price-StopLevel-Tick);
                      else              return(3);
                    break;
    case OP_SELLSTOP: 
                     S = "SELLSTOP";
                     if (ND(Bid-Price) <= StopLevel)
                       if (Redefinition) Price = ND(Bid-StopLevel-Tick);
                       else              return(2);
                     if (ND(Price-TP) <= StopLevel)
                       if (Redefinition) TP = ND(Price-StopLevel-Tick);
                       else              return(4);
                     if (ND(SL-Price) <= StopLevel && SL != 0)
                       if (Redefinition) SL = ND(Price+StopLevel+Tick);
                       else              return(3);
                     break;
    case OP_BUYLIMIT: 
                     S = "BUYLIMIT";
                     if (ND(Ask-Price) <= StopLevel)
                      if (Redefinition) Price = ND(Ask-StopLevel-Tick);
                      else              return(2);
                     if (ND(TP-Price) <= StopLevel && TP != 0)
                       if (Redefinition) TP = ND(Price+StopLevel+Tick);
                       else              return(4);
                     if (ND(Price-SL) <= StopLevel)
                       if (Redefinition) SL = ND(Price-StopLevel-Tick);
                       else              return(3);
                     break;
    case OP_SELLLIMIT: 
                     S = "SELLLIMIT";
                     if (ND(Price - Bid) <= StopLevel) 
                       if (Redefinition) Price = ND(Bid+StopLevel+Tick);
                       else              return(2);
                     if (ND(Price-TP) <= StopLevel)
                       if (Redefinition) TP = ND(Price-StopLevel-Tick);
                       else              return(4);
                     if (ND(SL-Price) <= StopLevel && SL != 0)
                       if (Redefinition) SL = ND(Price+StopLevel+Tick);
                       else              return(3);
                     break;
   }
// - 2 - == Окончание блока =============================================================
 
// - 3 - == Открытие ордера с ожидание торгового потока =================================   
 if(WaitForTradeContext())  // ожидание освобождения торгового потока
   {  
    Comment("Отправлен запрос на открытие ордера ", S, " ...");  
    int ticket=OrderSend(Symbol(), Type, Lot, Price, 3, 
               SL, TP, NULL, MagicNumber, 0);// открытие позиции
    // Попытка открытия позиции завершилась неудачей
    if(ticket<0)
      {
       int Error = GetLastError();
       if(Error == 2 || Error == 5 || Error == 6 || Error == 64 
          || Error == 132 || Error == 133 || Error == 149)     // список фатальных ошибок
         {
          Comment("Фатальная ошибка при открытии позиции т. к. "+
                   ErrorToString(Error)+" Советник отключен!");
          FatalError = True;
         }
        else 
         Comment("Ошибка открытия позиции ", S, ": ", Error);       // нефатальная ошибка
       return(1);
      }
    // ---------------------------------------------
    
    // Удачное открытие позиции   
    Comment("Позиция ", S, " открыта успешно!"); 
    PlaySound(OpenOrderSound); 
    return(0); 
    // ------------------------
   }
  else
   {
    Comment("Время ожидания освобождения торгового потока истекло!");
    return(1);  
   } 
// - 3 - == Окончание блока =============================================================
   
}

//+-------------------------------------------------------------------------------------+
//| Приведение значений к точности одного тика                                          |
//+-------------------------------------------------------------------------------------+
double NP(double A)
{
 return(MathRound(A/Tick)*Tick);
}  

//+-------------------------------------------------------------------------------------+
//| Генерация сигналов покупки и продажи по значениям двух стохастиков                  |
//+-------------------------------------------------------------------------------------+
void GetSignal()
{
 Signal = 0;
 
// - 1 - ====== Определение значений главной и сигнальной линий обоих стохастиков =======
 double FastM1 = iStochastic(NULL,0,FastK,FastD,FastSlowing,MODE_SMA,0,MODE_MAIN,1);
 double FastS1 = iStochastic(NULL,0,FastK,FastD,FastSlowing,MODE_SMA,0,MODE_SIGNAL,1);
 double SlowM1 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_MAIN,1);
 double SlowS1 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_SIGNAL,1);
 double SlowM2 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_MAIN,2);
 double SlowS2 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_SIGNAL,2);
// - 1 - ========================== Окончание блока =====================================

// - 2 - ======================== Генерация сигнала покупки =============================
 if (FastM1 > FastS1 && SlowM1 > SlowS1 && SlowM2 < SlowS2)
   {
    // - 2.1 - ===== Поиск предпоследнего пересечения главной и сигнальной линий ========
    // ============================ медленного стохастика ===============================
    int i = 2;
    while (SlowM2 < SlowS2 && i < Bars) 
      {
       SlowM2 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_MAIN,i);
       SlowS2 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_SIGNAL,i);
       i++;
      }
    // - 2.1 - ========================= Окончание блока ================================

    // - 2.2 - ============= Определение уровня стопа и генерация сигнала ===============
    StopLoss = Low[iLowest(NULL, 0, MODE_LOW, i - 2, 2)] - Tick;   
    Signal = 1;                                                         // Сигнал покупки
    // - 2.2 - ========================= Окончание блока ================================
   } 
// - 2 - ========================== Окончание блока =====================================

// - 3 - ======================== Генерация сигнала продажи =============================
 if (FastM1 < FastS1 && SlowM1 < SlowS1 && SlowM2 > SlowS2)
   {
    // - 3.1 - ===== Поиск предпоследнего пересечения главной и сигнальной линий ========
    // ============================ медленного стохастика ===============================
    i = 2;
    while (SlowM2 > SlowS2 && i < Bars) 
      {
       SlowM2 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_MAIN,i);
       SlowS2 = iStochastic(NULL,0,SlowK,SlowD,SlowSlowing,MODE_SMA,0,MODE_SIGNAL,i);
       i++;
      }
    // - 3.1 - ========================= Окончание блока ================================
    StopLoss = High[iHighest(NULL, 0, MODE_HIGH, i - 2, 2)] + Spread + Tick;   
    Signal = -1;                                                        // Сигнал продажи
   } 
// - 3 - ========================== Окончание блока =====================================
}

//+-------------------------------------------------------------------------------------+
//| Поиск своих ордеров. Возвращает:                                                    |
//|    0 - Своих позиций нет. Можно открывать новую позицию                             |
//|    1 - Не удалось закрыть указанную позицию. Требуется еще попытка                  |
//|    2 - Существует противоположная указанной поиция                                  |                
//+-------------------------------------------------------------------------------------+
int CheckOrders(int Type)   // Type - Тип позиции, которую нужно закрыть или -1, если тип
{                                                            // позиции не имеет значения
 for (int i = OrdersTotal()-1; i >= 0; i--)
   if (OrderSelect(i, SELECT_BY_POS))
     if (OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
       if (OrderType() == Type || Type < 0)
         {
          if (WaitForTradeContext())
            {
             if (Type == OP_BUY)
               double Price = MarketInfo(Symbol(), MODE_BID);
              else
               Price = MarketInfo(Symbol(), MODE_ASK);
             if (!OrderClose(OrderTicket(), OrderLots(), NP(Price), 3))
               return(1);  
            }
         }
        else    
         return(2);
 
 return(0);    
}    

//+-------------------------------------------------------------------------------------+
//| Открытие позиций                                                                    |
//+-------------------------------------------------------------------------------------+
bool Trade()
{
// - 1 - ==================== Открытие длинной позиции ==================================
 if (Signal > 0)
   {
    int Res = CheckOrders(OP_SELL);
    if (Res == 0)                          // Открытых позиций нет, можно открывать новую
      {
       double TP = Ask + iATR(NULL, 0, 24, 1)*Factor;//Уровень профита - 5 волатильностей
       if (OpenOrderCorrect(OP_BUY, Lots, NP(Ask), NP(StopLoss), NP(TP)) != 0)
         return(False);                    
      }   
    if (Res == 1) return(False);//Существует короткая позиция, которую закрыть не удалось
   }       
// - 1 - ==================== Окончание блока ===========================================
           
// - 2 - ==================== Открытие короткой позиции =================================
 if (Signal < 0)
   {
    Res = CheckOrders(OP_BUY);
    if (Res == 0)                          // Открытых позиций нет, можно открывать новую
      { 
       TP = Bid - iATR(NULL, 0, 24, 1)*Factor;// Уровень профита - 5 сред. волатильностей
       if (OpenOrderCorrect(OP_SELL, Lots, NP(Bid), NP(StopLoss), NP(TP)) != 0)
         return(False);                   
      }   
    if (Res == 1) return(False);// Существует длинная позиция, которую закрыть не удалось
   }       
// - 2 - ==================== Окончание блока ===========================================
 
 return(True);    
}

//+-------------------------------------------------------------------------------------+
//| Функция start эксперта                                                              |
//+-------------------------------------------------------------------------------------+
int start()
{
// - 1 - ========================== Можно ли работать эксперту? =========================
   if (!Activate || FatalError) return(0);
// - 1 - ============================= Окончание блока ==================================

// - 2 - ========================== Контроль открытия нового бара =======================
   if (LastBar == Time[0]) return(0);
// - 2 - ============================= Окончание блока ==================================
        
// - 3 - ========================== Расчет сигналов открытия и закрытия =================
   GetSignal();
// - 3 - ============================= Окончание блока ==================================
   
// - 4 - ================== Выполнение операций открытия и закрытия позиций =============
   if (!Trade()) return(0);
// - 4 - ============================= Окончание блока ==================================

   LastBar = Time[0];

   return(0);
}

