//+------------------------------------------------------------------+
//|                                               OpenBuySell EA.mq4 |
//|                             Copyright © 2014, fxdaytrader et. al |
//|                             http://ForexFactory.com/fxdaytrader_ |
//|                                            http://ForexBaron.net |
//+------------------------------------------------------------------+
//////////////////////////////////////////////////////////////////////
// !!! YOU MUST COMPILE THIS EA WITH THE BUILD 509 COMPILER, download the metaeditor for build 509 at http://www.forexfactory.com/showthread.php?t=470340 !!!
//parts of the code based/taken from code by others (e.g. Steve Hopwood, Baluda, WHRoeder)
//////////////////////////////////////////////////////////////////////
#property copyright "Copyright © 2014, fxdaytrader et. al, http://ForexBaron.net"
#property link      "http://ForexBaron.net"
#define EANAME      "AlongHuman EA v1.00"

extern bool SendLongTrades  = TRUE;
extern bool SendShortTrades = TRUE;

extern string moohi="0:ignore";
 int  MaxOpenBuyOrders  = 1;
 int  MaxOpenSellOrders = 1;

       bool FilterBySymbol         = true;
extern bool FilterByMagicNumber    = true;
extern int    MagicNumber  = 3344223;//0:manual trades
extern string orderComment = "ForexBaron.net EA ";
extern int    Slippage     = 3;
extern double SLpips = 20.0;
extern double TPpips = 30.0;
////////////////////////////////////////////////
extern string lhi0="";
extern string lhi="lots to trade:";
extern double lots         = 1.0;
//
extern string rah="Use risk based lotsize, instead of fixed lots?";
extern string rah1="   if no SL is specified, the script calculates with 30pips";
extern bool   UseLotsPerRiskCalculation=FALSE;
extern double RiskPercentage=2.5;
extern bool   DoFreeMarginCheck          = FALSE;//should check if there is enough free margin to open the trade
extern bool   DoFreeMarginForSlAlsoCheck = FALSE;//checks if there is also enough free margin for a (potentially) SL-hit
//
 string bthi="send orders when backtesting?";
 bool SendTestOrdersOnBacktest=false;

string cstring;
double sl,tp,pips2dbl;
int buys,sells;

int start() { 
   CountOpenTrades(Symbol(),MagicNumber);
   Comment(cstring+"open trades="+(buys+sells)+" (buys="+buys+"|sells="+sells+")");

 
   if (sells==0&&buys<MaxOpenBuyOrders) {
   //old: if (buys==0) {
    double ask = MarketInfo(Symbol(),MODE_ASK);
    sl = GetSL(Symbol(),OP_BUY);
    tp = GetTP(Symbol(),OP_BUY);
    lots = CalcLotsAndCheckMargin(Symbol(),OP_BUY,lots,RiskPercentage,ask,sl);
    OrderSend2Stage(Symbol(),OP_BUY,lots,ask,Slippage,sl,tp,orderComment+": BUY",MagicNumber,0,CLR_NONE);
   }//if (buys==0) {
   
   if (buys==0&&sells<MaxOpenSellOrders) {
   //old: if (sells==0) {
    double bid = MarketInfo(Symbol(),MODE_BID);   
    sl = GetSL(Symbol(),OP_SELL);
    tp = GetTP(Symbol(),OP_SELL);
    lots = CalcLotsAndCheckMargin(Symbol(),OP_SELL,lots,RiskPercentage,bid,sl);
    OrderSend2Stage(Symbol(),OP_SELL,lots,bid,Slippage,sl,tp,orderComment+": SELL",MagicNumber,0,CLR_NONE);
   }//if (sells==0) {
 }

//+------------------------------------------------------------------+

int init()  {
 BrokerDigitAdjust(Symbol()); 
 cstring = "\n"+EANAME+", © 2014, Marc (fxdaytrader) et. al ******* http://ForexBaron.net"+"\n"+" :: server: "+AccountServer()+", broker: "+AccountCompany()+"\n\n";
 
 if (SendTestOrdersOnBacktest && IsTesting()) {
  bool result;
  if (SendLongTrades) {
   sl = GetSL(Symbol(),OP_BUY);
   tp = GetTP(Symbol(),OP_BUY);
   result=OrderSend2Stage(Symbol(),OP_BUY,lots,Ask,Slippage,sl,tp,orderComment+": BUY",MagicNumber,0,CLR_NONE);
  }//if (SendLongTrades) {
  if (SendShortTrades) {
   sl = GetSL(Symbol(),OP_SELL);
   tp = GetTP(Symbol(),OP_SELL);
   result=OrderSend2Stage(Symbol(),OP_SELL,lots,Bid,Slippage,sl,tp,orderComment+": SELL",MagicNumber,0,CLR_NONE);
  }//if (SendShortTrades) {
 }//if (SendTestOrdersOnBacktest && IsTesting()) {
 return(0);
}

