//+------------------------------------------------------------------+
//|                                               OverHedgeTrend.mq4 |
//|                                               Copyright © 2006,  |
//|                                                                  |
//| Written by MrPip (Robert Hill) for kmrunner                      |
//| 3/7/06  Added check of direction for first trade Buy or Sell     |
//| 3/21/07 Added modification of Lots calculation to use number     |
//|         of open trades + 1 instead of using power of 2           |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005"
#property link      "http://www.strategtbuilderfx.com"
#include <stdlib.mqh>


extern string  Expert_Name = "OverHedgeTrend";
extern double  Lots = 0.1;
extern double  MaxLots = 13;
extern bool    useDoubleLots = false;
extern double  HedgeBase = 2.0; 
extern int     Slippage = 5;
extern bool    shutdownGrid = false;
extern int     TunnelWidth=20;

extern double  profitTarget = 100;     // if > 0, will close out all positions once the pip target has been met
extern int     LSMAPeriod = 21;
extern int     LSMA_TimeFrame = 15;
extern int     PauseSeconds  =     6; // Number of seconds to "sleep" before closing the next winning trade
extern int     MillisPerSec  =  1000; // DO NOT CHANGE - Standard multiplier to convert seconds to milliseconds

double TunnelSize;
string setup;
int     MagicNumber;
double startBuyRate, startSellRate;
int currentOpen;
int NumBuyTrades, NumSellTrades;   // Number of buy and sell trades in this symbol
bool myWantLongs;
double lotMM;
double Profit;
bool OK2Buy,OK2Sell, FirstDirSell;


//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//---- 
	MagicNumber = 3000 + func_Symbol2Val(Symbol())*100 + func_TimeFrame_Const2Val(Period()); 
   setup = Expert_Name + "-" + Symbol() + "_" + func_TimeFrame_Val2String(func_TimeFrame_Const2Val(Period()));
   return(0);
  }

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

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
  
//---- 


//---- test if we want to shutdown

   if (shutdownGrid )         // close all orders. then exit.. there is nothing more to do
   {
      CloseOpenPositions();
      return(0);
   }
   
   TunnelSize = GetTunnelSize();
   
// Check for new trade cycle
// First check if profit target is reached
// If yes then there will be no open positions
// So a new trade cycle will begin
    
   CheckProfit();
   currentOpen = CheckOpenPositions("BOTH");
   if (currentOpen == 0)
   {
// Start trades based on confirmation direction, was daily

      if (CheckDirection() == 0) return(0);
      if (CheckDirection() == 1)
      {
         OK2Buy = true;
         OK2Sell = false;
         FirstDirSell = false;
      }
      if (CheckDirection() == -1)
      {
         OK2Buy = false;
         OK2Sell = true;
         FirstDirSell = true;
      }
      if (OK2Buy)
      {
         startBuyRate = MarketInfo(Symbol(), MODE_ASK) ;
         startSellRate = MarketInfo(Symbol(), MODE_BID) - TunnelSize*Point;
      }
      if (OK2Sell)
      {
         startSellRate = MarketInfo(Symbol(), MODE_BID) ;
         startBuyRate = MarketInfo(Symbol(), MODE_BID) + TunnelSize*Point;
      }
      
   }
   else
   {
     OK2Buy = true;
     OK2Sell = true;
   }
   
// Determine how many lots for next trade based on current number of open positions

   if (useDoubleLots)
      lotMM = Lots * MathPow(HedgeBase,currentOpen);
   else
      lotMM = Lots * (currentOpen+1);
   
   lotMM = MathFloor(lotMM*10)/10;
   if (lotMM == 0) return (0);
   
// if MaxLots is reached do not allow more trades

   if (lotMM > MaxLots) return(0);
   
