//+------------------------------------------------------------------+
//|                                                   Simple S&R.mq4 |
//|                                        Copyright © 2008, LEGRUPO |
//|                                           http://www.legrupo.com |
//|                                                     Version: 1.3 |
//| History:                                                         |
//| 1.0 => Release version to the public                             |
//| 1.1 => fixes on this releases:                                   |
//|        1) StopLoss bug fixed;                                    |
//|        2) Now the EA close the open and pending orders on the    |
//|           end of the day, you just need to see when day ends     |
//|           on your broker. InterbankFX is GMT, so it is 0 on them |
//|        3) You don't need to manage the MagicNumber anymore       |
//| 1.2 => Added Money Management feature;                           |
//| 1.3 => Added HMA filter to stop big drawndown                    |
//| 1.4                                                              |
//| 1.5 => Tinashe B Chipomho (tinashechipomho@gmail.com)            |
//|        Refactored Indicator code, added first strategy (hedging) |
//|        Code is broken down into reusable units                   | 
//|        Fixed issues with 1.4                                     |
//|        Controls orders from previous day                         |
//|                                                                  |
//|                                                                  |
//|                                                                  |
//|                                                                  |
//|                                                                  |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, LEGRUPO"
#property link      "http://www.legrupo.com"

#include <WinUser32.mqh>
#include <stderror.mqh>
#include <stdlib.mqh>

#include <Logging.mqh>
#include <MoneyManagement.mqh>
#include <Trading.mqh>
#include <FiboPivotPoints.mqh>


//define various trading strategies
//these strategies are there to minimize the drawdown.
//various suggestions have been put forward by the team.
#define STRATEGY_NONE 0
#define STRATEGY_HEDGING 1
#define STRATEGY_MEGA_TREND 2

extern string _ = "Risk/Profit Management ";
extern double Trading.Risk = 10.0; // risk in percentage % (10% of risk in this case)
extern double Trading.LotSize = 0.1;
extern int Trading.Slippage = 3;
extern int Trading.StopLoss = 100;
extern int Trading.FlexPips = 8; //if the price reach within this range of the prescribed take profit
                         //we can close the position. 

extern string __ = "Money Management ";
extern bool MoneyManagement.Enabled = false;

extern string ___ = "Account Information ";
extern bool Account.Type.IsMicro = false;

extern string ____ = "Trailing Stop Management ";
extern int TrailingStopMode = 0;
extern bool TimeSpecific = false;

extern string _____ = "Strategy Types: Which strategy to do you want to use? 0 means none ";
extern int StrategyIndex = 1;

extern string ______ = "Simple Hedging Strategy: Adjust if your strategy index is 1";
extern double Strategy.Hedging.LotSize = 0;
extern double Strategy.Hedging.TrailingStop = 15;
extern double Strategy.Hedging.Slippage = 1;
extern double Strategy.Hedging.TrailingMode = 1;



/*
extern string _HMA = "        HMA Indicator Settings ";
string HMA_Name = "Mega trend";
extern int HMA_Period = 33;
extern int HMA_Mode = 3;
*/

int ExpertID=1844;
int MagicNumber = 1844; //magic number
string BASEFILENAME = "Fxi_Alpha_EA";
string BuyComment = "Simple R & S BUY";
string SellComment = "Simple R & S SELL";

bool StartOfDayProcessed = false;


//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init(){
  
  //create magic number
  MagicNumber = MakeMagicNumber();

  //set up logging filename and logging level.  
  SetLogFilename(BASEFILENAME+"."+Symbol()+".log");
  
  SetLogLevel(LOG_DEBUG); //we need to use the debug for now
                          //when its production ready we swicth to LOG_ERROR or LOG_FATAL
  LogDebug("Starting "+BASEFILENAME);
  return(0);
}

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

