//+-------------------------------------------------------------------------------------+
//|                                                               MultiValuteSignal.mq4 |
//|                                                                           Scriptong |
//|                                                                   scriptong@mail.ru |
//+-------------------------------------------------------------------------------------+
#property copyright "Scriptong"
#property link      "scriptong@mail.ru"

#property indicator_separate_window
//---- input parameters
extern int    Formula = 0;                         // Параметр внешнего индикатора
extern string A1 = "Расчетный таймфрейм (1- М1, 60 - H1, 240 - H4)";
extern int    TF = 1;
extern int RSIperiod = 13;
extern int shift = 1;
extern string A2 = "Список поддерживаемых валют (XXX - параметр игнорируется)";
extern string Valute1 = "USD";
extern string Valute2 = "GBP";
extern string Valute3 = "EUR";
extern string Valute4 = "CAD";
extern string Valute5 = "JPY";
extern string Valute6 = "AUD";
extern string Valute7 = "NZD";
extern string Valute8 = "CHF";
extern string Valute9 = "PLN";
extern string Valute10 = "HUF";
extern string Valute11 = "CZK";
extern string Valute12 = "DKK";
extern string Valute13 = "NOK";
extern string Valute14 = "SEK";
extern string Valute15 = "SGD";
extern string Valute16 = "ZAR";
extern string Valute17 = "XXX";
extern string Valute18 = "XXX";
extern string Valute19 = "XXX";
extern string Valute20 = "XXX";
extern string A4 = "Размер шрифта названия валютных пар";
extern int    TextSize = 8;
extern string A5 = "Цвет сигнала Buy";
extern color  BuyColor = Blue;
extern string A6 = "Цвет сигнала Sell";
extern color  SellColor = Red;
extern string A7 = "Цвет состояния \"нет сигнала\"";
extern color  NoSignalColor = Black;
extern string A8 = "Короткое имя индикатора для идентификации своего подокна";
extern string ShortName = "MultiValuteSignal";
extern double Ma = 190;

#define COLOR_BUY 1
#define COLOR_SELL -1
#define COLOR_NONE 0
#define COLOR_NDF 255

string TextFont = "Courier";

datetime LastBar,                                  // Время открытия текущего бара на..
                                                   // ..текущей валютной паре. До тех пор
                                                   // ..пока время открытия текущих баров
                                                   // ..остальных валютных пар не станет
                                                   // ..большим или равным времени..
                                                   // ..открытия текущего бара текущей..
                                                   // ..валютной пары, будет..
                                                   // ..производиться обращение ко всем..
                                                   // ..валютным парам
         LastAlert[210];                           // Время подачи последнего звукового..
                                                   // ..синала для каждой валютной пары
                                                   
string Valute[20],                                 // Рассматриваемые валюты, из которых
                                                   // ..формируются пары
       Pairs[210];                                 // Названия найденных валютных пар

int    PC,                                         // Количество найденных валютных пар
       Valutes,                                    // Количество используемых валют
       LastSignal[210],                            // Последнее известное значение цвета
                                                   // ..для валютной пары:
                                                   // COLOR_BUY - цвет сигнала покупки
                                                   // COLOR_SELL - цвет сигнала продажи
                                                   // COLOR_NONE -цвет отсутствия сигнала
                                                   // COLOR_NDF - не определен
       MyWindow;                                   // номер подокна индикатора
bool IsInit, Activate;
              

