//+------------------------------------------------------------------
//|
//| original can be found here http://www.trade2win.com/boards/forex-strategies-systems/71854-correlation-trading-basic-ideas-strategies-64.html
//| this wersion was made by mladen
//| USD centered version made by spotforex
//+------------------------------------------------------------------
#property copyright "www.forex-tsd.com"
#property link      "www.forex-tsd.com"

#property indicator_separate_window
#property indicator_buffers 7
#property indicator_level1 0.0

//
//
//
//
//

extern string Parameters       = "Parameters";
extern double cycles           = 1.0;
extern double filter           = 1.0;
//extern int    PerAvr           = 200;
extern int    Delta            =   3;
extern bool   EURShow          = true;
extern color  EURColor         = DeepSkyBlue;
extern bool   GBPShow          = true;
extern color  GBPColor         = Magenta;
extern bool   AUDShow          = true;
extern color  AUDColor         = Goldenrod;
extern bool   CHFShow          = true;
extern color  CHFColor         = White;
extern bool   JPYShow          = true;
extern color  JPYColor         = Yellow;
extern bool   NZDShow          = true;
extern color  NZDColor         = Aqua;
extern bool   CADShow          = true;
extern color  CADColor         = Red;
extern int    LinesWidth       = 0;
extern string SymbolSuffix     = "";
extern bool   alertsOn         = false;
extern int    alertsWhenNCross = 0;
extern bool   alertsOnCurrent  = false;
extern bool   alertsMessage    = true;
extern bool   alertsSound      = true;
extern bool   alertsEmail      = false;
extern string UniqueID         = "FXCorrelator";
extern bool   DisplayLabels    = true;
extern bool   DisplayPositiveNegative = true;
extern int    PosNegXPosition  = 10;
extern int    PosNegYPosition  = 10;
extern int    PosNegCorner     =  3;
extern color  PosNegColor      = DarkGray;

//
//
//
//
//

double Idx0[],Idx1[],Idx2[],Idx3[],Idx4[],Idx5[],Idx6[];
string Currencies[] = {"EUR","GBP","AUD","CHF","JPY","NZD","CAD"};
int    Colors[7];
string ShortName;
bool   returnBars;
string indicatorFileName;

//+------------------------------------------------------------------
//|
//+------------------------------------------------------------------
//
//
//
//
//

int init()
{
   SetIndexBuffer(0,Idx0);
   SetIndexBuffer(1,Idx1);
   SetIndexBuffer(2,Idx2);
   SetIndexBuffer(3,Idx3);
   SetIndexBuffer(4,Idx4);
   SetIndexBuffer(5,Idx5);
   SetIndexBuffer(6,Idx6);
         Colors[0] = EURColor; Colors[1] = GBPColor; Colors[2] = AUDColor; 
         Colors[3] = CHFColor; Colors[4] = JPYColor; Colors[5] = NZDColor; 
         Colors[6] = CADColor;
         
         //
         //
         //
         //
         //
         
         indicatorFileName = WindowExpertName();
         returnBars = (Parameters=="returnBars");
//         ShortName = "FXcorrelator ("+PerAvr+"/"+Delta+")";
         ShortName = "pa FXCorrelator USD ("+DoubleToStr(cycles,2)+","+DoubleToStr(filter,2)+","+Delta+")";
   IndicatorShortName(ShortName);
   return(0);
}

int deinit()
{
   int length = StringLen(UniqueID);
   for (int i=ObjectsTotal()-1; i>=0; i--)
   {
      string name = ObjectName(i); if (StringSubstr(name,0,length) == UniqueID) ObjectDelete(name);
   }         
   return(0); 
}

//+------------------------------------------------------------------
//|
//+------------------------------------------------------------------
//
//
//
//
//

int  current[][4];
#define _strongest 0
#define _weakest   1
#define _trendup   2
#define _trenddn   3

