//+----------------------------------------------------------------------------+
//|                                                       i-SignalOfTrade.mq4  |
//|                                                    Ким Игорь В. aka KimIV  |
//|                                                       http://www.kimiv.ru  |
//|                                                                            |
//|  05.12.2007  Сигнализатор выполнения торговых операций.                    |
//|  13.05.2008  Сигнализация срабатывания отложенного ордера.                 |
//|  29.12.2008  Исправлена ошибка сигнализации удаления ордера.               |
//+----------------------------------------------------------------------------+
#property copyright "Ким Игорь В. aka KimIV"
#property link      "http://www.kimiv.ru"
#property indicator_chart_window

//------- Внешние параметры индикатора ----------------------------------------+
extern string _P_WhatOutput    = "---------- Что выводить";
extern bool   OutAccountNumber = True;      // Выводить номер счёта
extern bool   OutAccountServer = True;      // Выводить имя активного сервера
extern bool   OutNameExpert    = True;      // Выводить имя эксперта
extern bool   OutDateTime      = True;      // Выводить дату и время
extern bool   UseTimeLocal     = True;      // Использовать локальное время
extern bool   OutTicket        = False;     // Выводить тикет
extern bool   OutMagic         = True;      // Выводить магик
extern bool   OutComment       = True;      // Выводить комментарий
extern string _P_WhereOutput = "---------- Куда выводить";
extern bool   UseAlert    = True;           // Использовать диалоговое окно
extern bool   UseComment  = True;           // Выводить комментарий
extern bool   UsePrint    = False;          // Записывать в журнал
extern bool   UseSendMail = False;          // Использовать передачу сообщений
extern bool   UseSound    = True;           // Использовать звук
extern string sotFileName = "expert.wav";   // Наименование звукового файла

//------- Глобальные переменные индикатора ------------------------------------+
int    sotOTI[];             // тикет   OrderTicket()
int    sotOTY[];             // тип     OrderType()
double sotOOP[];             // цена    OrderOpenPrice()
double sotOSL[];             // стоп    OrderStopLoss()
double sotOTP[];             // тейк    OrderTakeProfit()
bool   sotFirstRun=True;     // Флаг первого прогона


//+----------------------------------------------------------------------------+
//|                                                                            |
//|  ПРЕДОПРЕДЕЛЁННЫЕ ФУНКЦИИ                                                  |
//|                                                                            |
//+----------------------------------------------------------------------------+
//|  Custom indicator initialization function                                  |
//+----------------------------------------------------------------------------+
void init() {
  sotFirstRun=True;     // Флаг первого прогона
  Comment("");
}

//+----------------------------------------------------------------------------+
//|  Custom indicator deinitialization function                                |
//+----------------------------------------------------------------------------+
void deinit() { Comment(""); }