//+-------------------------------------------------------------------------------------+
//| Custom indicator initialization function                                            |
//+-------------------------------------------------------------------------------------+
int init()
{
   IsInit = False;                                 // Функция init не завершена
   MyWindow = -1;                                  // Подокно индикатора не определено
   ArrayInitialize(LastSignal, COLOR_NDF);         // Сигналы для пар не определены
   ArrayInitialize(LastAlert, 0);                  // Звуковые сообщения не выдавались
// - 1 - == Заполнение массива Valute ===================================================
   Valutes = 0;                                    // Ни одна из валют не определена
   FormValutes(Valute1);
   FormValutes(Valute2);
   FormValutes(Valute3);
   FormValutes(Valute4);
   FormValutes(Valute5);
   FormValutes(Valute6);
   FormValutes(Valute7);
   FormValutes(Valute8);
   FormValutes(Valute9);
   FormValutes(Valute10);
   FormValutes(Valute11);
   FormValutes(Valute12);
   FormValutes(Valute13);
   FormValutes(Valute14);
   FormValutes(Valute15);
   FormValutes(Valute16);
   FormValutes(Valute17);
   FormValutes(Valute18);
   FormValutes(Valute19);
   FormValutes(Valute20);
// - 1 - == Окончание блока  ============================================================

// - 2 - == Поиск доступных валютных пар ================================================
   FormPairs();
   if (PC == 0)
   {
      Alert("Ни одна валютная пара с содержанием указанных валют не найдена!");
      return(0);
   }
   SortPairs();                                    // Сортировка найденных валютных пар
// - 2 - == Окончание блока  ============================================================

// - 3 - == Проверка корректности заданного таймфрейма ==================================
   if (TF != 1 && TF != 5 && TF != 15 && TF != 30 && TF != 60 && TF != 240&& TF !=1440&&TF != 10080 && TF != 43200  )
   {
      Alert("Задан некорректный или слишком большой таймфрейм TF. Индикатор выключен.");
      return(0);
   }
   
// - 3 - == Окончание блока  ============================================================

// - 4 - == Окончание инициализации индикатора ==========================================
   IndicatorShortName(ShortName);                  // Зададим имя индикатора, по которому
                                                   // ..найдем его окно впоследствии
   LastBar = 0;                                    // Ни один бар не обработан
   IsInit = True;                                  // Инициализация успешно завершена
// - 4 - == Окончание блока  ============================================================

   return(0);
}
//+-------------------------------------------------------------------------------------+
//| Custom indicator deinitialization function                                          |
//+-------------------------------------------------------------------------------------+
int deinit()
{
   for (int i = ObjectsTotal()-1; i >= 0; i--)     // Удалим все созданные графические..
     if (StringSubstr(ObjectName(i), 0, StringLen(ShortName)) == ShortName)// ..объекты
       ObjectDelete(ObjectName(i));
   Comment("");       
   return(0);
}
//+-------------------------------------------------------------------------------------+
//| Запись валюты в массив Valute и подсчет количества валют                            |
//+-------------------------------------------------------------------------------------+
void FormValutes(string symbol)
{
   if (symbol == "XXX" || StringLen(symbol) != 3)  // Длина имени валюты должна быть три
      return;                                      // ..символа и не равна XXX
   Valute[Valutes] = symbol;                       // Запишем валюту в список
   Valutes++;                                      // Увеличим количество валют
}
//+-------------------------------------------------------------------------------------+
//| Поиск валютных пар на основании списка используемых валют Valute[]                  |
//+-------------------------------------------------------------------------------------+
void FormPairs()
{
   PC = 0;                                         // Ни одна валютная пара не найдена
   for (int i = 0; i < Valutes; i++)               // Найдем все валютные пары,..
                                                   // ..включающие данную валюту
      for (int k = i+1; k < Valutes; k++)          // Подставляем оставшиеся валюты к..
      {                                            // ..валюте Valute[i]
      // - 1 - == Проверка существования прямого сочетания валют ========================
         string Try = Valute[i] + Valute[k];       // Например, "USD" + "CHF" = USDCHF
         MarketInfo(Try, MODE_BID);                // Обращение к валютной паре Try
         if (GetLastError() == 0)                  // Если валютная пара существует,..
         {                                         // ..то ошибка получена не будет
            Pairs[PC] = Try;                       // Запись валютной пары в список
            PC++;                                  // Увеличиваем кол-во найденных пар
            continue;                              // Перейдем к следующей итерации цикла
         }                                         // ..k
      // - 1 - == Окончание блока =======================================================
          
      // - 2 - == Проверка существования обратного сочетания валют ======================
         Try = Valute[k] + Valute[i];              // Наоборот: "USD" + "CHF" = CHFUSD
         MarketInfo(Try, MODE_BID);                // Обращение к валютной паре
         if (GetLastError() == 0)                  // Если валютная пара существует,..
         {                                         // ..то ошибка получена не будет
            Pairs[PC] = Try;                       // Запись валютной пары в список
            PC++;                                  // Увеличиваем кол-во найденных пар
         }                                         
      // - 2 - == Окончание блока =======================================================
      }    
}
//+-------------------------------------------------------------------------------------+
//| Сортировка массива найденных валютных пар                                           |
//+-------------------------------------------------------------------------------------+
void SortPairs()
{
// - 1 - == Инициализация процесса сортировки ===========================================
   int cnt = 0;                                    // Счетчик для массива SortPairs
   string sort[];                                  // Временный массив для сортировки
   ArrayResize(sort, PC);                          // Изменяем его размер в соответствии
                                                   // ..с количеством найденных пар
// - 1 - == Окончание блока =============================================================
                                                   
// - 2 - == Сортировка ==================================================================
   for (int i = 0; i < PC; i++)                    // По всем найденным парам
   {
      if (Pairs[i] == "") continue;                // Если пара уже занесена в массив..
                                                   // ..sort, то переходим к следующей..
                                                   // ..паре
      string base_name = StringSubstr(Pairs[i], 0, 3);// Получим базу валютной пары
      sort[cnt] = Pairs[i];                        // Пару записываем первой в столбец
      cnt++;
      Pairs[i] = "";                               // Вычеркиваем ее из Pairs
      for (int k = i+1; k < PC; k++)               // Пройдем по массиву Pairs от..
                                                   // ..текущего элемента до последнего
         if (Pairs[k] != "" &&                     // Найдем элементы с имеющейся записью
             StringSubstr(Pairs[k], 0, 3) == base_name)// ..у которых база равна искомой
         {
            sort[cnt] = Pairs[k];                  // Такую пару переписываем в массив 
            Pairs[k] = "";                         // Записанную пару убираем из Pairs
            cnt++;
         }
   }
// - 2 - == Окончание блока =============================================================

// - 3 - == Переписываем отсортированный массив в Pairs =================================
   for (i = 0; i < PC; i++)
      Pairs[i] = sort[i];
// - 3 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Получение ценыз акрытия свечи Num инструмента sym                                   |
//+-------------------------------------------------------------------------------------+
double GetClose(string sym, int Num)
{
   double Res;                                     // Результат вычисления функции
   int P = 0;                                      // Счетчик обращения к данным
   while (P < 10000)                               // Обращение не более 10000 раз подряд
   {
      Res = iClose(sym, TF, Num);                  // Получение цены закрытия
      if (GetLastError() == 0)                     // Присутствие ошибки свидетельствует
         break;                                    // ..о неготовности данных
      P++;                                         // Увеличение счетчика
   }
   if (P == 10000) // Если выход из цикла по причине переполнения счетчика (долго ждали),
     return(0);    // ..то функция вернет ошибку
   return(Res);    // Если же цикл прерывался успешным получением значения, то функция..
                   // ..вернет Res
}
//+-------------------------------------------------------------------------------------+
//| Отображение названия валютной пары и сигнала                                        |
//+-------------------------------------------------------------------------------------+
void ShowPair(int i, int signal, int x, int y)
{
   switch (signal)                                 // Определим, каким цветом..
   {                                               // ..подсвечивать надпись
      case -1: color iColor = SellColor;
               string signal_text = "продажи.";
         break;    
      case 1:  iColor = BuyColor; 
               signal_text = "покупки.";
         break;
      case 0:  iColor = NoSignalColor; break;
   }
   string name = ShortName + Pairs[i];             // Генерация имени объекта
 
   if (ObjectFind(name) < 0)                       // Объект не существует
   {
      ObjectCreate(name, OBJ_LABEL, MyWindow, 0,0);// Создание объекта-метки
      ObjectSet(name, OBJPROP_CORNER, 0);          // Угол привязки - левый верхний
      ObjectSet(name, OBJPROP_XDISTANCE, x);       // Зададим координату по горизонтали
      ObjectSet(name, OBJPROP_YDISTANCE, y);       // Зададим координату по вертикали
   }
   if (LastAlert[i] < iTime(Pairs[i], TF, 0))      // Если последний сигнал был раньше..
   {                                               // ..начала текущего бара
      if (LastSignal[i] != signal &&               // Если предыдущий сигнал не равен..
          LastSignal[i] != COLOR_NDF &&            // ..новому и при этом предыдущий..
          signal != 0)                             // ..сигнал определен, а новый сигнал
                                                   // ..не пассивен, то выдадим звуковой
         Alert("Пара ", Pairs[i], " сигнал ",      // ..сигнал
               signal_text);

					////////////////////////////////////////////////////////////////////////////////////////////////////////
					////////////////////////////////////////////////////////////////////////////////////////////////////////
					////////////////////////////////////////////////////////////////////////////////////////////////////////
				 	SendMail("Пара " + Pairs[i] + " сигнал " + signal_text , "Пара " + Pairs[i] + " сигнал " + signal_text);
					////////////////////////////////////////////////////////////////////////////////////////////////////////
					////////////////////////////////////////////////////////////////////////////////////////////////////////
					////////////////////////////////////////////////////////////////////////////////////////////////////////

      LastSignal[i] = signal;                      // Сохраним значение нового сигнала
      LastAlert[i] = iTime(Pairs[i], TF, 0);       // Запомним новое время
   }
   ObjectSetText(name, Pairs[i], TextSize, TextFont,// Изменение описания объекта
                 iColor);         
}
//+-------------------------------------------------------------------------------------+
//| Поиск своего подокна на текущем графике                                             |
//+-------------------------------------------------------------------------------------+
bool FindOwn()
{
   MyWindow = WindowFind(ShortName);               // Получим номер подокна индикатора с
                                                   // ..именем ShortName
   if (MyWindow < 0)                               // Если подокно не найдено, то..
   {                                               // ..выключаем индикатор
      Alert("Индикатор ", ShortName, " не смог найти свое окно!");
      IsInit = false;
      return(false);
   }
   return(true);                                   // Подокно найдено, продолжаем работу
}
//+-------------------------------------------------------------------------------------+
//| Генерация сигнала по одной валютной паре                                            |
//+-------------------------------------------------------------------------------------+
int GetSignal(string symbol)
{
// - 1 - == Получение цены закрытия последнего сформированного бара =====================
   double close = GetClose(symbol, 1);
   if (close == 0)                                 // Если получили 0, то это ошибка, о..
   {                                               // ..которой сообщим пользователю
      Comment("Не удалось получить данные по символу ", symbol, 
              ". Производятся дальнейшие попытки получения данных.");
      return(-2);
   }
// - 1 - == Окончание блока =============================================================

// - 2 - == Определение открытия нового бара на обрабатываемой паре =====================
   if (iTime(symbol, TF, 0) < iTime(NULL, TF, 0))  // Если открытие текущего бара имеет..
      return(-2);                                  // ..время, меньшее, чем время..
                                                   // ..открытия текущего бара текущей..
                                                   // ..пары, то возвратим ошибку расчета
// - 2 - == Окончание блока =============================================================

// - 3 - == Получение значений индикатора на последнем баре =============================
double RSI = iRSI(symbol,TF,RSIperiod,PRICE_CLOSE,shift);
// - 3 - == Окончание блока =============================================================

// - 4 - == Генерация сигнала ===========================================================
   if (RSI<30)                         // Цена закрытия свечи выше..
      return(1);                                   // ..сопротивления - сигнал покупки
   if (RSI>70)                            // Цена закрытия свечи ниже..
      return(-1);                                  // ..поддержки - сигнал продажи
   return(0);                                      // Цена закрытия свечи между уровнями
                                                   // ..нет сигнала
// - 4 - == Окончание блока =============================================================
}
//+-------------------------------------------------------------------------------------+
//| Custom indicator iteration function                                                 |
//+-------------------------------------------------------------------------------------+
int start()
{
// - 1 - == Прошла ли инициализация успешно? ============================================
   if (!IsInit) return(0);
// - 1 - == Окончание блока =============================================================

// - 2 - == Однократный поиск индекса своего подокна ====================================
   if (MyWindow < 0)                               // Если индекс еще не установлен, то..
      if (!FindOwn())                              // ..произведем поиск. При неудаче..
         return(0);                                // ..отключаем индикатор
// - 2 - == Окончание блока =============================================================
   
// - 3 - ================== Один раз на каждый бар производится полный перерасчет =======
   if (LastBar == iTime(NULL, TF, 0)) return(0);
// - 3 - ================================= Окончание блока ==============================  

// - 4 - ================ Расчет коэффициента корреляции и генерация сигнала ============
   int row = 0;                                    // Текушая строка
   int count = 0;                                  // Текущий столбец
   for (int i = 0; i < PC; i++)
   {
      int signal = GetSignal(Pairs[i]);            // Получим сигнал по обрабатываемой..
                                                   // ..валютной паре
      if (signal == -2)                            // Если не удалось рассчитать сигнал,
         return(0);                                // ..то уходим до следующего тика
      ShowPair(i, signal,                          // Отображаем состояние валютной пары
               count*TextSize*7+10, 
               row*(TextSize+3)+15);
      row++;
      if (StringSubstr(Pairs[i], 0, 3) != StringSubstr(Pairs[i+1], 0, 3))
      {
         count++;
         row = 0;
      }
   }
// - 4 - ================================= Окончание блока ==============================  
   
   Comment("");          
   LastBar = iTime(NULL, TF, 0);                   // На этом баре выполнения инструкций
                                                   // ..блока 4 функции start больше не..
                                                   // ..будет
   return(0);
}