int start()
{  
   int window = WindowFind(ShortName);
   int i,r,limit,counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         limit=MathMin(Bars-1,Bars-counted_bars);
         if (returnBars) { Idx0[0]=limit; return(0); }
         if (ArrayRange(current,0)!=Bars) ArrayResize(current,Bars);
         
      //
      //
      //
      //
      //
         
      static bool initialized = false;
            if (DisplayLabels && !initialized)
            {
               int x = 25;
               int y = 16;

                  for (i = 0; i<7; i++,x+=33)
                  {
                     SetIndexLabel(i,Currencies[i]);
                     SetIndexStyle(i,DRAW_LINE,EMPTY,LinesWidth,Colors[i]); 
                     
                        string name = UniqueID+":obj_"+Currencies[i];
                           ObjectCreate(name,OBJ_LABEL,window,0,0);
                              ObjectSet(name,OBJPROP_XDISTANCE,x);
                              ObjectSet(name,OBJPROP_YDISTANCE,y);
                              ObjectSetText(name,Currencies[i],10,"Arial bold",Colors[i]);
                  }
               initialized = true;
            }
            checkLimit("EURUSD",limit);
            checkLimit("GBPUSD",limit);
            checkLimit("AUDUSD",limit);
            checkLimit("USDCHF",limit);
            checkLimit("USDJPY",limit);
            checkLimit("NZDUSD",limit);
            checkLimit("USDCAD",limit);
    
   //
   //
   //
   //
   //
              
   for (i=limit, r=Bars-i-1; i>=0; i--,r++)    
   {
      int PerAvr = MathMax(iHilbertPhase(iMA(NULL,0,1,0,MODE_SMA,PRICE_CLOSE,i),filter,cycles,i),3);
      double A1 = (iLwma(iMA("EURUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i      ),PerAvr,i, 0)-iLwma(iMA("EURUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i+Delta),PerAvr,i, 1))/1; 
      double A2 = (iLwma(iMA("GBPUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i      ),PerAvr,i, 2)-iLwma(iMA("GBPUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i+Delta),PerAvr,i, 3))/1; 
      double A3 = (iLwma(iMA("AUDUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i      ),PerAvr,i, 4)-iLwma(iMA("AUDUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i+Delta),PerAvr,i, 5))/1; 
      double A4 = (iLwma(iMA("USDCHF"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i+Delta),PerAvr,i, 6)-iLwma(iMA("USDCHF"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i      ),PerAvr,i, 7))/1;
      double A5 = (iLwma(iMA("USDJPY"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i+Delta),PerAvr,i, 8)-iLwma(iMA("USDJPY"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i      ),PerAvr,i, 9))/100; 
      double A6 = (iLwma(iMA("NZDUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i      ),PerAvr,i,10)-iLwma(iMA("NZDUSD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i+Delta),PerAvr,i,11))/1;
      double A7 = (iLwma(iMA("USDCAD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i+Delta),PerAvr,i,12)-iLwma(iMA("USDCAD"+SymbolSuffix,0,1,0,MODE_SMA,PRICE_CLOSE,i      ),PerAvr,i,13))/1;
      
      //
      //
      //    USD centered made by spotforex
      //
      //
      
      double USD = -(A1+A2+A3+A4+A5+A6+A7)/7;
      double EUR =  (A1-(A2+A3+A4+A5+A6+A7)/6) - USD;
      double GBP =  (A2-(A1+A3+A4+A5+A6+A7)/6) - USD;
      double AUD =  (A3-(A1+A2+A4+A5+A6+A7)/6) - USD;
      double CHF =  (A4-(A1+A2+A3+A5+A6+A7)/6) - USD;
      double JPY =  (A5-(A1+A2+A3+A4+A6+A7)/6) - USD;
      double NZD =  (A6-(A1+A2+A3+A4+A5+A7)/6) - USD;
      double CAD =  (A7-(A1+A2+A3+A4+A5+A6)/6) - USD;

      //
      //
      //
      //
      //

      current[r][_strongest] = current[r-1][_strongest];
      current[r][_weakest]   = current[r-1][_weakest];
      current[r][_trendup]   = 0;
      current[r][_trenddn]   = 0;
      double max    = -EMPTY_VALUE;
      double min    =  EMPTY_VALUE;
             
         if ( EURShow ) { Idx0[i] = EUR; checkMinMax(min,max,EUR,0,r); }
         if ( GBPShow ) { Idx1[i] = GBP; checkMinMax(min,max,GBP,1,r); }
         if ( AUDShow ) { Idx2[i] = AUD; checkMinMax(min,max,AUD,2,r); }
         if ( CHFShow ) { Idx3[i] = CHF; checkMinMax(min,max,CHF,3,r); }
         if ( JPYShow ) { Idx4[i] = JPY; checkMinMax(min,max,JPY,4,r); }
         if ( NZDShow ) { Idx5[i] = NZD; checkMinMax(min,max,NZD,5,r); }
         if ( CADShow ) { Idx6[i] = CAD; checkMinMax(min,max,CAD,6,r); }
   }
   if (DisplayPositiveNegative)
   {
         name = UniqueID+":posneg_";
            ObjectCreate(name,OBJ_LABEL,window,0,0);
               ObjectSet(name,OBJPROP_XDISTANCE,PosNegXPosition);
               ObjectSet(name,OBJPROP_YDISTANCE,PosNegXPosition);
               ObjectSet(name,OBJPROP_CORNER   ,PosNegCorner);
               ObjectSetText(name,"currently "+DoubleToStr(current[Bars-1][_trendup],0)+" positive and "+DoubleToStr(current[Bars-1][_trenddn],0)+" negative",10,"Arial bold",PosNegColor);
   }
   manageAlerts();
   return(0);
}


//+------------------------------------------------------------------
//|
//+------------------------------------------------------------------
//
//
//
//
//

double workLwma[][14];
double iLwma(double price, double period, int r, int instanceNo=0)
{
   if (ArrayRange(workLwma,0)!= Bars) ArrayResize(workLwma,Bars); r = Bars-r-1;
   
   //
   //
   //
   //
   //
   
   workLwma[r][instanceNo] = price;
      double sumw = period;
      double sum  = period*price;

      for(int k=1; k<period && (r-k)>=0; k++)
      {
         double weight = period-k;
                sumw  += weight;
                sum   += weight*workLwma[r-k][instanceNo];  
      }             
      return(sum/sumw);
}

//+------------------------------------------------------------------
//|
//+------------------------------------------------------------------
//
//
//
//
//

void checkLimit(string symbol, int& limit)
{
   string checkSymbol = symbol+SymbolSuffix;
      if (checkSymbol!=Symbol())
         limit = MathMin(Bars-1,iCustom(checkSymbol,0,indicatorFileName,"returnBars",0,0));
}

void checkMinMax(double& min, double& max, double value, int forSymbol, int r)
{
   if (value>0)   { current[r][_trendup] += 1; }
   if (value<0)   { current[r][_trenddn] += 1; }
   if (value>max) { current[r][_strongest] = forSymbol; max=value; }
   if (value<min) { current[r][_weakest]   = forSymbol; min=value; }
}

//
//
//
//
//

double workHil[][9];
#define _price      0
#define _smooth     1
#define _detrender  2
#define _period     3
#define _instPeriod 4
#define _phase      5
#define _deltaPhase 6
#define _Q1         7
#define _I1         8

#define Pi 3.14159265358979323846264338327950288

//
//
//
//
//

double iHilbertPhase(double price, double filter, double cyclesToReach, int i, int s=0)
{
   if (ArrayRange(workHil,0)!=Bars) ArrayResize(workHil,Bars);
   int r = Bars-i-1; s = s*9;
      
   //
   //
   //
   //
   //
      
      workHil[r][s+_price]      = price;
      workHil[r][s+_smooth]     = (4.0*workHil[r][s+_price]+3.0*workHil[r-1][s+_price]+2.0*workHil[r-2][s+_price]+workHil[r-3][s+_price])/10.0;
      workHil[r][s+_detrender]  = calcComp(r,_smooth,s);
      workHil[r][s+_Q1]         = 0.15*calcComp(r,_detrender,s)  +0.85*workHil[r-1][s+_Q1];
      workHil[r][s+_I1]         = 0.15*workHil[r-3][s+_detrender]+0.85*workHil[r-1][s+_I1];
      workHil[r][s+_phase]      = workHil[r-1][s+_phase];
      workHil[r][s+_instPeriod] = workHil[r-1][s+_instPeriod];

      //
      //
      //
      //
      //
           
         if (MathAbs(workHil[r][s+_I1])>0)
                     workHil[r][s+_phase] = 180.0/Pi*MathArctan(MathAbs(workHil[r][s+_Q1]/workHil[r][s+_I1]));
           
         if (workHil[r][s+_I1]<0 && workHil[r][s+_Q1]>0) workHil[r][s+_phase] = 180-workHil[r][s+_phase];
         if (workHil[r][s+_I1]<0 && workHil[r][s+_Q1]<0) workHil[r][s+_phase] = 180+workHil[r][s+_phase];
         if (workHil[r][s+_I1]>0 && workHil[r][s+_Q1]<0) workHil[r][s+_phase] = 360-workHil[r][s+_phase];

      //
      //
      //
      //
      //
                        
      workHil[r][s+_deltaPhase] = workHil[r-1][s+_phase]-workHil[r][s+_phase];

         if (workHil[r-1][s+_phase]<90 && workHil[r][s+_phase]>270)
             workHil[r][s+_deltaPhase] = 360+workHil[r-1][s+_phase]-workHil[r][s+_phase];
             workHil[r][s+_deltaPhase] = MathMax(MathMin(workHil[r][s+_deltaPhase],60),7);
      
            //
            //
            //
            //
            //
                  
            double alpha    = 2.0/(1.0+MathMax(filter,1));
            double phaseSum = 0; for (int k=0; phaseSum<cyclesToReach*360 && (r-k)>0; k++) phaseSum += workHil[r-k][s+_deltaPhase];
         
               if (k>0) workHil[r][s+_instPeriod]= k;
                  workHil[r][s+_period] = workHil[r-1][s+_period]+alpha*(workHil[r][s+_instPeriod]-workHil[r-1][s+_period]);
   return (workHil[r][s+_period]);
}

//
//
//
//
//

double calcComp(int r, int from, int s)
{
   return((0.0962*workHil[r  ][s+from] + 
           0.5769*workHil[r-2][s+from] - 
           0.5769*workHil[r-4][s+from] - 
           0.0962*workHil[r-6][s+from]) * (0.075*workHil[r-1][s+_period] + 0.54));
}

//
//
//
//
//

void manageAlerts()
{
   if (alertsOn)
   {
      if (alertsOnCurrent)      
            int whichBar = Bars-1;
      else      whichBar = Bars-2;            

      //
      //
      //
      //
      //
      
      static datetime time1=0;
      static string   mess1="";
         if (current[whichBar][_strongest]!= current[whichBar-1][_strongest])
                 doAlert(time1,mess1,"New maximum reached by : "+Currencies[current[whichBar][_strongest]]+", Current weakest is : "+Currencies[current[whichBar][_weakest]]);
      static datetime time2=0;
      static string   mess2="";
         if (current[whichBar][_weakest]!= current[whichBar-1][_weakest])
                 doAlert(time2,mess2,"New minimum reached by : "+Currencies[current[whichBar][_weakest]]+", Current strongest is : "+Currencies[current[whichBar][_strongest]]);

      //
      //
      //
      //
      //

      static datetime time3=0;
      static string   mess3="";
         if (current[whichBar][_trendup]>=alertsWhenNCross && current[whichBar-1][_trendup]<alertsWhenNCross)
                     doAlert(time3,mess3,"Currently "+current[whichBar][_trendup]+" grouped up");
      static datetime time4=0;
      static string   mess4="";
         if (current[whichBar][_trenddn]>=alertsWhenNCross && current[whichBar-1][_trenddn]<alertsWhenNCross)
                     doAlert(time4,mess4,"Currently "+current[whichBar][_trenddn]+" grouped down");
   }
}   

//
//
//
//
//

void doAlert(datetime& previousTime, string& previousAlert, string doWhat)
{
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[0]) {
       previousAlert  = doWhat;
       previousTime   = Time[0];

       //
       //
       //
       //
       //

       message =  StringConcatenate(Symbol()," at ",TimeToStr(TimeLocal(),TIME_SECONDS)," FX correlator ",doWhat);
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(StringConcatenate(Symbol(),"FX Correlator "),message);
          if (alertsSound)   PlaySound("alert2.wav");
   }
}