//+----------------------------------------------------------------------------+
//|  Custom indicator iteration function                                       |
//+----------------------------------------------------------------------------+
void start() {
  double p;                  // Размер пункта в валюте котировки
  int    d;                  // Количество цифр после десятичной точки в цене
  int    i;                  // Счётчик операций
  int    in;                 // Индекс элемента массива операций
  int    k=OrdersTotal();    // Количество текущих операций
  int    ot;                 // Тип операции OrderType()
  int    oti[];              // Массив новых тикетов
  string sh="";              // Строка заголовка почтового сообщения
  string st;                 // Строка тела почтового сообщения

  if (sotFirstRun) { MemCurrOP(); sotFirstRun=False; }
  if (OutAccountNumber) sh=DoubleToStr(AccountNumber(), 0)+" ";
  if (OutAccountServer) sh=sh+AccountServer();
  if (StringLen(sh)>0) sh=sh+"\n";
  if (OutDateTime) {
    if (UseTimeLocal) sh=sh+TimeToStr(TimeLocal(), TIME_DATE|TIME_MINUTES)+" ";
    else sh=sh+TimeToStr(TimeCurrent(), TIME_DATE|TIME_SECONDS)+" ";
  }
  if (OutNameExpert) sh=sh+WindowExpertName()+" ";
  if (StringLen(sh)>0) sh=sh+"\n";
  
  // Открытие/Установка новых, модификация существующих
  ArrayResize(oti, k);
  for (i=0; i<k; i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
      oti[i]=OrderTicket();
      d=MarketInfo(OrderSymbol(), MODE_DIGITS);
      p=MarketInfo(OrderSymbol(), MODE_POINT);
      in=ArraySearchInt(sotOTI, oti[i]);

      // операция НЕ найдена, значит новая
      if (in<0) {
        ot=OrderType();
        if (ot==OP_BUY || ot==OP_SELL) st="Open ";
        if (ot==OP_BUYLIMIT || ot==OP_BUYSTOP
        ||  ot==OP_SELLLIMIT || ot==OP_SELLSTOP) st="Set ";
        if (OutTicket) st=st+DoubleToStr(OrderTicket(), 0)+" ";
        st=st+GetNameOP(ot)+" "
             +DoubleToStr(OrderLots(), 2)+" "
             +OrderSymbol()
             +" PR="+DoubleToStr(OrderOpenPrice(), d)
             +" SL="+DoubleToStr(OrderStopLoss(), d)
             +" TP="+DoubleToStr(OrderTakeProfit(), d)
             +"\n"
             +IIFs(OutMagic, "Magic="+DoubleToStr(OrderMagicNumber(), 0), "")
             +IIFs(OutMagic && OutComment, " ", "")
             +IIFs(OutComment, "Comment=\""+OrderComment()+"\"", "")
             +IIFs(OutMagic || OutComment, "\n", "");
        if (ot==OP_BUY || ot==OP_SELL) {
          st=st+"Balance="+DoubleToStr(AccountBalance(), 2)
               +" FreeMargin="+DoubleToStr(AccountFreeMargin(), 2);
        }
        if (StringLen(sh)>0) st=sh+st;
        Signal(st);

      // операция найдена, значит существовала ранее и возможно изменена
      } else {
        // срабатывание отложенного ордера
        ot=OrderType();
        if (ot!=sotOTY[in] && (ot==OP_BUY || ot==OP_SELL)) {
          st="Open ";
          if (OutTicket) st=st+DoubleToStr(OrderTicket(), 0)+" ";
          st=st+GetNameOP(ot)+" by "+GetNameOP(sotOTY[in])+" "
               +DoubleToStr(OrderLots(), 2)+" "
               +OrderSymbol()
               +" PR="+DoubleToStr(OrderOpenPrice(), d)
               +" SL="+DoubleToStr(OrderStopLoss(), d)
               +" TP="+DoubleToStr(OrderTakeProfit(), d)
               +"\n"
               +IIFs(OutMagic, "Magic="+DoubleToStr(OrderMagicNumber(), 0), "")
               +IIFs(OutMagic && OutComment, " ", "")
               +IIFs(OutComment, "Comment=\""+OrderComment()+"\"", "")
               +IIFs(OutMagic || OutComment, "\n", "");
          if (ot==OP_BUY || ot==OP_SELL) {
            st=st+"Balance="+DoubleToStr(AccountBalance(), 2)
                 +" FreeMargin="+DoubleToStr(AccountFreeMargin(), 2);
          }
          if (StringLen(sh)>0) st=sh+st;
          Signal(st);
        }
        // модификация ордера или позиции
        if ((MathAbs(OrderOpenPrice()-sotOOP[in])>=p)
        ||  (MathAbs(OrderStopLoss()-sotOSL[in])>=p)
        ||  (MathAbs(OrderTakeProfit()-sotOTP[in])>=p)) {
          st="Modify ";
          if (OutTicket) st=st+DoubleToStr(OrderTicket(), 0)+" ";
          st=st+GetNameOP(OrderType())+" "
            +DoubleToStr(OrderLots(), 2)+" "
            +OrderSymbol();
          // изменилась цена открытия
          if (MathAbs(OrderOpenPrice()-sotOOP[in])>=p) {
            st=st+" PR="+DoubleToStr(sotOOP[in], d)+"->"+DoubleToStr(OrderOpenPrice(), d);
          }
          // изменился уровень стопа
          if (MathAbs(OrderStopLoss()-sotOSL[in])>=p) {
            st=st+" SL="+DoubleToStr(sotOSL[in], d)+"->"+DoubleToStr(OrderStopLoss(), d);
          }
          // изменился уровень тейка
          if (MathAbs(OrderTakeProfit()-sotOTP[in])>=p) {
            st=st+" TP="+DoubleToStr(sotOTP[in], d)+"->"+DoubleToStr(OrderTakeProfit(), d);
          }
          st=st+IIFs(OutMagic || OutComment, "\n", "")
               +IIFs(OutMagic, "Magic="+DoubleToStr(OrderMagicNumber(), 0), "")
               +IIFs(OutMagic && OutComment, " ", "")
               +IIFs(OutComment, "Comment=\""+OrderComment()+"\"", "")
               +IIFs(OutMagic || OutComment, "\n", "");
          if (StringLen(sh)>0) st=sh+st;
          Signal(st);
        }
      }
    }
  }

  // Закрытие позиций / Удаление ордеров
  k=ArraySize(sotOTI);
  for (i=0; i<k; i++) {
    if (ArraySearchInt(oti, sotOTI[i])<0) {
      ot=sotOTY[i];
      if (ot==OP_BUY || ot==OP_SELL) st="Close ";
      if (ot==OP_BUYLIMIT || ot==OP_BUYSTOP
      ||  ot==OP_SELLLIMIT || ot==OP_SELLSTOP) st="Delete ";
      if (OutTicket) st=st+DoubleToStr(sotOTI[i], 0)+" ";
      if (OrderSelect(sotOTI[i], SELECT_BY_TICKET)) {
        d=MarketInfo(OrderSymbol(), MODE_DIGITS);
        st=st+GetNameOP(ot)+" "
             +DoubleToStr(OrderLots(), 2)+" "
             +OrderSymbol()
             +" PR="+DoubleToStr(OrderClosePrice(), d)
             ;
        if (ot==OP_BUY || ot==OP_SELL) {
          st=st+" Profit="+DoubleToStr(OrderProfit()+OrderSwap(), 2)
               +"\n"
               +IIFs(OutMagic, "Magic="+DoubleToStr(OrderMagicNumber(), 0), "")
               +IIFs(OutMagic && OutComment, " ", "")
               +IIFs(OutComment, "Comment=\""+OrderComment()+"\"", "")
               +IIFs(OutMagic || OutComment, "\n", "")
               +"Balance="+DoubleToStr(AccountBalance(), 2)
               +" FreeMargin="+DoubleToStr(AccountFreeMargin(), 2);
        }
        if (StringLen(sh)>0) st=sh+st;
        Signal(st);
      }
    }
  }

  MemCurrOP();
}


