//+------------------------------------------------------------------+
//| Универсальный индикатор спреда на 2 инструмента. Особенности:  |
//| -- показывает линию бид-аск;                                     |
//| -- автоматически строит "конверт";                               |
//| -- балансировка производится объемами ставок по каждой ноге;     |
//| -- строит график изменения прибыльности по сделке.               |
//| Copyright © 2010, Son_Of_Earth                                   |
//+------------------------------------------------------------------+
#property copyright "Son_Of_Earth"
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_color1 clrAqua  
#property indicator_color2 clrRed
#property indicator_color3 clrBlue
#property indicator_color4 clrCoral
#property indicator_color5 clrCoral
#property indicator_color6 clrYellowGreen
#property indicator_color7 clrMediumTurquoise
#property indicator_color8 clrMediumTurquoise

extern string    Symbol1Name=""; // Нога BUY#I. Если не указан, берет по умолчанию текущий инструмент.
extern string    Symbol2Name=""; // Нога SELL#I.

extern string  __VOL___   = "- Авто расчет объемов -";
extern bool VOLMode = true;
extern int VOLPeriodATR = 144;            // Период усреднения ATR 
extern string  __VOLend___   = "- Объемы (при VOLMode=false) -";
extern double    Symbol1Vol=1;           // Множитель ноги BUY
extern double    Symbol2Vol=1;           // Множитель ноги SELL

extern bool      BidAskShow = false;      // Показывать линии бид-аск
extern bool      EnvShow = true;         // Показывать конверт
extern bool      EquityScale = true;      // Показывать масштаб эквити
extern int       EnvPeriod = 21;        // Период усреднения конверта
extern double    EnvDev = 1.0;           // Девиация конверта в процентах
extern double    AlertBidMore = EMPTY;   // Цена спреда, выше которой включает сигнал
extern double    AlertAskLess = EMPTY;   // Цена спреда, ниже которой включает сигнал

double Last[];
double _Ask[];
double _Bid[];
double EnvUp2[], EnvUp1[], MA[], EnvDn1[], EnvDn2[];
int wndNum;                                // Номер подокна индикатора
string wndName;                            // Наименование окна
int soundCount; 
double Symbol1K, Symbol2K;
string sname;

string oComment="spreadParam";             // Названия графических объектов

double volA1=1, volA2=EMPTY,     // Объем, рассчитанный по волатильности
      var1;
double kVol1,kVol2;                        // Изменение баланса при изменении цены инструмента 
                                           // на единицу в валюте котировки при объеме 1 лот

//===============================================================

int init(){

  if(Symbol1Name=="") Symbol1Name=Symbol();
  Symbol1Name=StringUpper(Symbol1Name);
  Symbol2Name=StringUpper(Symbol2Name);
  
  if(EquityScale) {
    Symbol1K = MarketInfo(Symbol1Name, MODE_TICKVALUE)/MarketInfo(Symbol1Name, MODE_TICKSIZE);
    Symbol2K = MarketInfo(Symbol2Name, MODE_TICKVALUE)/MarketInfo(Symbol2Name, MODE_TICKSIZE);
  }
  else {
    Symbol1K = 1;
    Symbol2K = 1;
  }
  
  // Если будет использоваться волатильность, рассчитываем объемы по волатильности
  if((VOLMode) && 
     iBars(Symbol1Name,0)>VOLPeriodATR &&     // Достаточно ли баров в истории для расчета волатильности?
     iBars(Symbol2Name,0)>VOLPeriodATR) {
    
    // Определяем балансовые коэффициенты каждого инструмента
    kVol1=MarketInfo(Symbol1Name, MODE_TICKVALUE)/MarketInfo(Symbol1Name, MODE_TICKSIZE);
    kVol2=MarketInfo(Symbol2Name, MODE_TICKVALUE)/MarketInfo(Symbol2Name, MODE_TICKSIZE);
  
    var1=volA1*kVol1*iATR(Symbol1Name,0,VOLPeriodATR,1);
    volA2=var1/kVol2/iATR(Symbol2Name,0,VOLPeriodATR,1);
    Symbol1Vol = volA1;
    Symbol2Vol = volA2;
  }
 
  int StartBar = Bars - MathMax(iBarShift(NULL,0,MathMax(iTime(Symbol1Name,0,iBars(Symbol1Name,0)-1),iTime(Symbol2Name,0,iBars(Symbol2Name,0)-1))),0);

  sname = DoubleToStr(Symbol1Vol,2)+"*"+DoubleToStr(Symbol1K,0)+"*"+Symbol1Name;
  sname=sname+"-";
  sname=sname+DoubleToStr(MathAbs(Symbol2Vol),2)+"*"+DoubleToStr(Symbol2K,0)+"*"+Symbol2Name;
  if(EnvShow)
    sname=sname+"("+EnvPeriod+","+DoubleToStr(EnvDev,2)+")";

  SetIndexShift(0,0);
  SetIndexBuffer(0,Last);
  SetIndexStyle(0,DRAW_LINE);
  SetIndexLabel(0,"Last");
  SetIndexDrawBegin(0,StartBar);

  if(BidAskShow) {
    SetIndexShift(1,0);
    SetIndexBuffer(1,_Ask);
    SetIndexStyle(1,DRAW_LINE);
    SetIndexLabel(1,"Ask");
    SetIndexDrawBegin(1,StartBar);

    SetIndexShift(2,0);
    SetIndexBuffer(2,_Bid);
    SetIndexStyle(2,DRAW_LINE);
    SetIndexLabel(2,"Bid");
    SetIndexDrawBegin(2,StartBar);
  }

  if(EnvShow) {
    SetIndexShift(3,0);
    SetIndexBuffer(3,EnvUp2);
    SetIndexStyle(3,DRAW_LINE);
    SetIndexLabel(3,"EnvUp2");
    SetIndexDrawBegin(3,StartBar+EnvPeriod);

    SetIndexShift(4,0);
    SetIndexBuffer(4,EnvUp1);
    SetIndexStyle(4,DRAW_LINE);
    SetIndexLabel(4,"EnvUp1");
    SetIndexDrawBegin(4,StartBar+EnvPeriod);

    SetIndexShift(5,0);
    SetIndexBuffer(5,MA);
    SetIndexStyle(5,DRAW_LINE);
    SetIndexLabel(5,"MA");
    SetIndexDrawBegin(5,StartBar+EnvPeriod);

    SetIndexShift(6,0);
    SetIndexBuffer(6,EnvDn1);
    SetIndexStyle(6,DRAW_LINE);
    SetIndexLabel(6,"EnvDn1");
    SetIndexDrawBegin(6,StartBar+EnvPeriod);

    SetIndexShift(7,0);
    SetIndexBuffer(7,EnvDn2);
    SetIndexStyle(7,DRAW_LINE);
    SetIndexLabel(7,"EnvDn2");
    SetIndexDrawBegin(7,StartBar+EnvPeriod);
  }

  soundCount=10;

  return(0);
}