//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start(){
  
  //----

  double prices[];
  GetCurrentFiboPivotPoints(prices);

  double pivot = prices[FIBO_LEVEL_PIVOT];
  double r3 = prices[FIBO_LEVEL_R3];
  double r2 = prices[FIBO_LEVEL_R2];
  double r1 = prices[FIBO_LEVEL_R1];
  double s1 = prices[FIBO_LEVEL_S1];
  double s2 = prices[FIBO_LEVEL_S2];
  double s3 = prices[FIBO_LEVEL_S3];
  
  if (prices[0]==0.0){
  
  }  
  
  
  if (prices[0]==0.0){
    LogWarn("There was an error getting the Pivot prices.");
    return (-1);
  }  
  
  if(MoneyManagement.Enabled) {
    Trading.LotSize = DefineLotSize(Trading.Risk,Trading.LotSize,Account.Type.IsMicro); //Adjust the lot size     total
  }
   

  //Print("Pivot="+pivot+", S1="+s1+", S2="+s2+", S3="+s3+", R1="+r1+", R2="+r2+", R3="+r3);
 
  double takeprofit = 0;
  int ticket = -1; //default ticket number.
  double stoploss = Trading.StopLoss * Point; //default stop loss level.
   
  double servers_min_stop = MarketInfo(Symbol(), MODE_STOPLEVEL) * MarketInfo(Symbol(), MODE_POINT);
   
  datetime TimeToEndOfDay = StrToTime(TimeToStr(TimeCurrent(),TIME_DATE)+" 23:55");
  //the last 5mintues of the trading hour of the day are purely for 
  //cleaning up pending orders and getting ready for the new day.
  //any other time let do business.
  datetime StartOfDay1 = StrToTime(TimeToStr(TimeCurrent(),TIME_DATE)+" 00:01");
  datetime StartOfDay2 = StrToTime(TimeToStr(TimeCurrent(),TIME_DATE)+" 00:30");
   
  if (TimeCurrent()<=TimeToEndOfDay){
    
    //if we dont have a sell order at R3 place one.
    if ( HasSellOrder(r3,MagicNumber,Trading.Slippage)!= true) {
      //open a sell order at r3 
      takeprofit = s1+MathMin(Trading.FlexPips*Point, servers_min_stop);
      stoploss = r3+MathMax(Trading.StopLoss * Point,servers_min_stop);
      ticket = PlaceResistanceOrder(r3,pivot, Trading.LotSize, stoploss, takeprofit ,TimeToEndOfDay);
    }
   
     
    //if we dont have a sell order at R2 place one.
    if ( HasSellOrder(r2,MagicNumber,Trading.Slippage)!= true) {
      //open a sell order at r2 
      takeprofit = s1+MathMin(Trading.FlexPips*Point, servers_min_stop);
      stoploss = r3+MathMax(Trading.StopLoss * Point,servers_min_stop);
      ticket = PlaceResistanceOrder(r2,pivot, Trading.LotSize, stoploss, takeprofit ,TimeToEndOfDay);
    }

    //if we dont have a sell order at R1 place one.
    if ( HasSellOrder(r1,MagicNumber,Trading.Slippage)!= true) {
      //open a sell order at r1 
      takeprofit = s1+MathMin(Trading.FlexPips*Point, servers_min_stop);
      stoploss = r3+MathMax(Trading.StopLoss * Point,servers_min_stop);
      ticket = PlaceResistanceOrder(r1,pivot, Trading.LotSize, stoploss, takeprofit ,TimeToEndOfDay);
    }
     
    //if we dont have a sell order at S3 place one.
    if ( HasBuyOrder(s3,MagicNumber,Trading.Slippage)!= true) {
      //open a buy order at s3 
      takeprofit = r1-MathMax(Trading.FlexPips*Point, servers_min_stop);
      stoploss = s3-MathMax(Trading.StopLoss * Point,servers_min_stop);
      ticket = PlaceSupportOrder(s3,pivot, Trading.LotSize, stoploss, takeprofit ,TimeToEndOfDay);
    }
     
    //if we dont have a sell order at S2 place one.
    if ( HasBuyOrder(s2,MagicNumber,Trading.Slippage)!= true) {
      //open a buy order at s2 
      takeprofit = r1-MathMax(Trading.FlexPips*Point, servers_min_stop);
      stoploss = s2-MathMax(Trading.StopLoss * Point,servers_min_stop);
      ticket = PlaceSupportOrder(s2,pivot, Trading.LotSize, stoploss, takeprofit ,TimeToEndOfDay);
    }

    //if we dont have a sell order at S1 place one.
    if ( HasBuyOrder(s1,MagicNumber,Trading.Slippage)!= true) {
      //open a sell order at s1 
      takeprofit = r1-MathMax(Trading.FlexPips*Point, servers_min_stop);
      stoploss = s1-MathMax(Trading.StopLoss*Point, servers_min_stop);
      ticket = PlaceSupportOrder(s1,pivot, Trading.LotSize, stoploss, takeprofit ,TimeToEndOfDay);
    }
    
    
    //now correct the OrderTakeProfit for left over orders, even if it means making a loss.
    
    //implement various strategies.
    ImplementStrategy(StrategyIndex, prices);
     
  }
  
  //we want to execute previous orders only during the start of the
  //day or when we haven't already done so.
  if (((TimeCurrent()>StartOfDay1) && (TimeCurrent()<StartOfDay2))|| (!StartOfDayProcessed) ){
    StartOfDayProcessed = true;
    AdjustPreviousOrders(prices);  
  }
  
   
  //TrackOrders(TrailingStopMode, MagicNumber, Symbol());

  return(0);
}

