//+------------------------------------------------------------------+
//|                                          The Holy Grail v1.6.mq4 |
//|              Copyright © 2009, Mark Johnson. All rights reserved |
//|                                        http://www.metaquotes.net |
//+------------------------------------------------------------------+


// this will be a customized version of HG v1.6; however, i only change to my preferences,
// or correct bugs; the logic of trading is intact.

// 1. change GMGoffset using mbkennel: http://www.forexfactory.com/showthread.php?t=204646&page=479
// 2. change pairs to HG v2.x according to scoobs: http://www.forexfactory.com/showthread.php?t=204646&page=480
// 3. add 'PosCount()' to allow multi-EA on 1 account;
// 4. add a switch for fixed lotsize;

// To do :

//1. tp override
//2. pend with stoplevel
//3. pa 4 in&out

#property show_inputs

//+------------------------------------------------------------------+
//| expert External variables                                        |
//+------------------------------------------------------------------+

extern int    Magic    = 1601;
extern int    GMTShift = -6;
extern string Key      = "Charvo Customized HG v2.x";

extern int    OrderLimit = 6;
extern bool   LotFixed = true;
extern double fixedLot = 0.01;

//+------------------------------------------------------------------+
//| expert Internal variables                                        |
//+------------------------------------------------------------------+

//string Pairs[] = {"AUDJPY","AUDUSD","CHFJPY","EURGBP","EURJPY","EURUSD",
//                  "GBPJPY","GBPUSD","USDCAD","USDCHF","USDJPY"};


/*string Pairs[] = {"AUDJPY","AUDUSD","CADJPY","CHFJPY","EURAUD",
                  "EURCAD","EURCHF","EURGBP","EURJPY","EURUSD",
                  "GBPJPY","GBPUSD","USDCAD","USDCHF","USDJPY"};*/

string Pairs[] = {"AUDJPY","AUDUSD","CADJPY","CHFJPY" ,"EURAUD","EURCAD","EURGBP",
                  "EURJPY","EURUSD","GBPJPY","GBPUSD" ,"USDCAD","USDCHF","USDJPY"};

string Author  = "Copyright © 2009, Mark Johnson. All rights reserved.";



int M05 = PERIOD_M5;
int M15 = PERIOD_M15;
int M30 = PERIOD_M30;
int H01 = PERIOD_H1;
int H04 = PERIOD_H4;
int D01 = PERIOD_D1;

int OB = 80;
int ML = 50;
int OS = 20;

int Decimals;

//+------------------------------------------------------------------+
//| expert Initialization function                                   |
//+------------------------------------------------------------------+

int init()
  {

    string Suffix = StringSubstr(Symbol(),6,StringLen(Symbol())-6);

    for(int a = 0; a < ArraySize(Pairs); a++)
      {
        Pairs[a] = Pairs[a] + Suffix;
      }
    
    double Step = MarketInfo(Symbol(),MODE_LOTSTEP);

    if(Step == 0.01) Decimals = 2;
    if(Step == 0.10) Decimals = 1;
    if(Step == 1.00) Decimals = 0;

    if(Magic == 0) Magic = AccountNumber();
    
    Comment("\nWaiting for tick update...");

    return(0);
    
  }
  
//+------------------------------------------------------------------+
//| expert Deinitialization function                                 |
//+------------------------------------------------------------------+

int deinit()
  {

    Comment("");
    
    return(0);

  }
  
//+------------------------------------------------------------------+
//| expert Start function                                            |
//+------------------------------------------------------------------+