int deinit() {
 Comment("");
 return(0);
}

void CountOpenTrades(string symbol,int magicnumber) {
 buys=0;
 sells=0;
 for (int cnt=OrdersTotal()-1; cnt>=0; cnt--) {
  if (!OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES)) continue;
  if (FilterBySymbol && OrderSymbol()!=symbol) continue;
  if (FilterByMagicNumber && OrderMagicNumber()!=magicnumber) continue;
   {
     if (OrderType()==OP_BUY) buys++;
     if (OrderType()==OP_SELL) sells++;
    }
  }
}

void BrokerDigitAdjust(string symbol) {
 int Multiplier = 1;
 int digits=MarketInfo(symbol,MODE_DIGITS);
 if (digits==3 || digits==5) Multiplier = 10;
 if (digits==6) Multiplier = 100;   
 if (digits==7) Multiplier = 1000;
 pips2dbl = Multiplier*MarketInfo(symbol,MODE_POINT);
 Slippage*=Multiplier;
}

bool OrderSend2Stage(string symbol,int type,double lots,double price,int slippage,double sl,double tp,string ocomment,int magic,datetime expiry,color col) {
 bool result=true;
 int ticket;
 RefreshRates();
 lots = NormalizeLots(symbol,lots);
 if ((type==OP_BUY&&SendLongTrades)||(type==OP_SELL&&SendShortTrades)) {
  ticket=OrderSend(symbol,type,lots,price,slippage,0,0,ocomment,magic,expiry,col);
  while (IsTradeContextBusy()) Sleep(100);
   if (!OrderSelect(ticket, SELECT_BY_TICKET)) return(false);
    if (sl!=0.00000 && tp!=0.00000) result = OrderModify(OrderTicket(),OrderOpenPrice(),sl,tp,OrderExpiration(),CLR_NONE);
    if (sl!=0.00000 && tp==0.00000) result = OrderModify(OrderTicket(),OrderOpenPrice(),sl,OrderTakeProfit(),OrderExpiration(),CLR_NONE);
    if (sl==0.00000 && tp!=0.00000) result = OrderModify(OrderTicket(),OrderOpenPrice(),OrderStopLoss(),tp,OrderExpiration(),CLR_NONE);
 }//if ((type==OP_BUY&&SendLongTrades)||(type==OP_SELL&&SendShortTrades)) {
 return(result);
}

//Lot size must be adjusted to be a multiple of lotstep, which may not be a power of ten on some brokers
//see also the original function by WHRoeder, http://forum.mql4.com/45425#564188, fxdaytrader
double NormalizeLots(string symbol, double lots) {
  if (MathAbs(lots)<MarketInfo(symbol,MODE_MINLOT)) return(MarketInfo(symbol,MODE_MINLOT));
  if (MathAbs(lots)>MarketInfo(symbol,MODE_MAXLOT)) return(MarketInfo(symbol,MODE_MAXLOT));
  double ls = MarketInfo(symbol,MODE_LOTSTEP); 
  lots=MathRound(lots/ls)*ls;
  return(MathMin(MarketInfo(symbol,MODE_MAXLOT),MathMax(MarketInfo(symbol,MODE_MINLOT),lots))); //check if lots >= min. lots && <= max. lots, fxdaytrader
}//double NormalizeLots(string symbol, double lots) {

//Open price for pending order must be adjusted to be a multiple of ticksize, not point, and on metals they are not the same.
//see also http://forum.mql4.com/45425#564188
double NormalizePrice(string symbol, double price) {
 if (price==0.00000000) return(0.0);
 double ts = MarketInfo(symbol,MODE_TICKSIZE);
return(MathRound(price/ts)*ts );
}

double GetSL(string symbol,int type) {
 sl=0.0;
 double ask=MarketInfo(symbol,MODE_ASK);
 double bid=MarketInfo(symbol,MODE_BID);
 if (type==OP_BUY && SLpips!=0.0) sl = ask-SLpips*pips2dbl;
 if (type==OP_SELL && SLpips!=0.0) sl = bid+SLpips*pips2dbl;
 sl = NormalizePrice(symbol,sl);
 return(sl);
}

