//+----------------------------------------------------------------------------+
//|                                                            b-SendMail.mqh  |
//|                                                    Ким Игорь В. aka KimIV  |
//|                                                       http://www.kimiv.ru  |
//|                                                                            |
//|  29.06.2007  Библиотека функций передачи почтовых сообщений.               |
//|                                                                            |
//|  Для использования в советнике добавить строки:                            |
//|  - вне функций       : #include <b-SendMail.mqh>                           |
//|  - в функции start() : if (UseSendMail) AccountState();                    |
//+----------------------------------------------------------------------------+
//#property library

//------- Внешние параметры модуля --------------------------------------------+
extern string _P_b_SendMail = "---------- Параметры модуля b-SendMail";
extern bool UseSendMail       = True;     // Использовать передачу сообщений
extern bool SendAccountNumber = True;     // Передавать номер счёта
extern bool SendAccountServer = True;     // Передавать имя активного сервера
extern bool SendNameExpert    = True;     // Передавать имя эксперта
extern bool SendDateTime      = True;     // Передавать дату и время
extern bool UseTimeLocal      = True;     // Использовать локальное время
extern bool SendTicket        = False;    // Передавать тикет

//------- Глобальные переменные модуля ----------------------------------------+
int    bsmOTI[];             // тикет   OrderTicket()
int    bsmOTY[];             // тип     OrderType()
double bsmOOP[];             // цена    OrderOpenPrice()
double bsmOSL[];             // стоп    OrderStopLoss()
double bsmOTP[];             // тейк    OrderTakeProfit()
bool   bsmFirstRun=True;     // Флаг первого прогона

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

  if (bsmFirstRun) { MemCurrOP(); bsmFirstRun=False; }
  if (SendAccountNumber) sh=DoubleToStr(AccountNumber(), 0)+" ";
  if (SendAccountServer) sh=sh+AccountServer();
  if (StringLen(sh)>0) sh=sh+"\n";
  if (SendDateTime) {
    if (UseTimeLocal) sh=sh+TimeToStr(TimeLocal(), TIME_DATE|TIME_MINUTES)+" ";
    else sh=sh+TimeToStr(TimeCurrent(), TIME_DATE|TIME_SECONDS)+" ";
  }
  if (SendNameExpert) 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(bsmOTI, 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 (SendTicket) 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";
        if (ot==OP_BUY || ot==OP_SELL) {
          st=st+"Balance="+DoubleToStr(AccountBalance(), 2)
               +" FreeMargin="+DoubleToStr(AccountFreeMargin(), 2);
        }
        if (StringLen(sh)>0) st=sh+st;
        SendMail(WindowExpertName(), st);

      // операция найдена, значит существовала ранее и возможно изменена
      } else {
        if ((MathAbs(OrderOpenPrice()-bsmOOP[in])>=p)
        ||  (MathAbs(OrderStopLoss()-bsmOSL[in])>=p)
        ||  (MathAbs(OrderTakeProfit()-bsmOTP[in])>=p)) {
          st="Modify ";
          if (SendTicket) st=st+DoubleToStr(OrderTicket(), 0)+" ";
          st=st+GetNameOP(OrderType())+" "
            +DoubleToStr(OrderLots(), 2)+" "
            +OrderSymbol();
          // изменилась цена открытия
          if (MathAbs(OrderOpenPrice()-bsmOOP[in])>=p) {
            st=st+" PR="+DoubleToStr(bsmOOP[in], d)+"->"+DoubleToStr(OrderOpenPrice(), d);
          }
          // изменился уровень стопа
          if (MathAbs(OrderStopLoss()-bsmOSL[in])>=p) {
            st=st+" SL="+DoubleToStr(bsmOSL[in], d)+"->"+DoubleToStr(OrderStopLoss(), d);
          }
          // изменился уровень тейка
          if (MathAbs(OrderTakeProfit()-bsmOTP[in])>=p) {
            st=st+" TP="+DoubleToStr(bsmOTP[in], d)+"->"+DoubleToStr(OrderTakeProfit(), d);
          }
          if (StringLen(sh)>0) st=sh+st;
          SendMail(WindowExpertName(), st);
        }
      }
    }
  }

  // Открытие/Установка новых, модификация существующих
  k=ArraySize(bsmOTI);
  for (i=0; i<k; i++) {
    if (ArraySearchInt(oti, bsmOTI[i])<0) {
      ot=bsmOTY[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 (SendTicket) st=st+DoubleToStr(bsmOTI[i], 0)+" ";
      if (SelectByTicketFromHistory(bsmOTI[i])) {
        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"
               +"Balance="+DoubleToStr(AccountBalance(), 2)
               +" FreeMargin="+DoubleToStr(AccountFreeMargin(), 2);
        }
        if (StringLen(sh)>0) st=sh+st;
        SendMail(WindowExpertName(), 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("Buy Limit");
		case OP_SELLLIMIT: return("Sell Limit");
		case OP_BUYSTOP  : return("Buy Stop");
		case OP_SELLSTOP : return("Sell Stop");
		default          : return("Unknown Operation");
	}
}

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

  ArrayResize(bsmOTI, k);
  ArrayResize(bsmOTY, k);
  ArrayResize(bsmOOP, k);
  ArrayResize(bsmOSL, k);
  ArrayResize(bsmOTP, k);
  for (i=0; i<k; i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
      bsmOTI[i]=OrderTicket();
      bsmOTY[i]=OrderType();
      bsmOOP[i]=OrderOpenPrice();
      bsmOSL[i]=OrderStopLoss();
      bsmOTP[i]=OrderTakeProfit();
    }
  }
}

//+----------------------------------------------------------------------------+
//|  Возвращает флаг удачного выбора ордрера или позиции по тикету             |
//|  Параметры:                                                                |
//|    ti - тикет ордера, позиции                                              |
//|    sy - наименование инструмента   ("" - текущий символ)                   |
//|    op - операция                   (-1 - любая позиция)                    |
//|    mn - MagicNumber                (-1 - любой магик)                      |
//+----------------------------------------------------------------------------+
bool SelectByTicketFromHistory(int ti, string sy="", int op=-1, int mn=-1) {
  int i, k=OrdersHistoryTotal();

  if (sy=="") sy=Symbol();
  for (i=0; i<k; i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
      if (OrderSymbol()==sy && (op<0 || OrderType()==op)) {
        if ((mn<0 || OrderMagicNumber()==mn) && OrderTicket()==ti) return(True);
      }
    }
  }
  return(False);
}
//+----------------------------------------------------------------------------+