int start()
  {
    
    if (LotFixed == false)
    {
      double Lots = NormalizeDouble(AccountBalance()/60000,Decimals);
      double Min  = MarketInfo(Symbol(),MODE_MINLOT);
      double Max  = MarketInfo(Symbol(),MODE_MAXLOT);

      if(Lots < Min) Lots = Min;
      if(Lots > Max) Lots = Max;
    }
    else
    {
      Lots = fixedLot;
    }

    string Status = "Trading enabled...";
    
/*    int gmtHH = TimeHour(TimeLocal());
    
    if(GMTShift < 0) gmtHH = gmtHH - MathAbs(GMTShift);
    if(GMTShift > 0) gmtHH = gmtHH + MathAbs(GMTShift);
    
    if((TimeDayOfWeek(TimeLocal()) == 5 && TimeHour(TimeLocal()) > 12) ||
       (TimeDayOfWeek(TimeLocal()) == 0 && TimeHour(TimeLocal()) < 23) ||
        TimeDayOfWeek(TimeLocal()) == 6 || NewsExist() == true)
      {
        Status = "Trading disabled...";
      }  */
//**********
    int gmtHH = TimeHour(TimeLocal()) - GMTShift; /* GMTShift = -5 for NYC without daylight savings, e.g. */ 
    int gmtday = TimeDayOfWeek(TimeLocal());
    if (gmtHH < 0) 
    {
      gmtHH += 24;
      gmtday -= 1;   
    }
    if (gmtHH > 23) 
    {
      gmtHH -= 24;
      gmtday += 1;
    }
    if (gmtday < 0) {gmtday += 7;}
    if (gmtday > 6) {gmtday -= 7;}
       
    if((gmtday == 5 && gmtHH > 11) || (gmtday == 0 && gmtHH < 23) ||  gmtday == 6) //|| NewsExist() == true) 
    {   // do not trade on Friday past GMT 11:59, do not trade sunday before AUD/JPY open, or Saturday ever, or if NewsExist()
        Status = "Trading disabled for time or news...";
    }
//**********
    Comment("\n",Author,"\n\n",Status,"\n\n","Lots = ",DoubleToStr(Lots,2));

    if(StringFind(Status,"disabled",0) > 0)
      {
        return(0);
      }

    for(int b = 0; b < ArraySize(Pairs); b++)
      {
      
        if(PosCount(Magic)>=OrderLimit)
          {
            break;
          }

        if(MarketInfo(Pairs[b],MODE_BID) > 0 && MarketInfo(Pairs[b],MODE_ASK) > 0)
          {      
            
            int Rsi = 2;

            if((StringSubstr(Pairs[b],0,3) == "AUD" && (gmtHH >= 22 || gmtHH < 06)) || 
               (StringSubstr(Pairs[b],3,3) == "AUD" && (gmtHH >= 22 || gmtHH < 06)) ||
               (StringSubstr(Pairs[b],0,3) == "CAD" && (gmtHH >= 13 && gmtHH < 21)) || 
               (StringSubstr(Pairs[b],3,3) == "CAD" && (gmtHH >= 13 && gmtHH < 21)) ||
               (StringSubstr(Pairs[b],0,3) == "CHF" && (gmtHH >= 07 && gmtHH < 15)) || 
               (StringSubstr(Pairs[b],3,3) == "CHF" && (gmtHH >= 07 && gmtHH < 15)) ||
               (StringSubstr(Pairs[b],0,3) == "EUR" && (gmtHH >= 07 && gmtHH < 15)) || 
               (StringSubstr(Pairs[b],3,3) == "EUR" && (gmtHH >= 07 && gmtHH < 15)) ||
               (StringSubstr(Pairs[b],0,3) == "GBP" && (gmtHH >= 08 && gmtHH < 16)) || 
               (StringSubstr(Pairs[b],3,3) == "GBP" && (gmtHH >= 08 && gmtHH < 16)) ||
               (StringSubstr(Pairs[b],0,3) == "JPY" && (gmtHH >= 24 || gmtHH < 08)) || 
               (StringSubstr(Pairs[b],3,3) == "JPY" && (gmtHH >= 24 || gmtHH < 08)) ||
               (StringSubstr(Pairs[b],0,3) == "NZD" && (gmtHH >= 22 || gmtHH < 06)) || 
               (StringSubstr(Pairs[b],3,3) == "NZD" && (gmtHH >= 22 || gmtHH < 06)) ||
               (StringSubstr(Pairs[b],0,3) == "USD" && (gmtHH >= 13 && gmtHH < 21)) ||
               (StringSubstr(Pairs[b],3,3) == "USD" && (gmtHH >= 13 && gmtHH < 21)))  
              {
                Rsi = 3;
              }            

            //double RSI_M05 = iRSI(Pairs[b],M05,Rsi,PRICE_CLOSE,0);
            double RSI_M15 = iRSI(Pairs[b],M15,Rsi,PRICE_CLOSE,0);
            double RSI_H01 = iRSI(Pairs[b],H01,Rsi,PRICE_CLOSE,0);
            double RSI_H04 = iRSI(Pairs[b],H04,Rsi,PRICE_CLOSE,0);
         
            
            if(Trend(Pairs[b]) == "U")
            //   Trend_Strength(Pairs[b],H04) == "U" )
              {
                if( RSI_M15 < OS && RSI_H01 < OS && RSI_H04 < OS )
                  {
                    if(TradeExist(Pairs[b]) == false && LagRSI_Agree(Pairs[b], M05, OP_BUY, 0.9, 0.1) )
                      {
                        if(IsTradeAllowed() == true) SendOrder(Pairs[b],OP_BUY,Lots);
                        //if(IsTradeAllowed() == true) PendOrder(Pairs[b],OP_BUYLIMIT,Lots);
                      }
                  }
              }
               
            if(Trend(Pairs[b]) == "D")
            //   Trend_Strength(Pairs[b],H04) == "D" )
              {
                if( RSI_M15 > OB && RSI_H01 > OB && RSI_H04 > OB )
                  {
                    if(TradeExist(Pairs[b]) == false && LagRSI_Agree(Pairs[b], M05, OP_SELL, 0.9, 0.1) )
                      {
                        if(IsTradeAllowed() == true) SendOrder(Pairs[b],OP_SELL,Lots);
                        //if(IsTradeAllowed() == true) PendOrder(Pairs[b],OP_SELLLIMIT,Lots);
                      }
                  }
              }
              
          }
      
      }
      
    return(0);

  }