/**
 * Now implement the Trading strategy defined by index.
 *
 */
void ImplementStrategy(int index, double prices[]){
  switch (index){
    case STRATEGY_HEDGING:
      SimpleHedging(prices);
      return;
    case STRATEGY_MEGA_TREND:
      MegaTrend(prices);
      return; 
    default:
      return;
  }
}

  
/**
 * Yesterday or Last week's orders are a pain in the neck.
 * we need to close them as quickly as possible. If at all we get chance
 * to close them at BE we can do it.
 **/
void AdjustPreviousOrders(double prices[]){
  int total = OrdersTotal()-1;
  int today = TimeDayOfWeek(Time[0]), orderDay=-1;
  double buytakeprofit=0,selltakeprofit=0;
  double servers_min_stop = MarketInfo(Symbol(), MODE_STOPLEVEL) * MarketInfo(Symbol(), MODE_POINT);
  
   //move the takeprofit to s1 even if it means a losing t/p
   selltakeprofit = MathMin(Ask,prices[FIBO_LEVEL_S1]);
   //move the takeprofit to s1 even if it means a losing t/p
   buytakeprofit = MathMax(Bid,prices[FIBO_LEVEL_R1]);
   
  for(;total>=0; total--){
    if (OrderSelect(total,SELECT_BY_POS,MODE_TRADES)!=true)
      continue;
    if (OrderSymbol()!= Symbol())  
      continue;
    if (OrderMagicNumber()!= MagicNumber)
      continue;
    if ((OrderType()!=OP_BUY) && (OrderType()!=OP_SELL))
      continue;
      
    //process only yesterday's or last week's orders.
    orderDay = TimeDayOfWeek(OrderOpenTime());    
    if ( (orderDay < today) ||
         ((today==1) && ((orderDay==5)||(orderDay==6)) )
        ){
        
      
      //we can close it at break even if its possible.
      //if (OrderProfit()==0.0){
      //  LogInfo("Closing yesterday/last week order at break even.");
      //  PartiallyClose(OrderType(),OrderTicket(),OrderLots(), OrderOpenPrice(), OrderTakeProfit(),Slippage);
      //}  
      
      if ((OrderType()==OP_BUY) && (OrderTakeProfit()!=buytakeprofit) ){
        //also make sure the takeprofit is valid.
        //if its not an invalid stop error can be generated.
        LogInfo("A buy order from yesterday or last week is present: OrderTicket="+OrderTicket()+", OrderProfit="+OrderProfit()+", OrderType="+OrderType2String(OrderType())+", OrderOpenPrice="+OrderOpenPrice());  
        LogInfo("Buy Order, new takeprofit calculated to be "+ buytakeprofit+" and the current price is "+ Bid);
        if (!ModifyOrder(OrderTicket(), OrderOpenPrice(),OrderStopLoss(),buytakeprofit, 0,CLR_NONE))  
          LogError("Failed to modify an old order. "+ ErrorDescription(GetLastError()));
      }
      else if ((OrderType()==OP_SELL) && (OrderTakeProfit()!=selltakeprofit)){
        //also make sure the takeprofit is valid.
        //if its not an invalid stop error can be generated.
        LogInfo("A sell order from yesterday or last week is present: OrderTicket="+OrderTicket()+", OrderProfit="+OrderProfit()+", OrderType="+OrderType2String(OrderType())+", OrderOpenPrice="+OrderOpenPrice());  
        LogInfo("Sell Order, new takeprofit calculated to be "+ selltakeprofit+" and the current price is "+ Ask);
        if (!ModifyOrder(OrderTicket(), OrderOpenPrice(),OrderStopLoss(),selltakeprofit, 0,CLR_NONE))  
          LogError("Failed to modify an old order. "+ ErrorDescription(GetLastError()));
        
      }
    }
    
  }
}