double GetTP(string symbol,int type) {
 tp=0.0;
 double ask=MarketInfo(symbol,MODE_ASK);
 double bid=MarketInfo(symbol,MODE_BID);
 if (type==OP_BUY && TPpips!=0.0) tp = ask+TPpips*pips2dbl;
 if (type==OP_SELL && TPpips!=0.0) tp = bid-TPpips*pips2dbl;
 tp = NormalizePrice(symbol,tp);
 return(tp);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//risk/lots calculation stuff:
double CalcLotsAndCheckMargin(string symbol,int type,double lots,double RiskPercentage,double price,double SLprice) {
  
 if (MathAbs(RiskPercentage)>0.000 && UseLotsPerRiskCalculation) lots = CalculateLotSize(symbol,RiskPercentage,lots,price,SLprice);//if using risk-based calculation
 if (DoFreeMarginCheck) lots = FreeMarginCheck(symbol,type,lots,price,SLprice);//do we have enough free margin?
  else if (!DoFreeMarginCheck) lots = NormalizeLots(symbol,lots);
 return(lots);
}

double CalculateLotSize(string symbol,double RiskPercent,double lots,double BidAskPrice,double SLprice) {
   //Calculate the lot size by risk. Code kindly supplied by jmw1970. Nice one jmw (see www.SteveHopwoodForex.com), code adapted/modified by fxdaytrader (e.g. inputs)
      
  
  //mod. - if there is no SLprice specified, the script would work with 50 pips (you can change that, search for the 30 in the code some lines below) ...
  if (SLprice==0) SLprice = BidAskPrice+(30*pips2dbl);
    
   lots = NormalizeLots(symbol,lots);//to normalize and eventually set back to MODE_MINLOTS in case we have to return them now:, fxdaytrader
   if (RiskPercent==0.0) return(lots); //nothing to do ..., fxdaytrader
   if (BidAskPrice == 0.00000 || SLprice == 0.00000) return(lots);//Just in case to prevent occurring strange calculations, fxdaytrader
   
   double FreeMargin = AccountFreeMargin();
    //double TickValue = MarketInfo(symbol,MODE_TICKVALUE) ;
   //fix tickvalue for extremely rare occasion when a change in ticksize leads to a change in tickvalue, source: http://forum.mql4.com/29781, fxdaytrader
   double TickValue = MarketInfo(symbol,MODE_TICKVALUE) * MarketInfo(symbol,MODE_POINT) / MarketInfo(symbol,MODE_TICKSIZE);

   double LotStep = MarketInfo(symbol,MODE_LOTSTEP);
   double SLPts = MathAbs(BidAskPrice - SLprice);
   
   SLPts/= MarketInfo(symbol,MODE_POINT);//No idea why *= factor does not work here, but it doesn't
   
   double Exposure = SLPts * TickValue; // Exposure based on 1 full lot

   double AllowedExposure = (FreeMargin * RiskPercent) / 100;
   
   //int TotalSteps = ((AllowedExposure / Exposure) / LotStep);
   double TotalSteps = ((AllowedExposure / Exposure) / LotStep);//changed to double, fxdaytrader
   double LotSize = TotalSteps * LotStep;
   
   return(NormalizeLots(symbol,LotSize));
}//double CalculateLotSize(string symbol,double RiskPercent,double lots,double BidAskPrice,double SLprice) {

///////////////////////////////////////////////////////////////////////////////
//start freemargincheck
/*
  Usage: before the OrderSend-Command you may set your already calculated lotsize = FreeMarginCheck(string symbol,int ordertype,double lots,double BidAskPrice,double SLprice)
  if there is no SLprice specified, the script would work with 50 pips (you can change that, search for the 30 in the code some lines below) ...
*/

double FreeMarginCheck(string symbol,int ordertype,double lots,double BidAskPrice,double SLprice) { //fxdaytrader
  lots = NormalizeLots(symbol,lots);
  if (CheckAccountFreeMargin(symbol,ordertype,lots)) return(lots);//everything ok
 
  //adjust:
  Print("... not enough money, will try to decrease lotsize of "+DoubleToStr(lots,MarketInfo(symbol,MODE_DIGITS))+" (max. lots: "+DoubleToStr(MarketInfo(symbol,MODE_MAXLOT),MarketInfo(symbol,MODE_DIGITS))+" | min. lots: "+DoubleToStr(MarketInfo(symbol,MODE_MINLOT),MarketInfo(symbol,MODE_DIGITS))+") ...");
  if (SLprice==0.0) {
    //x-digit broker adjust
     int Multiplier = 1;
     double digits=MarketInfo(symbol,MODE_DIGITS);
     if (digits==3 || digits==5) Multiplier = 10;
     if (digits==6) Multiplier = 100;   
     if (digits==7) Multiplier = 1000;
     double pips2dbl = Multiplier*MarketInfo(symbol,MODE_POINT); 
    //end x-digit broker adjust
   Print("... no SL specified, will count with 30 pips instead ...");
   SLprice = BidAskPrice+(30*pips2dbl);
  }//if (SLprice==0.0) {
  
  double ls=MarketInfo(symbol,MODE_LOTSTEP);
  while (!CheckAccountFreeMargin(symbol,ordertype,lots))
   {
    //lots = lots - ls;
    lots = NormalizeLots(symbol,lots - ls);
   }//while (!CheckAccountFreeMargin(symbol,ordertype,lots))
  
  if (DoFreeMarginForSlAlsoCheck) {
   Print("... lotsize adjusted to "+DoubleToStr(lots,MarketInfo(symbol,MODE_DIGITS))+", checking for enough free margin for SL now ...");
   lots = NormalizeLots(symbol,CheckMarginAndNormalizeLotSize(symbol,lots,BidAskPrice,SLprice,AccountFreeMargin())); 
  }
  Print("... done - lotsize finally adjusted to "+DoubleToStr(lots,MarketInfo(symbol,MODE_DIGITS))+" ...");
  
 return(lots);//everything ok now, hopefully ... ;)
}//double FreeMarginCheck(string symbol,int ordertype,double lots) { //fxdaytrader

bool CheckAccountFreeMargin(string symbol,int ordertype,double lots) {
 if (ordertype==OP_BUYSTOP || ordertype==OP_BUYLIMIT) ordertype=OP_BUY;
 if (ordertype==OP_SELLSTOP || ordertype==OP_SELLLIMIT) ordertype=OP_SELL;
//nice formula/workaround, source/found at http://www.earnforex.com/blog/ordersend-error-134-no-enough-money/, fxdaytrader
  if ( ((AccountStopoutMode()==1) && (AccountFreeMarginCheck(symbol,ordertype,lots) > AccountStopoutLevel()))
  || ( (AccountStopoutMode()==0)  && (((AccountFreeMarginCheck(symbol,ordertype,lots)/AccountEquity())*100) > AccountStopoutLevel()))) return(true);//everything seems to be ok
   else return(false);
}//bool CheckAccountFreeMargin(string symbol,int ordertype,double lots) {

double CheckMarginAndNormalizeLotSize(string symbol,double lots,double BidAskPrice,double SLprice,double accBalance) {
//----
 lots = NormalizeLots(symbol,lots);
 
 double SLpips =(MathAbs(BidAskPrice - SLprice))/ MarketInfo(symbol,MODE_POINT);
   
 double ls = MarketInfo(symbol,MODE_LOTSTEP);
 double MarginRequired = MarketInfo(symbol,MODE_MARGINREQUIRED);
 //double accBalance = AccountFreeMargin();
 // fix tickvalue for extremely rare occasion when a change in ticksize leads to a change in tickvalue, source: http://forum.mql4.com/29781
 double tickValueReliable = MarketInfo(symbol,MODE_TICKVALUE) * MarketInfo(symbol,MODE_POINT) / MarketInfo(symbol,MODE_TICKSIZE);
  //Print("checkMarginAndNormalizeLotSize() - checking free margin (currency="+AccountCurrency()+"): free margin required for "+DoubleToStr(lots,3)+" lots: "+DoubleToStr(lots * MarginRequired,2)+" | for SL: "+DoubleToStr(lots * SLpips * PointValuePerLot(symbol),2)+" = total: "+DoubleToStr(lots * MarginRequired+lots * SLpips * PointValuePerLot(symbol),2)+" | current free margin: "+DoubleToStr(AccountFreeMargin(),2)+"");
  if (accBalance < lots * SLpips * tickValueReliable + lots * MarginRequired)
  //old: if (accBalance < lots * SLpips * PointValuePerLot(symbol) + lots * MarginRequired) // do we have enough money for that trade?
  { // if not, try to decrease lots:  
    if (lots>MarketInfo(symbol,MODE_MAXLOT)) lots=MarketInfo(symbol,MODE_MAXLOT);//saves time ...
   while (accBalance < (lots * SLpips * tickValueReliable) + (lots * MarginRequired))
    {
     lots = lots - ls;
     lots = NormalizeLots(symbol,lots);
    }
  }
//----
 //if (lots <= 0.0) lots = -1;
 if (lots <= 0.0) lots = MarketInfo(symbol,MODE_MINLOT);//return the minimum if everything other fails ...
 return(lots);
}//double CheckMarginAndNormalizeLotSize(string symbol,double lots,double BidAskPrice,double SLprice,double accBalance) {

//end freemargincheck
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////