//+------------------------------------------------------------------+
//| expert LaguerreRSI function |
//+------------------------------------------------------------------+ 
double LaguerreRSI(string strPair, int intTF, double dblGamma, int intShift)
{
   int intSize = 100;

   double dblRSI, dblUp, dblDown;
   double L[4], LA[4];

   ArrayInitialize( L, 0.0 );

   for(int inx = intSize; inx >= 0; inx--)
   {

      double Price = iMA(strPair,intTF,1,0,MODE_EMA,PRICE_CLOSE,inx+intShift);

      ArrayCopy(LA, L);
      dblUp = 0.0;
      dblDown = 0.0;

      L[0] = ( 1 - dblGamma ) * Price + ( dblGamma * LA[0] );
      L[1] = ( -dblGamma * L[0] ) + LA[0] + ( dblGamma * LA[1] );
      L[2] = ( -dblGamma * L[1] ) + LA[1] + ( dblGamma * LA[2] );
      L[3] = ( -dblGamma * L[2] ) + LA[2] + ( dblGamma * LA[3] );

      if(L[0] >= L[1]) dblUp = L[0] - L[1]; 
      else dblDown = L[1] - L[0];

      if(L[1] >= L[2]) dblUp = dblUp + L[1] - L[2]; 
      else dblDown = dblDown + L[2] - L[1];

      if(L[2] >= L[3]) dblUp = dblUp + L[2] - L[3]; 
      else dblDown = dblDown + L[3] - L[2];

      if(dblUp + dblDown != 0.0) dblRSI = dblUp / (dblUp + dblDown); 
   }

   return( dblRSI );

}

//+------------------------------------------------------------------+
//| expert LaguerreRSI Pattern Identification Function |
//+------------------------------------------------------------------+ 
bool LagRSI_Agree(string Pair, int TF, int PosType, double OBlvl, double OSlvl)
{
  bool LagAgree = false;
  
  double LagRSI0 = LaguerreRSI(Pair, M05, 0.55, 0);
  double LagRSI1 = LaguerreRSI(Pair, M05, 0.55, 1);
  double LagRSI2 = LaguerreRSI(Pair, M05, 0.55, 2);
 
  if (PosType == OP_BUY) 
  {
   if(LagRSI0>OSlvl && LagRSI1<OSlvl && LagRSI2<OSlvl) LagAgree = true;
  }

  if (PosType == OP_SELL) 
  {
   if(LagRSI0<OBlvl && LagRSI1>OBlvl && LagRSI2>OBlvl) LagAgree = true;
  }
   
  return(LagAgree);

}
//+------------------------------------------------------------------+
//| expert TradeExists function                                      |
//+------------------------------------------------------------------+