/**
 * In this function place only buy orders.
 */
int PlaceSupportOrder(double price, double pivot, double lots, double stoploss, double takeprofit, datetime expiration){
  LogDebug("");
  LogDebug("PlaceSupportOrder():Start");
  int ticket = -1;
  RefreshRates();
  double servers_min_stop = MarketInfo(Symbol(), MODE_STOPLEVEL) * MarketInfo(Symbol(), MODE_POINT);
  int cmd = OP_BUYLIMIT;
  //if the price is within + or - Slippage*Point simply buy.
  if (  (((Trading.Slippage*Point) + Ask) >= price) && 
        ((Ask-(Trading.Slippage*Point) ) <= price) ){

    cmd = OP_BUY;
    LogInfo("Support Order at "+price+" is placed at market price.");
    LogInfo("SupportOrder: "+ OrderType2String(cmd)+" Price["+Ask+"] Pivot["+pivot+"] StopLoss["+stoploss+"] TakeProfit["+takeprofit+"] Expiration["+TimeToStr(expiration,TIME_DATE|TIME_MINUTES)+"]");
    ticket = CreateOrder(cmd, lots, NormalizeDouble(Ask,Digits),
                         NormalizeDouble(stoploss,Digits), NormalizeDouble(takeprofit,Digits), 
                         BuyComment, MagicNumber, Trading.Slippage,expiration);
                              
  }
  else if (MathAbs(Ask-price) > servers_min_stop){
    //if the price is too close to the stoplevel there is 
    //nothing much we can do
    if (Ask < price)
      cmd = OP_BUYSTOP;
  
    
    LogInfo("SupportOrder: "+ OrderType2String(cmd)+" Price["+price+"] Pivot["+pivot+"] StopLoss["+stoploss+"] TakeProfit["+takeprofit+"] Expiration["+TimeToStr(expiration,TIME_DATE|TIME_MINUTES)+"]");
    ticket = CreateOrder(cmd, lots, NormalizeDouble(price,Digits),
                         NormalizeDouble(stoploss,Digits), NormalizeDouble(takeprofit,Digits), 
                         BuyComment, MagicNumber, Trading.Slippage,expiration);
  }
  LogDebug("PlaceSupportOrder():End");
  LogDebug("");
  return (ticket);
}

/**
 * In this function place only sell orders.
 */