//+----------------------------------------------------------------------------+
//|                                                                            |
//|  ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ                                                  |
//|                                                                            |
//+----------------------------------------------------------------------------+
//|  Выполняет поиск элемента массива по значению                              |
//|  и возвращает индекс найденного элемента или -1                            |
//|  Параметры:                                                                |
//|    m - массив элементов                                                    |
//|    e - значение элемента                                                   |
//+----------------------------------------------------------------------------+
int ArraySearchInt(int& m[], int e) {
  for (int i=0; i<ArraySize(m); i++) {
    if (m[i]==e) return(i);
  }
  return(-1);
}

//+----------------------------------------------------------------------------+
//|  Возвращает наименование торговой операции                                 |
//|  Параметры:                                                                |
//|    op - идентификатор торговой операции                                    |
//+----------------------------------------------------------------------------+
string GetNameOP(int op) {
	switch (op) {
		case OP_BUY      : return("Buy");
		case OP_SELL     : return("Sell");
		case OP_BUYLIMIT : return("BuyLimit");
		case OP_SELLLIMIT: return("SellLimit");
		case OP_BUYSTOP  : return("BuyStop");
		case OP_SELLSTOP : return("SellStop");
		default          : return("Unknown Operation");
	}
}

//+----------------------------------------------------------------------------+
//|  Возвращает одно из двух значений взависимости от условия.                 |
//+----------------------------------------------------------------------------+
string IIFs(bool condition, string ifTrue, string ifFalse) {
  if (condition) return(ifTrue); else return(ifFalse);
}

//+----------------------------------------------------------------------------+
//|  Запоминает текущие операции в массиве.                                    |
//+----------------------------------------------------------------------------+
void MemCurrOP() {
  int i, k=OrdersTotal();

  ArrayResize(sotOTI, k);
  ArrayResize(sotOTY, k);
  ArrayResize(sotOOP, k);
  ArrayResize(sotOSL, k);
  ArrayResize(sotOTP, k);
  for (i=0; i<k; i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
      sotOTI[i]=OrderTicket();
      sotOTY[i]=OrderType();
      sotOOP[i]=OrderOpenPrice();
      sotOSL[i]=OrderStopLoss();
      sotOTP[i]=OrderTakeProfit();
    }
  }
}

//+----------------------------------------------------------------------------+
//|  Подача сигнала.                                                           |
//|  Параметры:                                                                |
//|    st - строка сообщения                                                   |
//+----------------------------------------------------------------------------+
void Signal(string st) {
  if (UseAlert) Alert(st);
  if (UseComment) Comment(st);
  if (UseSendMail) SendMail(WindowExpertName(), st);
  if (UsePrint) Print(st);
  if (UseSound) PlaySound(sotFileName);
}
//+----------------------------------------------------------------------------+