bool TradeExist(string symbol)
  {

    bool Found = false;

    for(int c = 0; c <= OrdersTotal(); c++)
      {
        if(OrderSelect(c,SELECT_BY_POS,MODE_TRADES) == true)
          {
            if(OrderSymbol() == symbol && OrderMagicNumber() == Magic)
              {
                Found = true;
                break;
              }
          }
      }
    
    return(Found);

  }

//+------------------------------------------------------------------+
//| expert SendOrder function                                        |
//+------------------------------------------------------------------+  

void SendOrder(string symbol, int dir, double lots)
  {
    
    double price;
    double point;
    double sl;
    double tp;

    int ticket = -1;
    int spread = 10;
    int digit  = 0;
    
    int take = iATR(symbol,PERIOD_H4,20,0)/MarketInfo(symbol,MODE_POINT); 
    int stop = iATR(symbol,PERIOD_H4,20,0)/MarketInfo(symbol,MODE_POINT);
        
    if(MarketInfo(symbol,MODE_DIGITS) == 3 || MarketInfo(symbol,MODE_DIGITS) == 5)
      {
        spread *= 10;
      }
    
    take += MarketInfo(symbol,MODE_SPREAD);
    
    if(dir == OP_BUY && MarketInfo(symbol,MODE_SPREAD) <= spread)
      {
        while(ticket < 0)
          {
            RefreshRates();
            price  = MarketInfo(symbol,MODE_ASK);
            point  = MarketInfo(symbol,MODE_POINT);
            digit  = MarketInfo(symbol,MODE_DIGITS);
            sl     = NormalizeDouble(price-stop*point,digit);
            tp     = NormalizeDouble(price+take*point,digit);
            ticket = OrderSend(symbol,dir,lots,price,3,sl,tp,Key,Magic,0,CLR_NONE);
            Print("HG v1.6 SendOrder() BUY has ", GetLastError());
            Sleep(8000);
          }
      }
      
    if(dir == OP_SELL && MarketInfo(symbol,MODE_SPREAD) <= spread)
      {
        while(ticket < 0)
          {
            RefreshRates();
            price  = MarketInfo(symbol,MODE_BID);
            point  = MarketInfo(symbol,MODE_POINT);
            digit  = MarketInfo(symbol,MODE_DIGITS);
            sl     = NormalizeDouble(price+stop*point,digit);
            tp     = NormalizeDouble(price-take*point,digit);
            ticket = OrderSend(symbol,dir,lots,price,3,sl,tp,Key,Magic,0,CLR_NONE);
            Print("HG v1.6 SendOrder() Sell has ", GetLastError());
            Sleep(8000);
          }
      }
  
  }
  