// Determine if next trade should be long or short based on current number of open positions

  myWantLongs = true;
  if (MathMod(currentOpen,2) > 0.1) myWantLongs = false;
   
   if (FirstDirSell) myWantLongs = !myWantLongs;   // added to match starting trade direction
	if (myWantLongs)
	{
	   if (MarketInfo(Symbol(), MODE_ASK) >= startBuyRate )
	   {

        OpenBuyOrder();
	   }
	}
	else
	{
	   if (MarketInfo(Symbol(), MODE_BID) <= startSellRate)
	   {
        OpenSellOrder();
	   }
	}
	
   return(0);
}

double GetTunnelSize()
{
  double val;
  double myBid, myAsk;
  int digits;
  
  digits = MarketInfo(Symbol(), MODE_DIGITS) ;
   
  myBid = MarketInfo(Symbol( ), MODE_BID);
  if (digits > 0)  myBid = NormalizeDouble( myBid, digits);
  myAsk = MarketInfo(Symbol( ), MODE_ASK);
  if (digits > 0)  myAsk = NormalizeDouble( myAsk, digits);
  
  
   val = ((2*(myAsk-myBid)/Point)+TunnelWidth);
   return(val);
}

//+------------------------------------------------------------------------+
//| LSMAConfirmation is use by CheckDirection to allow trades only in      |
//| the direction of the confirmation time frame, was Daily                |
//| LSMA - Least Squares Moving Average function calculation               |
//| LSMA_In_Color Indicator plots the end of the linear regression line    |
//+------------------------------------------------------------------------+

double LSMAConfirmation(int Rperiod, int shift)
{
   int i;
   double sum;
   int length;
   double lengthvar;
   double tmp;
   double wt;

   length = Rperiod;
 
   sum = 0;
   for(i = length; i >= 1  ; i--)
   {
     lengthvar = length + 1;
     lengthvar /= 3;
     tmp = 0;
     tmp = ( i - lengthvar)*iClose(NULL, LSMA_TimeFrame, length-i+shift);
     sum+=tmp;
    }
    wt = sum*6/(length*(length+1));
    wt = NormalizeDouble(wt,MarketInfo(Symbol(),MODE_DIGITS));
    
    return(wt);
}

//+------------------------------------------------------------------+
//| CheckDirection                                                   |
//| Check direction for trade                                        |
//| Now uses Confirmation, was Daily                                 |
//| return 1 for up, -1 for down, 0 for flat                         |
//+------------------------------------------------------------------+
int CheckDirection()
{
   double SlowEMA, FastEMA;
   double Dif;
   
 
   Dif = LSMAConfirmation(LSMAPeriod,1) - LSMAConfirmation(LSMAPeriod,2);
   
   if(Dif > 0 ) return(1);
   if(Dif < 0 ) return(-1);
   return(0);
   

}

