//+------------------------------------------------------------------+
//|                                       Super_Carry_Trade_ver2.mq4 |
//|                                                         Zen_Leow |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Zen_Leow"
#property link      ""
#include <stdlib.mqh>
#include <stderror.mqh>

extern int       EA_MAGIC_NUM = 511112;
extern int       StartHour = 19;
extern int       LadderHeight = 400;
extern int       CarryBuffer = 200;

extern double    Slippage = 3.0;
extern int       TakeProfit = 10;
extern int       CustomSpread = 9;
extern int       StopLoss = 0;

extern bool      MoneyManagement = true;
extern double    RiskPercent = 0.5;
extern double    Lots = 0.1;
extern double    MaxLots = 15.0;
extern double    MinLots = 0.01;

string           msg = "";
double           CurrentAveragePrice = 0;
bool             AveragingStarted = false;


//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
{

      if(
      (Symbol()=="EURJPY") || (Symbol()=="EURJPYm") ||
      (Symbol()=="USDJPY")  || (Symbol()=="USDJPYm") ||
      (Symbol()=="CHFJPY")  || (Symbol()=="CHFJPYm") ||
      (Symbol()=="AUDJPY")  || (Symbol()=="AUDJPYm") ||     
      (Symbol()=="CADJPY")  || (Symbol()=="CADJPYm") ||
      (Symbol()=="NZDJPY")  || (Symbol()=="NZDJPYm") ||
      (Symbol()=="GBPJPY")  || (Symbol()=="GBPJPYm") ||
      (Symbol()=="SGDJPY")  || (Symbol()=="SGDJPYm")
      )

//----
   GetCurrentAveragePrice();
   WriteComment();
   CheckForAveraging();
//----
   return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }

void CheckForAveraging()
{
   int total = OrdersTotal();
   if (total > 0)
   {
      for(int cnt=0;cnt<total;cnt++)
      {
         if(OrderSelect(cnt,SELECT_BY_POS))
         {
            if(OrderSymbol()==Symbol() && OrderMagicNumber() == EA_MAGIC_NUM && (OrderComment()=="SuperCarry_Buy_START "+EA_MAGIC_NUM+" Averager"))
            {
               AveragingStarted = true;
            }
         }
      }
   }
   else
   {
      AveragingStarted = false;
   }
}

void WriteComment()
{
   int CurrentHour = TimeHour(TimeCurrent());
   msg = "";
   msg = msg + "Current Average Position Price: "+CurrentAveragePrice;
   if (CurrentHour == StartHour)
   {
      msg = msg + "\nEA at Work!";
   }
   else
   {
      msg = msg + "\nEA sleeping...";
   }
   if (AveragingStarted)
   {
      msg = msg + "\nAveraging Started";
   }
   else
   {
      msg = msg + "\nNo Averaging... yet.";
   }
   
   Comment(msg);
}
  
double GetPositionSize()
{
   double PositionSize = 0;
   
   if (MoneyManagement)
   {
      PositionSize = ((AccountEquity()/100) * RiskPercent) / (MarketInfo(Symbol(),MODE_LOTSIZE) / AccountLeverage());
   }
   else
   {
      PositionSize = Lots;
   }
   PositionSize = NormalizeDouble(PositionSize,2);
   if (PositionSize < MinLots)
   {
      PositionSize = MinLots;
   }
   if (PositionSize > MaxLots)
   {
      PositionSize = MaxLots;
   }
   return (PositionSize);
}
  
double GetCurrentAveragePrice()
{
   double PositionSizeTotal = 0;
   double AveragePrice = 0;
   int total = OrdersTotal();
   if (total > 0)
   {
      for(int cnt=0;cnt<total;cnt++)
      {
         if(OrderSelect(cnt,SELECT_BY_POS))
         {
            if(OrderSymbol()==Symbol() && OrderMagicNumber() == EA_MAGIC_NUM && (OrderType()==OP_BUY))
            {
               PositionSizeTotal = PositionSizeTotal + OrderLots();
               AveragePrice = AveragePrice + (OrderOpenPrice() * OrderLots());
            }
         }
      }
      
      if (PositionSizeTotal>0)
       {
         AveragePrice = AveragePrice / PositionSizeTotal;
         NormalizeDouble(AveragePrice, Digits);
         CurrentAveragePrice = AveragePrice;
       }

   }
   CurrentAveragePrice = AveragePrice;
   return (AveragePrice);
}