//+------------------------------------------------------------------+
//| expert PendOrder function                                        |
//+------------------------------------------------------------------+  
/*
void PendOrder(string symbol, int dir, double lots)
  {
    int stplvl = 0;
    int spred = 0;
    double price = 0;
    double sl, tp;
    int order = -1;
    
    double point  = MarketInfo(symbol,MODE_POINT);    
    int digit  = MarketInfo(symbol,MODE_DIGITS);
    int take = 2 * iATR(symbol,M15,14,0)/MarketInfo(symbol,MODE_POINT); 
    int stop = 3 * iATR(symbol,M15,14,0)/MarketInfo(symbol,MODE_POINT);
    

    if(dir == OP_BUYLIMIT)
      {
        while(order < 0)
          {
            RefreshRates();
            stplvl = MarketInfo(symbol,MODE_STOPLEVEL);
            spred = MarketInfo(symbol,MODE_SPREAD);
            price = MarketInfo(symbol,MODE_ASK)-stplvl*MarketInfo(symbol,MODE_POINT);
            sl     = NormalizeDouble(price-stop*point,digit);
            tp     = NormalizeDouble(price+take*point,digit);
            order = OrderSend(symbol,dir,lots,price,3,sl,tp,Key,Magic,0,CLR_NONE);
            Print("HG v1.6 PendOrder() BUYLIMIT has: ", GetLastError());
            Sleep(10000);
          }
      }
      
    if(dir == OP_SELLLIMIT)
      {
        while(order < 0)
          {
            RefreshRates();
            stplvl = MarketInfo(symbol,MODE_STOPLEVEL);
            spred = MarketInfo(symbol,MODE_SPREAD);
            price = MarketInfo(symbol,MODE_BID)+stplvl*MarketInfo(symbol,MODE_POINT);
            sl     = NormalizeDouble(price+stop*point,digit);
            tp     = NormalizeDouble(price-take*point,digit);
            order = OrderSend(symbol,dir,lots,price,3,sl,tp,Key,Magic,0,CLR_NONE);
            Print("HG v1.6 PendOrder() SELLLIMIT has: ", GetLastError());
            Sleep(10000);
          }
      }
  
  }
*/  
//+------------------------------------------------------------------+
//| expert News function -- a contribution by mbkennel               |
//  http://www.forexfactory.com/showthread.php?p=3510269&highlight=newsexist#post3510269
//+------------------------------------------------------------------+  
bool NewsExist()
{
   static int minuteSave=-1;
   static bool NewsSave = true;

   /* do not hammer server each tick */    
   if (Minute() == minuteSave) {
      return(NewsSave);
   }
   minuteSave = Minute();
   bool News = false;

   for(int d = 0; d < ArraySize(Pairs); d++) {
      int MinutesSincePrevEvent = iCustom(NULL,0,"Economic News",Pairs[d],true,false,false,true,true,1,0);
      int MinutesUntilNextEvent = iCustom(NULL,0,"Economic News",Pairs[d],true,false,false,true,true,1,1);

      if(MinutesUntilNextEvent < 60 || MinutesSincePrevEvent < 15) {
      News = true;
      }
   }

   string xmlFileName = Month()+"-"+Day()+"-"+Year()+"-"+Symbol()+Period()+"-"+"FFCal.xml";
   int handle = FileOpen(xmlFileName,FILE_BIN|FILE_READ|FILE_WRITE);
   if(handle < 0) {
         News = true;
   } else {
     FileClose(handle);
   }

   NewsSave = News;
   return(News);
}
//+------------------------------------------------------------------+
//| expert Trend_Strength function |
//+------------------------------------------------------------------+ 

string Trend_Strength(string symbol, int tf)
{

string Trend = "F";  // Flat

double ADX01 = iADX(symbol,tf,10,PRICE_CLOSE, MODE_MAIN,0);

double EMA01 = iMA(symbol,tf,20,0,MODE_EMA,PRICE_CLOSE,0);
double EMA02 = iMA(symbol,tf,40,0,MODE_EMA,PRICE_CLOSE,0);
double EMA03 = iMA(symbol,tf,80,0,MODE_EMA,PRICE_CLOSE,0);

if(ADX01 > 20)
{
   if(EMA01 > EMA02 && EMA02 > EMA03)
   {
   Trend = "U";
   }
   if(EMA01 < EMA02 && EMA02 < EMA03)
   {
   Trend = "D";
   }
}

return(Trend);

}
//+------------------------------------------------------------------+
//| expert Trend function                                            |
//+------------------------------------------------------------------+  

string Trend(string symbol)
  {
  
    string Pair_Trend = "F"; // Flat  

    double EMA20_1 = iMA(symbol,M15,020,0,MODE_EMA,PRICE_CLOSE,0);
    double EMA20_2 = iMA(symbol,H01,020,0,MODE_EMA,PRICE_CLOSE,0);
    double EMA20_3 = iMA(symbol,H04,020,0,MODE_EMA,PRICE_CLOSE,0);
    
    if((EMA20_1 < EMA20_2) && (EMA20_2 < EMA20_3))
      {
        Pair_Trend = "D"; // Down
      }

    if((EMA20_1 > EMA20_2) && (EMA20_2 > EMA20_3))
      {
        Pair_Trend = "U"; // Up
      }
       
    return(Pair_Trend);
  
  }

//+------------------------------------------------------------------+
//| expert positions counter function                                            |
//+------------------------------------------------------------------+  
      
int PosCount(int magic)
{

   int poscnt=0;
   
   for(int c = 0; c <= OrdersTotal(); c++)
   {
      if(OrderSelect(c,SELECT_BY_POS,MODE_TRADES) == true)
      {
         if(OrderMagicNumber() == magic)
         {
          poscnt++;
         }
      }
   }
   return(poscnt);
}