//+------------------------------------------------------------------+
//| Индикатор арбитражной ситуации для спреда, состоящего            |
//| из трех инструментов                                             |
//| Copyright © 2010, leonid553, Son_Of_Earth                        |
//+------------------------------------------------------------------+
#property copyright "leonid553 & Son_Of_Earth"
#property link      "leonid553@ya.ru,sonofearth.ua@gmail.com"

#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 Lime
#property indicator_color2 DodgerBlue
#property indicator_color3 Red
#property indicator_color4 Aqua
#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 1
#property indicator_width4 1

extern string Symbol1Name = "EURUSD";     // Название инструмента 1
extern bool   Symbol1Reverse = false;     // Является ли инструмент 1 реверсивным (с обратной корреляцией) 
                                           // по отношению к остальным инструментам
extern string Symbol2Name = "USDCHF";     // Название инструмента 2
extern bool   Symbol2Reverse = false;     // Является ли инструмент 2 реверсивным (с обратной корреляцией) 
                                           // по отношению к остальным инструментам

extern string  ___MISC___ = " == Прочие настройки ==========";
extern bool   UseVolatility  =True ;             //отрисовка с учетом волатильности
extern bool   Triangle =True ;             // Рисовать треугольник?
extern color  TriangleColor = DarkSalmon; // Цвет треугольника
extern double AlertChannelMore = EMPTY;   // Если ширина канала больше указанной, включается сигнал
                                           //   -1 - контроль выключен.
extern double AlertChannelLess = EMPTY;   // Если ширина канала меньше указанной, включается сигнал
                                           //   -1 - контроль выключен.


extern string  __VOL___   = " == Расчет объемов ====";
extern int VOLMode = 3;                   // Режим расчета объемов для торговли
                                           //   1 - по ценам открытия
                                           //   2 - по волатильности, а если невозможно - по ценам открытия
                                           //   3 - по волатильности (если возможно) и ценам открытия
extern int VOLPeriodATR = 144;            // Период усреднения ATR 

extern string  ___MA___   = " == Параметры МА ценовых линий ====";

extern int MASlow = 21;                   // Период медленной МА
extern int MAFast = 8;                    // Период быстрой МА
extern int MAMethod=2;                    // Метод аппроксимации
                                           // - MODE_SMA=0 Простое скользящее среднее 
                                           // - MODE_EMA=1 Экспоненциальное скользящее среднее 
                                           // - MODE_SMMA=2 Сглаженное скользящее среднее 
                                           // - MODE_LWMA=3 Линейно-взвешенное скользящее среднее 
extern int MAPrice=6;                     // Расчетная цена:
                                           // - PRICE_CLOSE=0 Цена закрытия 
                                           // - PRICE_OPEN=1 Цена открытия 
                                           // - PRICE_HIGH=2 Максимальная цена 
                                           // - PRICE_LOW=3 Минимальная цена 
                                           // - PRICE_MEDIAN=4 Средняя цена, (high+low)/2 
                                           // - PRICE_TYPICAL=5 Типичная цена, (high+low+close)/3 
                                           // - PRICE_WEIGHTED=6 Взвешенная цена закрытия, (high+low+close+close)/4 

// Буферы для отображения данных
double Buf1[];    // Первый инструмент
double Buf2[];    // Второй инструмент
double BufS[];    // Средняя линия
double BufW[];    // Ширина канала
double BufW_Up[]; // Восходящий канал
double BufW_Dn[]; // Нисходящий канал

// Параметры графических объектов 
string LabelName = "label";  
color  LabelColor = Aqua;
string TriangleName = "triangle";  
int wndNum;                                // Номер подокна индикатора
string wndName;                            // Наименование окна
string oSymb1, oSymb2,                     // Названия графических объектов
       oSymbS, oLabel, oTriangle;

double kPrice1,kPrice2;                    // Весовые коэффициенты ценовых графиков
double kVol1,kVol2;                        // Изменение баланса при изменении цены инструмента 
                                           // на единицу в валюте котировки при объеме 1 лот
