//+------------------------------------------------------------------+
//|                                          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 v1.6";

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],M15) == "U" && Trend(Pairs[b],H01) == "U" &&
               Trend(Pairs[b],H04) == "U" && Trend(Pairs[b],D01) == "U")
              {
                if(RSI_M05 > OS && RSI_M15 < OS && RSI_H01 < OS && RSI_H04 < OS)
                  {
                    if(TradeExist(Pairs[b]) == false)
                      {
                        if(IsTradeAllowed() == true) SendOrder(Pairs[b],OP_BUY,Lots);
                        //if(IsTradeAllowed() == true) PendOrder(Pairs[b],OP_BUYLIMIT,Lots);
                      }
                  }
              }
               
            if(Trend(Pairs[b],M15) == "D" && Trend(Pairs[b],H01) == "D" && 
               Trend(Pairs[b],H04) == "D" && Trend(Pairs[b],D01) == "D")
              {
                if(RSI_M05 < OB && RSI_M15 > OB && RSI_H01 > OB && RSI_H04 > OB)
                  {
                    if(TradeExist(Pairs[b]) == false)
                      {
                        if(IsTradeAllowed() == true) SendOrder(Pairs[b],OP_SELL,Lots);
                        //if(IsTradeAllowed() == true) PendOrder(Pairs[b],OP_SELLLIMIT,Lots);
                      }
                  }
              }
              
          }
      
      }
      
    return(0);

  }

//+------------------------------------------------------------------+
//| 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 = 2 * iATR(symbol,M15,14,0)/MarketInfo(symbol,MODE_POINT); 
    int stop = 4 * iATR(symbol,M15,14,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 function                                            |
//+------------------------------------------------------------------+  

string Trend(string symbol, int tf)
  {
  
    string Pair_Trend = "F"; // Flat
    
    double EMA_020 = iMA(symbol,tf,020,0,MODE_EMA,PRICE_CLOSE,0);
    //double EMA_040 = iMA(symbol,tf,040,0,MODE_EMA,PRICE_CLOSE,0);
    double EMA_200 = iMA(symbol,tf,200,0,MODE_EMA,PRICE_CLOSE,0);
    
    if((EMA_020 < EMA_200))
      {
        Pair_Trend = "D"; // Down
      }

    if((EMA_020 > EMA_200))
      {
        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);
}