int PlaceResistanceOrder(double price, double pivot, double lots, double stoploss, double takeprofit, datetime expiration){
  
  int ticket = -1;
  RefreshRates();
  double servers_min_stop = MarketInfo(Symbol(), MODE_STOPLEVEL) * MarketInfo(Symbol(), MODE_POINT);
  int cmd = OP_SELLLIMIT;
  if (  (((Trading.Slippage*Point) + Bid) >= price) && 
        ((Bid-(Trading.Slippage*Point) ) <= price) ){
    LogInfo("Opening a sell order at market price.");
    LogInfo("ResistanceOrder: "+ OrderType2String(cmd)+" Price["+price+"] Pivot["+pivot+"] StopLoss["+stoploss+"] TakeProfit["+takeprofit+"] Expiration["+TimeToStr(expiration,TIME_DATE|TIME_MINUTES)+"]");
    ticket = CreateOrder(cmd, lots, NormalizeDouble(Ask,Digits),
                         NormalizeDouble(stoploss,Digits), NormalizeDouble(takeprofit,Digits), 
                         SellComment, MagicNumber, Trading.Slippage,expiration);
  }
  else if (MathAbs(Bid-price) > servers_min_stop){
    //if the price is too close to the stoplevel there is 
    //nothing much we can do
    if (Bid > price)
      cmd = OP_SELLSTOP;
    
    LogInfo("ResistanceOrder: "+ OrderType2String(cmd)+" Price["+price+"] Pivot["+pivot+"] StopLoss["+stoploss+"] TakeProfit["+takeprofit+"] Expiration["+TimeToStr(expiration,TIME_DATE|TIME_MINUTES)+"]");
    ticket = CreateOrder(cmd, lots, NormalizeDouble(price,Digits),
                         NormalizeDouble(stoploss,Digits), NormalizeDouble(takeprofit,Digits), 
                         SellComment, MagicNumber, Trading.Slippage,expiration);
  }
  return (ticket);
}

//+------------------------------------------------------------------+
//| Make Magic Number                                                |
//+------------------------------------------------------------------+
int MakeMagicNumber(){
  int SymbolCode  = 0;
  int PeriodCode  = 0;
   
   //---- Symbol Code
    if( StringSubstr(Symbol(),0,6) == "AUDCAD") { SymbolCode = 1000; }
  else if( StringSubstr(Symbol(),0,6) == "AUDJPY") { SymbolCode = 2000; }
  else if( StringSubstr(Symbol(),0,6) == "AUDNZD") { SymbolCode = 3000; }
  else if( StringSubstr(Symbol(),0,6) == "AUDUSD") { SymbolCode = 4000; }
  else if( StringSubstr(Symbol(),0,6) == "CHFJPY") { SymbolCode = 5000; }
  else if( StringSubstr(Symbol(),0,6) == "EURAUD") { SymbolCode = 6000; }
  else if( StringSubstr(Symbol(),0,6) == "EURCAD") { SymbolCode = 7000; }
  else if( StringSubstr(Symbol(),0,6) == "EURCHF") { SymbolCode = 8000; }
  else if( StringSubstr(Symbol(),0,6) == "EURGBP") { SymbolCode = 9000; }
  else if( StringSubstr(Symbol(),0,6) == "EURJPY") { SymbolCode = 1000; }
  else if( StringSubstr(Symbol(),0,6) == "EURUSD") { SymbolCode = 1100; }
  else if( StringSubstr(Symbol(),0,6) == "GBPCHF") { SymbolCode = 1200; }
  else if( StringSubstr(Symbol(),0,6) == "GBPJPY") { SymbolCode = 1300; }
  else if( StringSubstr(Symbol(),0,6) == "GBPUSD") { SymbolCode = 1400; }
  else if( StringSubstr(Symbol(),0,6) == "NZDJPY") { SymbolCode = 1500; }
  else if( StringSubstr(Symbol(),0,6) == "NZDUSD") { SymbolCode = 1600; }
  else if( StringSubstr(Symbol(),0,6) == "USDCAD") { SymbolCode = 1700; }
  else if( StringSubstr(Symbol(),0,6) == "USDCHF") { SymbolCode = 1800; }
  else if( StringSubstr(Symbol(),0,6) == "USDJPY") { SymbolCode = 1900; }
                     
   
   //---- Period Code
  if( TimeSpecific ){
    if( Period() == PERIOD_M1 )      { PeriodCode = 10; }
    else if( Period() == PERIOD_M5 ) { PeriodCode = 20; }
    else if( Period() == PERIOD_M15 ){ PeriodCode = 30; }
    else if( Period() == PERIOD_M30 ){ PeriodCode = 40; }
    else if( Period() == PERIOD_H1 ) { PeriodCode = 50; }
    else if( Period() == PERIOD_H4 ) { PeriodCode = 60; }
    else if( Period() == PERIOD_D1 ) { PeriodCode = 70; }
    else if( Period() == PERIOD_W1)  { PeriodCode = 80; }
    else if( Period() == PERIOD_MN1 ){ PeriodCode = 90; }
  }
  else
    PeriodCode = 0;

  return(ExpertID+SymbolCode+PeriodCode);
}