//+------------------------------------------------------------------+
//| Close Open Positions                                             |
//| Close all open positions                                         |
//| Losing trades are closed first so that                           |
//| another trade is not triggered.                                  |
//+------------------------------------------------------------------+
void CloseOpenPositions()
{
   int cnt, err, digits;
   double myPrice;
   
   digits = MarketInfo(Symbol(), MODE_DIGITS) ;
   
// First close losing trades
   
   for(cnt=OrdersTotal()-1;cnt>=0;cnt--)
   {
      OrderSelect (cnt, SELECT_BY_POS,MODE_TRADES);
      if ( OrderSymbol() != Symbol()) continue;
      if ( OrderMagicNumber() != MagicNumber)  continue;
      
        if(OrderType() == OP_BUY && OrderProfit() < 0)
        {
          Sleep(PauseSeconds*MillisPerSec);
          myPrice = MarketInfo(Symbol( ), MODE_BID);
          if (digits > 0)  myPrice = NormalizeDouble( myPrice, digits);
          if (OrderClose(OrderTicket(),OrderLots(),myPrice,Slippage,Violet))
          {
            Print("Buy Order closed for " + Symbol() + " at " + myPrice + ".");
          }
          else
          {
            err=GetLastError();
            Print("Error closing order : (", err , ") " + ErrorDescription(err));
          }
        }
        if(OrderType() == OP_SELL && OrderProfit() < 0)
        {
          Sleep(PauseSeconds*MillisPerSec);
          myPrice = MarketInfo(Symbol( ), MODE_ASK);
          if (digits > 0)  myPrice = NormalizeDouble( myPrice, digits);
          if (OrderClose(OrderTicket(),OrderLots(),Ask,Slippage,Violet))
          {
            Print("Sell Order closed for " + Symbol() + " at " + Ask + ".");
          }
          else
          {
            err=GetLastError();
            Print("Error closing order : (", err , ") " + ErrorDescription(err));
          }
        }
   }
   
// Then close winning trades
   
   for(cnt=OrdersTotal()-1;cnt>=0;cnt--)
   {
      OrderSelect (cnt, SELECT_BY_POS,MODE_TRADES);
      if ( OrderSymbol() != Symbol()) continue;
      if ( OrderMagicNumber() != MagicNumber)  continue;
      
        if(OrderType() == OP_BUY && OrderProfit() > 0)
        {
          Sleep(PauseSeconds*MillisPerSec);
          myPrice = MarketInfo(Symbol( ), MODE_BID);
          if (digits > 0)  myPrice = NormalizeDouble( myPrice, digits);
          if (OrderClose(OrderTicket(),OrderLots(),myPrice,Slippage,Gold))
          {
            Print("Buy Order closed for " + Symbol() + " at " + myPrice + ".");
          }
          else
          {
            err=GetLastError();
            Print("Error closing order : (", err , ") " + ErrorDescription(err));
          }
        }
        if(OrderType() == OP_SELL && OrderProfit() > 0)
        {
          Sleep(PauseSeconds*MillisPerSec);
          myPrice = MarketInfo(Symbol( ), MODE_ASK);
          if (digits > 0)  myPrice = NormalizeDouble( myPrice, digits);
          if (OrderClose(OrderTicket(),OrderLots(),myPrice,Slippage,Gold))
          {
            Print("Sell Order closed for " + Symbol() + " at " + myPrice + ".");
          }
          else
          {
            err=GetLastError();
            Print(" Error closing order : (", err , ") " + ErrorDescription(err));
          }
        }
   }

}

//+------------------------------------------------------------------------+
//| counts the number of open positions                                    |
//| type BUY returns number of long positions                              |
//| type SELL returns number of short positions                            |
//| type BOTH returns number of long and short positions                   |
//+------------------------------------------------------------------------+

int CheckOpenPositions(string type)
{
   int cnt, NumPositions;
   int NumBuyTrades, NumSellTrades;   // Number of buy and sell trades in this symbol
   
   NumBuyTrades = 0;
   NumSellTrades = 0;
   for(cnt=OrdersTotal()-1;cnt>=0;cnt--)
   {
      OrderSelect (cnt, SELECT_BY_POS, MODE_TRADES);
      if ( OrderSymbol() != Symbol()) continue;
      if ( OrderMagicNumber() != MagicNumber)  continue;
      if(OrderType() == OP_BUY )
      {
        NumBuyTrades++;
      }
      else if(OrderType() == OP_SELL)
      {
        NumSellTrades++;
      }
             
   }
   NumPositions = NumBuyTrades + NumSellTrades;
   if (type == "BUY") return (NumBuyTrades);
   if (type == "SELL") return (NumSellTrades);
   return (NumPositions);
}

//+------------------------------------------------------------------+
//| GetProfit                                                        |
//| Return Open Profit from all open positions                       |
//+------------------------------------------------------------------+
double GetProfit( )
{
   int i;
   double   openProfit = 0;

   openProfit = 0;

   for(i=OrdersTotal()-1;i>=0;i--)
   {
      OrderSelect(i, SELECT_BY_POS );
      if ( OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber )
      {
          openProfit = openProfit + MathRound(OrderProfit()/MarketInfo(Symbol(),MODE_TICKVALUE)*10);
      }
   }
   return (openProfit);
}