int soundCount;                            // Счетчик повторов сигнала

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init() {
  // Делаем нечувствительными к регистру ввода наименования инструментов
  Symbol1Name=StringUpper(Symbol1Name);
  Symbol2Name=StringUpper(Symbol2Name);

  // Определяем балансовые коэффициенты каждого инструмента
  kVol1=MarketInfo(Symbol1Name, MODE_TICKVALUE)/MarketInfo(Symbol1Name, MODE_TICKSIZE);
  kVol2=MarketInfo(Symbol2Name, MODE_TICKVALUE)/MarketInfo(Symbol2Name, MODE_TICKSIZE);
  
  // Задаем имена графическим объектам
  oSymb1=Symbol1Name;
  oSymb2=Symbol2Name;
  oLabel=LabelName;
  oTriangle=TriangleName;

  // Формируем свойства графиков индикатора
  IndicatorBuffers(7);
  
  SetIndexStyle(0,DRAW_LINE);
  SetIndexBuffer(0,Buf1);
  SetIndexLabel(0, Symbol1Name); 

  SetIndexStyle(1,DRAW_LINE);
  SetIndexBuffer(1,Buf2);
  SetIndexLabel(1,Symbol2Name); 

  SetIndexStyle(2,DRAW_LINE);
  SetIndexBuffer(2,BufW_Up);
  SetIndexLabel(2,"Channel width Up");

  SetIndexStyle(3,DRAW_LINE);
  SetIndexBuffer(3,BufW_Dn);
  SetIndexLabel(3,"Channel width Down");
  
  SetIndexBuffer(4,BufW);
  SetIndexBuffer(5,BufS);

  soundCount=10; // Максимально к-во повторов звукового сигнала

  return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()  {
  // Удаляем все графические объекты
  DeleteObject(oSymb1);
  DeleteObject(oSymb2); 
  DeleteObject(oLabel);
  DeleteObject(oTriangle);
  return(0);  
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start() {
  // Подсчет количества баров для построения индикатора
  int limit; int counted_bars=IndicatorCounted();
  if(counted_bars<0) return(-1);
  if(counted_bars>0) counted_bars--; 
  limit=Bars-counted_bars;
  
  // Определяем параметры окна индикатора
  wndNum=WindowFind(WindowExpertName());
  wndName=WindowExpertName()+wndNum; 
 
  //------------------------------------------------------------------ 
  // Расчет ценовых коэффициентов путем масштабирования
  // обратно пропорционально текущей цене
  kPrice1=100; 
  kPrice2=kPrice1/iOpen(Symbol2Name,0,0)*iOpen(Symbol1Name,0,0); 
  // Если график цены реверсивен остальным инструментам 
  // (например, падает, когда другие инструменты растут)
  // разворачиваем такой график.
  if(Symbol1Reverse) kPrice1=-kPrice1; 
  if(Symbol2Reverse) kPrice2=-kPrice2;
  
  //--------------------------------------------------------------------  
  // Расчет соотношения объемов для торговли.
  // Рассчитываются не абсолютные значения, а относительные, приведенные
  // к первому инструменту. При определении абсолютных объемов, исходя
  // из выбранной модели управления капиталом, следует сохранить 
  // рассчитанные пропорции.
  
  double volA1=1, volA2=EMPTY,     // Объем, рассчитанный по волатильности
         volP1=1, volP2=EMPTY,     // Объем, рассчитанный по цене открытия
         var1;

  // Если будет использоваться волатильность, рассчитываем объемы по волатильности
  if((VOLMode==2 || VOLMode==3) && 
     iBars(Symbol1Name,0)>VOLPeriodATR &&     // Достаточно ли баров в истории для расчета волатильности?
     iBars(Symbol2Name,0)>VOLPeriodATR) {
    var1=volA1*kVol1*iATR(Symbol1Name,0,VOLPeriodATR,1);
    volA2=var1/kVol2/iATR(Symbol2Name,0,VOLPeriodATR,1);
  }
  // Если будет использоваться цена открытия, рассчитываем объемы по цене открытия
  if(VOLMode==1 || VOLMode==3 || volA2==EMPTY) {
    var1=volP1*kVol1*iOpen(Symbol1Name,0,0);
    volP2=var1/kVol2/iOpen(Symbol2Name,0,0);
  } 
  
  //------------------------------------------------------------------ 
  // Отрисовка ценовых линий: рисуем и синхронизируем по наличию баров
  for(int i=0; i<limit; i++) { 
    // Первый ценовой график
    Buf1[i]= kPrice1*
      (iMA(Symbol1Name,Period(),MAFast,0,MAMethod,MAPrice,iBarShift(Symbol1Name,0,Time[i],false))-
       iMA(Symbol1Name,Period(),MASlow,0,MAMethod,MAPrice,iBarShift(Symbol1Name,0,Time[i],false)));
   
    // Второй ценовой график
      if (UseVolatility==false){
      Buf2[i]= kPrice2*
     (iMA(Symbol2Name,Period(),MAFast,0,MAMethod,MAPrice,iBarShift(Symbol2Name,0,Time[i],false))-
      iMA(Symbol2Name,Period(),MASlow,0,MAMethod,MAPrice,iBarShift(Symbol2Name,0,Time[i],false)));
                        }
     if (UseVolatility==true && volA2!=EMPTY){
      Buf2[i]= kPrice2*volA2*
     (iMA(Symbol2Name,Period(),MAFast,0,MAMethod,MAPrice,iBarShift(Symbol2Name,0,Time[i],false))-
      iMA(Symbol2Name,Period(),MASlow,0,MAMethod,MAPrice,iBarShift(Symbol2Name,0,Time[i],false)));
                        }                   
                          
 // График средней цены
    BufS[i]=(Buf1[i]+Buf2[i])/2;                
    // График ширины ценового канала
    BufW[i]=Buf1[i]-Buf2[i];
    // Разделяем канал на восходящий и нисходящий
    BufW_Up[i]=EMPTY_VALUE;
    BufW_Dn[i]=EMPTY_VALUE; 
    if(MathAbs(BufW[i])>MathAbs(BufW[i-1])) {
      BufW_Up[i]=BufW[i];
      BufW_Up[i-1]=BufW[i-1];
    }
    else {
      BufW_Dn[i]=BufW[i];
      BufW_Dn[i-1]=BufW[i-1];
    }
  } // Конец блока отрисовки ценовых линий

 //------------------------------------------------------------------------
  // Построение графических объектов
  // Пишем комментарий в окне индикатора с правой стороны
  string sVolA1="",sVolA2="",sVolP1="",sVolP2="";
  if(volP2!=EMPTY) {
    sVolP1=DoubleToStr(volP1,2)+"= ";
    sVolP2=DoubleToStr(volP2,2)+"= ";
  }
  if(volA2!=EMPTY) {
    sVolA1=" ="+DoubleToStr(volA1,2);
    sVolA2=" ="+DoubleToStr(volA2,2);
  }
  DrawLabel(oSymb1, sVolP1+Symbol1Name+sVolA1, 10, indicator_color1, 5);
  DrawLabel(oSymb2, sVolP2+Symbol2Name+sVolA2, 10, indicator_color2,18);
  
  // Отображаем  схождение-расхождение-флет линий                       
  string label;                     
  double width1, width2, width3;           // Ширина элементарного канала
  width1=MathAbs(Buf1[1]-BufS[1])+
         MathAbs(Buf2[1]-BufS[1]);
  width2=MathAbs(Buf1[2]-BufS[2])+
         MathAbs(Buf2[2]-BufS[2]);
  width3=MathAbs(Buf1[3]-BufS[3])+
         MathAbs(Buf2[3]-BufS[3]);
  // Опререляем схождение-расхождение по трем последовательным сформировавшимся барам
  if(width2>width1 && width3>width2 )     // схождение
    label = "Convergence";
  else if(width2<width1 && width3<width2) // расхождение
    label = "Divergence";
  else 
    label = "Flat";                       // флет
  DrawLabel(oLabel,label, 9, LabelColor, 31); 

  //  Рисуем треугольник - графическую интерпретацию схождения-расхождения
  if(Triangle) {                                    // если отрисовка треугольника разрешена 
    datetime level_1,level_2,level_3;               // объявляем координаты по вертик. оси 
    double delta_1,delta_2,delta_3;                 // объявляем координаты по гориз. оси  
    double PriceMax   =  WindowPriceMax(wndNum);
    double PriceMin   =  WindowPriceMin(wndNum);
    double PriceMid   = (PriceMax+PriceMin)/2;
    double PriceDelta = (PriceMax-PriceMin)/8;
    datetime BegTime, EndTime;
    BegTime=Time[0]+Period()*233;
    EndTime=Time[0]+Period()*700;
    if(width2>width1 && width3>width2) {            // если имеет место схождение  
      level_1=BegTime;  delta_1 = PriceMid+PriceDelta;
      level_2=BegTime;  delta_2 = PriceMid-PriceDelta;
      level_3=EndTime;  delta_3 = PriceMid;  
    }       
    else if(width2<width1 && width3<width2) {       // если имеет место расхождение       
      level_1=BegTime; delta_1 = PriceMid;
      level_2=EndTime; delta_2 = PriceMid+PriceDelta;
      level_3=EndTime; delta_3 = PriceMid-PriceDelta;  
    } 
    else {                                          // если имеет место флет
      level_1=BegTime;  delta_1=PriceMid;
      level_2=BegTime;  delta_2=PriceMid;
      level_3=BegTime;  delta_3=PriceMid;
    }
    // Собственно отрисовка треугольника                  
    DrawTriangle(oTriangle,level_1,delta_1,level_2,delta_2,level_3,delta_3);
  } //  конец блока отрисовки графических элементов       

  //------------------------------------------------------------------------
  // Контроль сужения или расширения канала до заданных пределов
  if (soundCount>0 && 
      ((AlertChannelMore!=EMPTY &&  BufW[0]>AlertChannelMore) || (AlertChannelLess!=EMPTY && BufW[0]<AlertChannelLess))) {
    PlaySound("alert2.wav");
    Print(Symbol1Name+"-"+Symbol2Name+": channel width condition is achived"); 
    soundCount--;
  }

    return(0);
} // Конец функции Start

//+----------------- Пользовательские функции ------------------------------------------+

// Рисуем метку
void DrawLabel(string aName, string aText, int aFontSize, color aColor, int aValue) {
  string objName=aName+wndName;
  ObjectDelete(objName);
  ObjectCreate(objName, OBJ_LABEL, wndNum, 0, 0);
  ObjectSetText(objName, aText, aFontSize, "Verdana", aColor);
  ObjectSet(objName, OBJPROP_CORNER, 1);
  ObjectSet(objName, OBJPROP_XDISTANCE, 4);
  ObjectSet(objName, OBJPROP_YDISTANCE, aValue); 
}

// Рисуем треугольник
void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3) {
  string objName=name+wndName;
  ObjectDelete(objName);
  if(ObjectCreate(objName,OBJ_TRIANGLE,wndNum,t1,p1,t2,p2,t3,p3)) { 
    ObjectSet(objName, OBJPROP_COLOR, TriangleColor); 
    ObjectSet(objName, OBJPROP_PRICE1, p1);
    ObjectSet(objName, OBJPROP_PRICE2, p2);
    ObjectSet(objName, OBJPROP_PRICE3, p3);
    ObjectSet(objName, OBJPROP_TIME1, t1);
    ObjectSet(objName, OBJPROP_TIME2, t2);
    ObjectSet(objName, OBJPROP_TIME3, t3);
  }
}

// Удаляем графический объект
void DeleteObject(string name) {
  ObjectDelete(name+wndName);
}

// Переводим строку в верхний регистр
string StringUpper(string s) {
  int c, i, k=StringLen(s), n;
  for (i=0; i<k; i++) {
    n=0;
    c=StringGetChar(s, i);
    if (c>96 && c<123) n=c-32;    // a-z -> A-Z
    if (c>223 && c<256) n=c-32;   // а-я -> А-Я
    if (c==184) n=168;            //  ё  ->  Ё
    if (n>0) s=StringSetChar(s, i, n);
  }
  return(s);
}