//===============================================================

int deinit()  {
  // Удаляем все графические объекты
  DeleteObject(oComment);
  return(0);  
}

//===============================================================

int start()
{
  wndNum=WindowFind(WindowExpertName());
  wndName=WindowExpertName()+wndNum; 
  DrawLabel(oComment, sname, 8, DarkGray, 16);
  int    counted_bars=IndicatorCounted();
  if(counted_bars<0) return(-1);
  if(counted_bars>0) counted_bars--; 
  int limit=Bars-counted_bars;

  int i;
  datetime t;

  if(BidAskShow) {
    double ask1 = Symbol1Vol*Symbol1K*MarketInfo(Symbol1Name,MODE_ASK);
    double bid1 = Symbol1Vol*Symbol1K*MarketInfo(Symbol1Name,MODE_BID);
    double ask2 = Symbol2Vol*Symbol2K*MarketInfo(Symbol2Name,MODE_ASK);
    double bid2 = Symbol2Vol*Symbol2K*MarketInfo(Symbol2Name,MODE_BID);
    double _ask = ask1-bid2, 
           _bid = bid1-ask2;
  }

  for (i=0;i<limit;i++) {
    t=Time[i];
    Last[i] = Symbol1Vol*Symbol1K*iClose(Symbol1Name,0,iBarShift(Symbol1Name,0,t)) - 
              Symbol2Vol*Symbol2K*iClose(Symbol2Name,0,iBarShift(Symbol2Name,0,t));
    if(BidAskShow) {
      _Ask[i]=_ask;
      _Bid[i]=_bid;
    }
  }

  if(EnvShow) {
    for(i=0;i<limit;i++) {
      EnvUp2[i]=iEnvelopesOnArray(Last,0,EnvPeriod,MODE_SMA,0,EnvDev,MODE_UPPER,i);
      EnvUp1[i]=iEnvelopesOnArray(Last,0,EnvPeriod,MODE_SMA,0,EnvDev/2,MODE_UPPER,i);
      MA[i]=iMAOnArray(Last,0,EnvPeriod,0,MODE_SMA,i);
      EnvDn1[i]=iEnvelopesOnArray(Last,0,EnvPeriod,MODE_SMA,0,EnvDev/2,MODE_LOWER,i);
      EnvDn2[i]=iEnvelopesOnArray(Last,0,EnvPeriod,MODE_SMA,0,EnvDev,MODE_LOWER,i);
    }
  }
  if (soundCount>0 && 
      ((AlertBidMore!=EMPTY &&  _Bid[0]>AlertBidMore) || (AlertAskLess!=EMPTY && _Ask[0]<AlertAskLess))) {
    PlaySound("alert2.wav");
    Print(Symbol1Name+"-"+Symbol2Name+": channel width condition is achived"); 
    soundCount--;
  }
  return(0);
}

//-----------------------------------------

// Рисуем метку
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, 0);
  ObjectSet(objName, OBJPROP_XDISTANCE, 4);
  ObjectSet(objName, OBJPROP_YDISTANCE, aValue); 
}

// Удаляем графический объект
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);
}