double CountOpenLots(int type){
  int total = OrdersTotal()-1;
  double lots = 0.0;
  for(;total>=0;total--){
    if (OrderSelect(total,SELECT_BY_POS,MODE_TRADES)!=true)
      continue;
    if (OrderSymbol()!= Symbol())  
      continue;
    if (OrderMagicNumber()!= MagicNumber)
      continue;
    if (OrderType()!=type)
      continue;
    lots = lots + OrderLots(); 
  }
  return (lots);
}

/** ============================= STRATEGIES ================================= **/

//Simple hedging strategy.
void SimpleHedging(double prices[]){
  //if the prices breaks the R3 and S3 barrier we need to be ready and place some buy order sell stop orders.
  //with a very tight stop loss
  
  int mn = MagicNumber+STRATEGY_HEDGING;
  
  double r3 = prices[FIBO_LEVEL_R3];
  double s3 = prices[FIBO_LEVEL_S3];
  
  string HedgeBuyComment ="(Hedge) "+BuyComment;
  string HedgeSellComment ="(Hedge) "+SellComment;
  
  int ticket = -1;
  
  double buylots = Strategy.Hedging.LotSize;
  double selllots = Strategy.Hedging.LotSize;

  if (buylots <=0.0){
    //hedge all open buy positions.
    buylots = CountOpenLots(OP_SELL);
  }
  
  if (selllots <=0.0){
    //hedge all open buy positions.
    selllots = CountOpenLots(OP_BUY);
  }

  
  //if the lot size is zero or less, the user wants to cover all the open sell or buy positions
  if ( (Ask >= r3 ) &&
       (Ask <= (r3+(Strategy.Hedging.Slippage*Point)) ) 
     ){
     
    
    //we should place a buy order at this Resistance level 3 
    //if one is not already there.
    if ((!HasBuyOrder(r3,mn,Strategy.Hedging.Slippage)) && (!HasClosedBuyOrder(r3,mn,Strategy.Hedging.Slippage)) ){
      LogDebug("Price reached near the R3 level, R3="+r3+", Price="+Ask); 
      LogInfo("Opening a buy order at R3, Lots="+buylots+", Price="+Ask+", StopLoss="+ prices[FIBO_LEVEL_R2]+""); 
      ticket = CreateOrder(OP_BUY,buylots,Ask,
                           prices[FIBO_LEVEL_R2], 0,
                           HedgeBuyComment,mn,Strategy.Hedging.Slippage,0);
      if (ticket<=0)
        LogError("Failed to open buy order (Hedge). "+ ErrorDescription(GetLastError()));                      
    }
  }

  if ( (Bid >= s3 ) &&
       (Bid <= (s3+(Strategy.Hedging.Slippage*Point)) ) 
     ){
     
    
    //we should place a buy order at this Resistance level 3 
    //if one is not already there.
    if (!HasSellOrder(s3,mn,Strategy.Hedging.Slippage) && (!HasClosedSellOrder(s3,mn,Strategy.Hedging.Slippage)) ){
      LogDebug("Price reached near the S3 level, S3="+s3+", Price="+Bid); 
      LogInfo("Opening a buy order at S3, Lots="+selllots+", Price="+Bid+", StopLoss="+ prices[FIBO_LEVEL_S2]+""); 
      ticket = CreateOrder(OP_SELL,selllots,Bid,
                           prices[FIBO_LEVEL_S2], 0,
                           HedgeSellComment,mn,Strategy.Hedging.Slippage,0);
      if (ticket<=0)
        LogError("Failed to open sell order (Hedge). " + ErrorDescription(GetLastError()));                      
    }
  }
  
  double params [];
  ArrayResize(params,1);
  params[0]=Strategy.Hedging.TrailingStop;
  TrackOrders(Strategy.Hedging.TrailingMode, mn, Symbol(),params);
  
}

//___________________________________________________________________________________________//

//use mega trend indicator values
void MegaTrend(double prices[]){
  LogWarn("Mega trend Strategy is not yet implemented.");
}