double GetLowestOfLastLadder(double CurrentPrice)
{
   double LowestPrice = 999999;
   int total = OrdersTotal();
   if (total > 0)
   {
      for(int cnt=0;cnt<total;cnt++)
      {
         if(OrderSelect(cnt,SELECT_BY_POS))
         {
            if(OrderSymbol()==Symbol() && OrderMagicNumber() == EA_MAGIC_NUM && (OrderType()==OP_BUY))
            {
               if (OrderOpenPrice() < LowestPrice)
               {
                  LowestPrice = OrderOpenPrice();
               }
            }
         }
      }
      NormalizeDouble(LowestPrice, Digits);
   }
   if (LowestPrice != 999999)
   {
      return (LowestPrice);
   }
   else
   {
      return (CurrentPrice + (LadderHeight * Point));
   }
}

void PlaceLadderTrades(double StartPrice, double EndPrice)
{
   //double EndPrice = StartPrice + (LadderHeight * Point);
   StartPrice = StartPrice + (TakeProfit * Point) + (CustomSpread * Point);
   double currentTP = StartPrice + (TakeProfit * Point);
   NormalizeDouble(EndPrice,Digits);
   NormalizeDouble(currentTP,Digits);
   NormalizeDouble(StartPrice,Digits);
   while (currentTP < EndPrice)
   {
      SendOrders (OP_BUYSTOP, GetPositionSize(), StartPrice, Slippage, 0, currentTP, "SuperCarry_Buy", 0);
      // if this order doesn't open due to whatever factors, forget it and move on.
      StartPrice = currentTP + (CustomSpread * Point);
      currentTP = StartPrice + (TakeProfit * Point);
      NormalizeDouble(currentTP,Digits);
      NormalizeDouble(StartPrice,Digits);  
   }
}


bool DecideToOpenTrade(int tradeType, int tradeType2)
{
   int total = OrdersTotal();
   if (total > 0)
   {
      for(int cnt=0;cnt<total;cnt++)
      {
         if(OrderSelect(cnt,SELECT_BY_POS))
         {
            if(OrderSymbol()==Symbol() && OrderMagicNumber() == EA_MAGIC_NUM && ((OrderType()==tradeType) || (OrderType()==tradeType2)))
            {
               if (Time[0] <= OrderOpenTime()) // don't open a new position if we're still on the same candle
               {
                  return (false);
               }
            }
         }
      }
   }
   // in case trades has already opened and closed within the candle
   int histotal = OrdersHistoryTotal();
   if (histotal > 0)
   {
      for(cnt=0;cnt<histotal;cnt++)
      {
         if(OrderSelect(cnt,SELECT_BY_POS,MODE_HISTORY))
         {
            if(OrderSymbol()==Symbol() && OrderMagicNumber() == EA_MAGIC_NUM && ((OrderType()==tradeType) || (OrderType()==tradeType2)))
            {
               if (Time[0] <= OrderOpenTime()) // don't open a new position if we're still on the same candle
               {
                  return (false);
               }
            }
         }
      }
   }
   return (true);
}

bool SendOrders (int PositionType, double LotSize, double PriceToOpen, double Slippage, double SL_Price, double TP_Price, string comments, datetime ExpirationTime)
{
   int ticket, errorType;
   
   if (PositionType == OP_BUY)
   {  
      Print("Bid: "+Bid+" Ask: "+Ask+" | Opening Buy Order: "+Symbol()+", "+PositionType+", "+LotSize+", "+PriceToOpen+", "+Slippage+", "+SL_Price+", "+TP_Price+", "+comments+", "+EA_MAGIC_NUM+", "+ExpirationTime+", Green");
      ticket=OrderSend(Symbol(),PositionType,LotSize,PriceToOpen,Slippage,SL_Price,TP_Price,comments,EA_MAGIC_NUM,ExpirationTime,Green);
      if(ticket>0)
      {
         if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) 
         {
            
            Print("BUY order opened : ",OrderOpenPrice());
            msg = ticket + ": Buy position opened on "+Symbol()+" at "+ Day()+"/"+Month()+"/"+Year()+" - "+Hour()+":"+Minute()+":"+Seconds();
            WriteToLogFile(msg);
            return (true);
         }
      }
      else 
      {  
         errorType = GetLastError();
         Print("Error opening BUY order : ", ErrorDescription(errorType));
         msg = "CANNOT open BUY position on "+Symbol()+" at "+ Day()+"/"+Month()+"/"+Year()+" - "+Hour()+":"+Minute()+":"+Seconds();
         WriteToLogFile(msg);
         return (false);
      }
   }
   if (PositionType == OP_BUYSTOP)
   {  
      Print("Bid: "+Bid+" Ask: "+Ask+" | Opening Buy Stop Order: "+Symbol()+", "+PositionType+", "+LotSize+", "+PriceToOpen+", "+Slippage+", "+SL_Price+", "+TP_Price+", "+comments+", "+EA_MAGIC_NUM+", "+ExpirationTime+", Green");
      ticket=OrderSend(Symbol(),PositionType,LotSize,PriceToOpen,Slippage,SL_Price,TP_Price,comments,EA_MAGIC_NUM,ExpirationTime,Green);
      if(ticket>0)
      {
         if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) 
         {
            
            Print("BUY order opened : ",OrderOpenPrice());
            msg = ticket + ": Buy position opened on "+Symbol()+" at "+ Day()+"/"+Month()+"/"+Year()+" - "+Hour()+":"+Minute()+":"+Seconds();
            WriteToLogFile(msg);
            return (true);
         }
      }
      else 
      {  
         errorType = GetLastError();
         Print("Error opening BUY order : ", ErrorDescription(errorType));
         msg = "CANNOT open BUY position on "+Symbol()+" at "+ Day()+"/"+Month()+"/"+Year()+" - "+Hour()+":"+Minute()+":"+Seconds();
         WriteToLogFile(msg);
         return (false);
      }
   }
   return (false);
}