//+------------------------------------------------------------------+
//| OpenBuyOrder                                                     |
//+------------------------------------------------------------------+
void OpenBuyOrder()
{
   int err,ticket, digits;
   double myPrice;

   myPrice = MarketInfo(Symbol(), MODE_ASK);
   digits = MarketInfo(Symbol( ), MODE_DIGITS) ;
   if (digits > 0) 
   {
     myPrice = NormalizeDouble( myPrice, digits);
   }
   ticket = OrderSend(Symbol(),OP_BUY,lotMM,myPrice,Slippage,0,0,setup,MagicNumber,0,Blue);
   if(ticket<=0)
   {
      err = GetLastError();
      Print("Error opening BUY order [" + setup + "]: (" + err + ") " + ErrorDescription(err)); 
   }
}

//+------------------------------------------------------------------+
//| OpenSellOrder                                                    |
//+------------------------------------------------------------------+
void OpenSellOrder()
{
   int err,ticket, digits;
   double myPrice;

   myPrice = MarketInfo(Symbol(), MODE_BID);
   digits = MarketInfo(Symbol( ), MODE_DIGITS) ;
   
   ticket = OrderSend(Symbol(),OP_SELL,lotMM,myPrice,Slippage,0,0,setup,MagicNumber,0,Red);
   if(ticket<=0)
   {
      err = GetLastError();
      Print("Error opening Sell order [" + setup + "]: (" + err + ") " + ErrorDescription(err)); 
   }
}

//+------------------------------------------------------------------+
//| Check  Profit                                                    |
//| Output comment showing progit, longs, shorts, etc                |
//| If profit target is reached close all open positions             |
//+------------------------------------------------------------------+
void CheckProfit()
{
   double Profit;
   int openLongs,openShorts;
   
   Profit = GetProfit();
   if ( Profit >= profitTarget ) 
   {
       CloseOpenPositions();
   }
}

//+------------------------------------------------------------------+
//| Time frame interval appropriation  function                      |
//+------------------------------------------------------------------+

int func_TimeFrame_Const2Val(int Constant ) {
   switch(Constant) {
      case 1:  // M1
         return(1);
      case 5:  // M5
         return(2);
      case 15:
         return(3);
      case 30:
         return(4);
      case 60:
         return(5);
      case 240:
         return(6);
      case 1440:
         return(7);
      case 10080:
         return(8);
      case 43200:
         return(9);
   }
}

//+------------------------------------------------------------------+
//| Time frame string appropriation  function                               |
//+------------------------------------------------------------------+

string func_TimeFrame_Val2String(int Value ) {
   switch(Value) {
      case 1:  // M1
         return("PERIOD_M1");
      case 2:  // M1
         return("PERIOD_M5");
      case 3:
         return("PERIOD_M15");
      case 4:
         return("PERIOD_M30");
      case 5:
         return("PERIOD_H1");
      case 6:
         return("PERIOD_H4");
      case 7:
         return("PERIOD_D1");
      case 8:
         return("PERIOD_W1");
      case 9:
         return("PERIOD_MN1");
   	default: 
   		return("undefined " + Value);
   }
}

int func_Symbol2Val(string symbol)
 {
   string mySymbol = StringSubstr(symbol,0,6);
	if(mySymbol=="AUDCAD") return(1);
	if(mySymbol=="AUDJPY") return(2);
	if(mySymbol=="AUDNZD") return(3);
	if(mySymbol=="AUDUSD") return(4);
	if(mySymbol=="CHFJPY") return(5);
	if(mySymbol=="EURAUD") return(6);
	if(mySymbol=="EURCAD") return(7);
	if(mySymbol=="EURCHF") return(8);
	if(mySymbol=="EURGBP") return(9);
	if(mySymbol=="EURJPY") return(10);
	if(mySymbol=="EURUSD") return(11);
	if(mySymbol=="GBPCHF") return(12);
	if(mySymbol=="GBPJPY") return(13);
	if(mySymbol=="GBPUSD") return(14);
	if(mySymbol=="NZDUSD") return(15);
	if(mySymbol=="USDCAD") return(16);
	if(mySymbol=="USDCHF") return(17);
	if(mySymbol=="USDJPY") return(18);
   Comment("unexpected Symbol");
	return(19);
}