void CloseAllOrders()
{
   int total = OrdersTotal()-1;
   int positionType;
   if (total > 0)
   {
      for(int cnt=total;cnt>=0;cnt--)
      {
         if(OrderSelect(cnt,SELECT_BY_POS))
         {
            if (OrderSymbol()==Symbol() && OrderMagicNumber() == EA_MAGIC_NUM && OrderType()==OP_BUY)
            {
               if (!OrderClose(OrderTicket(),OrderLots(),MarketInfo(OrderSymbol(),MODE_BID),3))
               {
                  OrderError();
               }
            }
            
            if (OrderSymbol()==Symbol() && OrderMagicNumber() == EA_MAGIC_NUM && OrderType()==OP_BUYSTOP) // pending orders
            {
               if (!OrderDelete(OrderTicket()))
               {
                  OrderError();
               }
            }
         }
      }
   }
}

void OrderError() {
  int iError=GetLastError();
  Print("Order:",OrderTicket()," GetLastError()=",iError," ",ErrorDescription(iError));
}

void WriteToLogFile(string input)
{
   string filename = "SuperCarryTrade-"+Symbol()+"-"+Day()+"-"+Month()+"-"+Year()+".log";
   int handle = FileOpen(filename,FILE_READ|FILE_WRITE);
   if (handle>1)
   {
      FileSeek(handle, 0, SEEK_END); // go to end of file
      FileWrite(handle, input);
      FileClose(handle);
   }
}
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
//----
   GetCurrentAveragePrice();
   CheckForAveraging();
   WriteComment();
   int CurrentHour = TimeHour(TimeCurrent());
   if (CurrentHour == StartHour)
   {
      if(DecideToOpenTrade(OP_BUY,OP_BUYSTOP))
      {
         double PriceToOpen = 0;
         double LadderEndPrice = 0;
         if(CurrentAveragePrice != 0 && Bid > CurrentAveragePrice && AveragingStarted) // Finally you can clear your orders!!!
         {
            CloseAllOrders();
         }
         GetCurrentAveragePrice();
         if(CurrentAveragePrice == 0) // Start a brand new ladder
         {
            PriceToOpen = Ask;
            if (SendOrders(OP_BUY, GetPositionSize(), PriceToOpen, Slippage, 0, PriceToOpen + (TakeProfit*Point), "SuperCarry_Buy_START "+EA_MAGIC_NUM, 0))
            {
               LadderEndPrice = PriceToOpen + (LadderHeight * Point);
               PlaceLadderTrades(PriceToOpen, LadderEndPrice);
            }
         }
         if(CurrentAveragePrice != 0 && Bid < (CurrentAveragePrice - (CarryBuffer * Point))) // Start the averaging ladder
         {
            PriceToOpen = Ask;
            if (SendOrders(OP_BUY, GetPositionSize(), PriceToOpen, Slippage, 0, 0, "SuperCarry_Buy_START "+EA_MAGIC_NUM+" Averager", 0))
            {
               LadderEndPrice = GetLowestOfLastLadder(PriceToOpen);
               PlaceLadderTrades(PriceToOpen, LadderEndPrice);
            }
         }
         
      }
   }
//----
   return(0);
}
//+------------------------------------------------------------------+