//+------+
//|BollTrade
//+------+
#property copyright "Ron Thompson"
#property link      "http://www.ForexMT4.com/"

// This EA is NEVER TO BE SOLD individually 
// This EA is NEVER TO BE INCLUDED as part of a collection that is SOLD

/*

This EA is designed for EURUSD M15

This EA places BUY and SELL MARKET ORDERS depending on how far outside the Bollinger Bands
the current price falls. This is a counter-trade system, that is, it SELLS when the UPPER
bands are exceeded by BDistance pips and BUYS when the LOWER Band is exceeded.
BDistance, BPerion and Deviation control the Bollinger Bands

It depends on retracement and exhaustion to make profit.

It only places one trade per bar.



If enabled (IStep greater than 0) the system will double lot sizes when there is a loss.
This is an attempt to recover the previous loss, and make money on the current trade.
And there are styles (IStyle) of lot growth for loss recovery

The greatest failure of this EA will come when you don't have enough money to support
the Martingles steps anymore, so start your lot sizes small.

LotIncreasement grows the lot size based on your starting balance, risking more money
when you have more money. It is built in, based on Starting Balance and LotResolution,
and is independent of IStep and the Martingale process.


---Starting Balance---
   
   The formula for Lots is    AccountBalance() / StartingBalance

   Lets say your AccountBalance() is currently 550$
      
   if you want to trade 10.0  lots, put   55    into StartingBalance    550 /    55  = 10
   if you want to trade  1.0  lots, put   550   into StartingBalance    550 /   550  =  1
   if you want to trade  0.5  lots, put   1100  into StartingBalance    550 /  1100  =  0.5
   if you want to trade  0.1  lots, put   5500  into StartingBalance    550 /  5500  =  0.1
   if you want to trade  0.01 lots, put   55000 into StartingBalance    550 / 55000  =  0.01 
   
---LotResolution---

   This controls the Lot size of step your broker/account can support
   If set to 1, then your lot size is increased in 0.1 steps as your account grows
   If set to 2, then your lot size is increased in 0.01 steps as your account grows
   
   IBFX supports LotResolution of 2
   

---IStep---

   IStep is ONLY USED WHEN IStyle==1
   
   IStep is the USER control for progression of lot growth to recover loss
   Lots are multiplied by this number for each losing trade
      
      

---IStyle---

   Allows you to pick the type of loss recovery you're most comfortible with.
   
   0 == NO LOSS RECOVERY
   1 == lot growth based on IStep
   2 == Modified Martingale - growth by 2.0000 with 1st step being 1 
   3 == Fibonacci - growth by Fibo numbers, requires 2 wins to recover the total loss
   4 == future implementation
   
      
---SameBarRecovery---
   Usually there is only 1 trade allowed per bar. If this option is set to true, then
   another trade will be allowed on the same bar, but only if the last trade was a loss.
   
   NOTE! - This does NOT work well with Darma's ATR option


---MinFreeMarginPct---

   This percent of your account must be free for margin before an order will be placed
   
   

---ProfitMade---

   The amount of pips you expect to make on EACH TRADE
   

---LossLimit---

   The amount of pips you can afford to lose on EACH TRADE
   

---Other elements---

   Trades are constrained to one-per-bar. This is controlled in the    // bar counting
   section of the code. Trade is only enabled if there are no orders open at the new bar.
   Search for     //uncomment for multiple trades per bar 
   There is also an override for this using SameBarRecovery but only for losing trades
   
*/



// user input
extern double StartingBalance   =   40976;    // lot size control during LotIncrease
extern double LotResolution     =       2;    // lot increase increment  1 = 0.1   2 = 0.01

extern bool   UseNewsAvoidance  =    true;
extern int    MinsBefore        =      15;
extern int    MinsAfter         =      15;
extern bool   IncludeMedImpact  =    true;
extern bool   IncludeLowImpact  =    true;

extern bool   SameBarRecovery   =    true;
extern int    IStyle            =       1;    // Which style of loss recovery
extern double IStep             =       2.2;  // User style double, triple, or what
extern double MinFreeMarginPct  =      25.0;  // margin available before trading

extern bool   UseATR            =   false;
extern double ATR_BDistanceMult =       1.4;
extern double ATR_LossLimitMult =       3.6;
extern double ATR_ProfitMadeMult=       0.8;
extern int    ATR_Period        =       4;

extern double ProfitMade        =       8;    // how much money do you expect to make
extern double LossLimit         =       9;    // how much loss can you tolorate
extern double BDistance         =      14;    // plus how much
extern int    BPeriod           =      15;    // Bollinger period
extern double Deviation         =       1.8;  // Bollinger deviation


// naming, numbering, logging
string TradeComment = "_bolltrade_v05b.txt"; // logfile name ( prefixed with Symbol() )
int    MagicNumber  = 200702022059;          // allows multiple experts to trade on same account/Symbol
bool   KillLogging=false;                    // turn off all logging

// Bar handling
datetime bartime=0;                          // used to determine when a bar has moved
int      bartick=0;                          // number of times bars have moved

// Trade control & statistics
double Lots;                                 // size of trades, calculated in init()
double lotsi;                                // used for Martingale loss recovery
int    Slippage=2;                           // how many pips of slippage can you tolorate
bool   TradeAllowed=true;                    // used to manage trades
int    OrderBar=0;                           // used with SameBarRecovery
int    loopcount;                            // count of order attempts
int    maxloop=25;                           // maximum number of attempts to handle errors
int    L2L=10;                               // real-to-server difference for order masking
int    maxOrders;                            // statistic for maximum numbers or orders open at one time
double maxLots;                              // Largest lot size ever opened

// used for verbose error logging
#include <stdlib.mqh>

// Loss Recovery
int    lotptr=0;                             // array pointer for Loss Recovery
int    maxptr=9;                             // maximum Loss Recovery array element
int    Step[10];                             // how to step the IStep pointer

//EA specific
double myATR;

//+-------------+
//| Custom init |
//|-------------+
// Called ONCE when EA is added to chart or recompiled

int init()
  {
   Lots=NormalizeDouble(AccountBalance()/StartingBalance,LotResolution);
   lotsi=Lots;
   
   logwrite(TradeComment,"LotIncrease ACTIVE Account balance="+AccountBalance()+" Lots="+Lots+" StartingBalance="+StartingBalance);

   // correct LotResolution if not 1 or 2
   if(LotResolution<1 || LotResolution>2) LotResolution=1;
   
   
   //
   // The Loss Recovery array element 0 should always start with a 
   // value of 1 which represents lotsi*1 so the lot size is NOT 
   // affected if the loss recovery option is not used during trading.
   //
   
   // NO LOSS RECOVERY
   if (IStyle==0)
     {
      logwrite(TradeComment,"Loss Recovery Style = 0 - NONE");
      Step[0]=1;
      Step[1]=1;
      Step[2]=1;
      Step[3]=1;
      Step[4]=1;
      Step[5]=1;
      Step[6]=1;
      Step[7]=1;
      Step[8]=1;
      Step[9]=1;
     }

   // USER steps based on IStep   
   if (IStyle==1)
     {
      logwrite(TradeComment,"Loss Recovery Style = 1 - USER");
      int stylei=1;
      Step[0]=stylei; logwrite(TradeComment,"Step0="+stylei); stylei=stylei*IStep;
      Step[1]=stylei; logwrite(TradeComment,"Step1="+stylei); stylei=stylei*IStep;
      Step[2]=stylei; logwrite(TradeComment,"Step2="+stylei); stylei=stylei*IStep;
      Step[3]=stylei; logwrite(TradeComment,"Step3="+stylei); stylei=stylei*IStep;
      Step[4]=stylei; logwrite(TradeComment,"Step4="+stylei); stylei=stylei*IStep;
      Step[5]=stylei; logwrite(TradeComment,"Step5="+stylei); stylei=stylei*IStep;
      Step[6]=stylei; logwrite(TradeComment,"Step6="+stylei); stylei=stylei*IStep;
      Step[7]=stylei; logwrite(TradeComment,"Step7="+stylei); stylei=stylei*IStep;
      Step[8]=stylei; logwrite(TradeComment,"Step8="+stylei); stylei=stylei*IStep;
      Step[9]=stylei; logwrite(TradeComment,"Step9="+stylei); stylei=stylei*IStep;
     }

   // Martingale
   if (IStyle==2)
     {
      
      // slightly modified to start at 1 since the current order 
      // plus the new order means it's doubled
      logwrite(TradeComment,"Loss Recovery Style = 2 - MART");
      Step[0]=1;
      Step[1]=1;
      Step[2]=2;
      Step[3]=4;
      Step[4]=8;
      Step[5]=16;
      Step[6]=32;
      Step[7]=64;
      Step[8]=128;
      Step[9]=256;
     }

   // Fibonacci
   if (IStyle==3)
     {
      logwrite(TradeComment,"Loss Recovery Style = 3 - FIBO");
      Step[0]=1;
      Step[1]=1;
      Step[2]=2;
      Step[3]=3;
      Step[4]=5;
      Step[5]=8;
      Step[6]=13;
      Step[7]=21;
      Step[8]=34;
      Step[9]=55;
     }

   logwrite(TradeComment,"Init Complete");
   logwriteblank(TradeComment);
   logwriteblank(TradeComment);
   logwriteblank(TradeComment);
   Comment(" ");
  }

//+----------------+
//| Custom DE-init |
//+----------------+
// Called ONCE when EA is removed from chart

int deinit()
  {

   // always indicate deinit statistics
   logwrite(TradeComment,"MAX number of orders "+maxOrders);
   logwrite(TradeComment,"Max Lots is "+maxLots);
   
   logwrite(TradeComment,"DE-Init Complete");
   Comment(" ");
  }


//+-----------+
//| Main      |
//+-----------+
// Called EACH TICK and each Bar[]

int start()
  {

   int      cnt=0;
   int      gle=0;
   int      ticket=0;
   int      OrdersPerSymbol=0;
  
   // stoploss and takeprofit and close control
   double SL=0;
   double TP=0;
   double CurrentProfit=0;
   
   // direction control
   bool BUYme=false;
   bool SELLme=false;
      

   // bar counting
   if(bartime!=Time[0]) 
     {
      bartime=Time[0];
      bartick++; 
      OrdersPerSymbol=0;
      for(cnt=OrdersTotal();cnt>=0;cnt--)
        {
         OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
         if( OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) OrdersPerSymbol++;
        } 

      // this allows only one trade per bar preventing
      // lots of trades on big, single bar news moves
      if(OrdersPerSymbol==0) TradeAllowed=true;
 
      if(UseATR)
        {
         myATR=iATR(Symbol(),0,ATR_Period,1)/Point; 
         BDistance = ATR_BDistanceMult *myATR; 
         logwrite(TradeComment,"New bar - BDistance="+BDistance+" myATR="+myATR);
        }
     }

   // Lot increasement based on AccountBalance and StartingBalance
   Lots=NormalizeDouble(AccountBalance()/StartingBalance,LotResolution);
   if( Lots>MarketInfo(Symbol(),MODE_MAXLOT) ) Lots=MarketInfo(Symbol(),MODE_MAXLOT);
   
     
   //+-----------------------------+
   //| Insert your indicator here  |
   //| And set either BUYme or     |
   //| SELLme true to place orders |
   //+-----------------------------+
   
   double ma = iMA(Symbol(),0,BPeriod,0,MODE_SMA,PRICE_OPEN,0);
   double stddev = iStdDev(Symbol(),0,BPeriod,0,MODE_SMA,PRICE_OPEN,0);   
   double bup = ma+(Deviation*stddev);
   double bdn = ma-(Deviation*stddev);

   if(UseATR)
     {
      myATR=iATR(Symbol(),0,ATR_Period,1)/Point; 
      BDistance = ATR_BDistanceMult *myATR; 
      LossLimit = ATR_LossLimitMult *myATR; 
      ProfitMade= ATR_ProfitMadeMult*myATR; 
     }

   if(Close[0]>bup+(BDistance*Point)) SELLme=true;
   if(Close[0]<bdn-(BDistance*Point))  BUYme=true;
   

   //+------------+
   //| End Insert |
   //+------------+

   //ENTRY LONG (buy, Ask) 
   if( TradeAllowed && BUYme )
     {
      logwrite(TradeComment,"BUY  lotptr="+lotptr+"  Step="+Step[lotptr]);
      if(UseATR)
        {
         logwrite(TradeComment,"BUY  BDistance="+BDistance+" ATR_BDistance="+BDistance+" ATR_ProfitMade="+ProfitMade+" ATR_LossLimit="+LossLimit);
        }
      OpenBuy();
     }
        

   //ENTRY SHORT (sell, Bid)
   if( TradeAllowed && SELLme )
     {
      logwrite(TradeComment,"SELL lotptr="+lotptr+"  Step="+Step[lotptr]);
      if(UseATR)
        {
         logwrite(TradeComment,"SELL BDistance="+BDistance+" ATR_BDistance="+BDistance+" ATR_ProfitMade="+ProfitMade+" ATR_LossLimit="+LossLimit);
        }
      OpenSell();
     }

     
   //
   // Order Management
   //
   for(cnt=OrdersTotal();cnt>=0;cnt--)
     {
      OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
      if( OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber )
        {
        
         if(OrderType()==OP_BUY)
           {
            CurrentProfit=Bid-OrderOpenPrice() ;
            
            // Did we make a profit or loss
            if( ProfitMade>0 && (CurrentProfit/Point) >= ProfitMade )
              {
               CloseBuy("PROFIT");
               lotsi=Lots;
               lotptr=0;
               logwrite(TradeComment,"BUY PROFIT RESETS pointer lotptr="+lotptr+"  Step="+Step[lotptr]);
               logwriteblank(TradeComment);
              }
            if( LossLimit>0 && CurrentProfit<=(LossLimit*(-1)*Point)  )
              {
               CloseBuy("LOSS");
               lotptr++;
               if(lotptr>maxptr) lotptr=0;
               logwrite(TradeComment,"BUY LOSS BUMPS pointer lotptr="+lotptr+"  Step="+Step[lotptr]);
               lotsi=NormalizeDouble(Lots*Step[lotptr],LotResolution);
               if(SameBarRecovery && OrderBar==bartick)
                 {
                  logwrite(TradeComment,"BUY LOSS Allowing another trade during same bar");
                  TradeAllowed=true;
                 }
               logwriteblank(TradeComment);
              }

           } //if BUY


         if(OrderType()==OP_SELL)
           {
            CurrentProfit=OrderOpenPrice()-Ask;
            
            // Did we make a profit or loss
            if( ProfitMade>0 && (CurrentProfit/Point) >= ProfitMade )
              {
               CloseSell("PROFIT");
               lotsi=Lots;
               lotptr=0;
               logwrite(TradeComment,"SELL PROFIT RESETS pointer lotptr="+lotptr+"  Step="+Step[lotptr]);
               logwriteblank(TradeComment);
              }
            if( LossLimit>0 && CurrentProfit<=(LossLimit*(-1)*Point) )
              {
               CloseSell("LOSS");
               lotptr++;
               if(lotptr>maxptr) lotptr=0;
               logwrite(TradeComment,"SELL LOSS BUMPS pointer lotptr="+lotptr+"  Step="+Step[lotptr]);
               lotsi=NormalizeDouble(Lots*Step[lotptr],LotResolution);
               if(SameBarRecovery && OrderBar==bartick)
                 {
                  logwrite(TradeComment,"SELL LOSS Allowing another trade during same bar");
                  TradeAllowed=true;
                 }
                logwriteblank(TradeComment);
              }

           } //if SELL
           
        } // if(OrderSymbol)
        
     } // for

   // statistic for lot growth
   if(lotsi>maxLots) maxLots=lotsi;

  } // start()


void logwrite (string filename, string mydata)
  {
   int myhandle;
   string gregorian=TimeToStr(CurTime(),TIME_DATE|TIME_SECONDS);

   Print(mydata+" "+gregorian);
   
   // don't log anything if testing or if user doesn't want it
   //if(IsTesting()) return(0);
   if(KillLogging) return(0);

   myhandle=FileOpen(Symbol()+"_"+filename, FILE_CSV|FILE_WRITE|FILE_READ, ";");
   if(myhandle>0)
     {
      FileSeek(myhandle,0,SEEK_END);
      FileWrite(myhandle, mydata+" "+gregorian);
      FileClose(myhandle);
     }
  } 


void logwriteblank (string filename)
  {
   int myhandle;
      
   // don't log anything if testing or if user doesn't want it
   if(IsTesting()) return(0);
   if(KillLogging) return(0);

   myhandle=FileOpen(Symbol()+"_"+filename, FILE_CSV|FILE_WRITE|FILE_READ, ";");
   if(myhandle>0)
     {
      FileSeek(myhandle,0,SEEK_END);
      FileWrite(myhandle," ");
      FileClose(myhandle);
     }
  } 


//ENTRY LONG (buy, Ask) 
void OpenBuy()
     {
      int      gle=0;
      int      ticket=0;
      
      double SL=0;
      double TP=0;

      int loopcount=0;
      while(true)
        {
         if( AccountFreeMargin()< (AccountBalance()*(MinFreeMarginPct/100)) )
           {
            logwrite(TradeComment,"Your BUY equity is too low to trade");
            break;
           }

         if(LossLimit ==0) SL=0; else SL=Ask-((LossLimit+L2L)*Point );
         if(ProfitMade==0) TP=0; else TP=Ask+((ProfitMade+L2L)*Point );
         ticket=PlaceOrder(Symbol(),OP_BUY,lotsi,Ask,Slippage,SL,TP,TradeComment,MagicNumber,White);
         gle=GetLastError();
         if(gle==0)
           {
            logwrite(TradeComment,"BUY Ticket="+ticket+" Ask="+Ask+" Lots="+lotsi+" SL="+SL+" TP="+TP);
            TradeAllowed=false;
            OrderBar=bartick;
            break;
           }
            else 
           {
            logwrite(TradeComment,"-----ERROR-----  opening BUY order: Lots="+lotsi+" SL="+SL+" TP="+TP+" Bid="+Bid+" Ask="+Ask+" ticket="+ticket+" Err="+gle+" "+ErrorDescription(gle)); 
            
            RefreshRates();
            Sleep(500);

            loopcount++;
            if(loopcount>maxloop) break;
           }
        }//while   
     }//BUYme



   //ENTRY SHORT (sell, Bid)
void OpenSell()
     {
      int      gle=0;
      int      ticket=0;
      
      double SL=0;
      double TP=0;

      int loopcount=0;
      while(true)
        {
         if( AccountFreeMargin()< (AccountBalance()*(MinFreeMarginPct/100)) )
           {
            logwrite(TradeComment,"Your SELL equity is too low to trade");
            break;
           }

         if(LossLimit ==0) SL=0; else SL=Bid+((LossLimit+L2L)*Point );
         if(ProfitMade==0) TP=0; else TP=Bid-((ProfitMade+L2L)*Point );
         ticket=PlaceOrder(Symbol(),OP_SELL,lotsi,Bid,Slippage,SL,TP,TradeComment,MagicNumber,Red);
         gle=GetLastError();
         if(gle==0)
           {
            logwrite(TradeComment,"SELL Ticket="+ticket+" Bid="+Bid+" Lots="+lotsi+" SL="+SL+" TP="+TP);
            TradeAllowed=false;
            OrderBar=bartick;
            break;
           }
            else 
           {
            logwrite(TradeComment,"-----ERROR-----  opening SELL order: Lots="+lotsi+" SL="+SL+" TP="+TP+" Bid="+Bid+" Ask="+Ask+" ticket="+ticket+" Err="+gle+" "+ErrorDescription(gle)); 
                            
            RefreshRates();
            Sleep(500);

            loopcount++;
            if(loopcount>maxloop) break;
           }
        }//while
     }//SELLme




int PlaceOrder(string sym, int cmd, double vol, double price, int slippage, double sl, double tp, string comment, int magic, color arrow_color)
{
	int ticket = -1;
	int minsUntil;
	int minsSince;
	bool placeOrder = true;
	
	if (UseNewsAvoidance)
	{
		// Call indicator to see if we are near news time
		minsSince = iCustom(NULL, 0, "IndFFCal", true, IncludeMedImpact, IncludeLowImpact, true, 1, 0);
		minsUntil = iCustom(NULL, 0, "IndFFCal", true, IncludeMedImpact, IncludeLowImpact, true, 2, 0);
		
		if (minsSince < MinsAfter && minsUntil < MinsBefore)
			placeOrder = false;
	}
	if (placeOrder)
		ticket = OrderSend(sym, cmd, vol, price, slippage, sl, tp, comment, magic, arrow_color);

	return (ticket);
}



void CloseBuy (string myInfo)
  {
   int gle;
   int cnt;
   int OrdersPerSymbol;

   int loopcount=0;
   
   string bTK=" Ticket="+OrderTicket();
   string bSL=" SL="+OrderStopLoss();
   string bTP=" TP="+OrderTakeProfit();
   string bPM;
   string bLL;
   string bER;

   bPM=" PM="+ProfitMade;
   bLL=" LL="+LossLimit;

   while(true)
     {
      OrderClose(OrderTicket(),OrderLots(),Bid,Slippage,White);
      gle=GetLastError();
      bER=" error="+gle+" "+ErrorDescription(gle);

      if(gle==0)
        {
         logwrite(TradeComment,"CLOSE BUY "+myInfo+ bTK + bSL + bTP + bPM + bLL);
         break;
        }
       else 
        {
         logwrite(TradeComment,"-----ERROR----- CLOSE BUY "+myInfo+ bER +" Bid="+Bid+ bTK + bSL + bTP + bPM + bLL);
         RefreshRates();
         Sleep(500);
        }


      // sometimes an order close is delayed, or a gap jumps to the L2L on the server
      // This keeps a server-closed order from hanging here
      OrdersPerSymbol=0;
      for(cnt=OrdersTotal();cnt>=0;cnt--)
        {
         OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
         if( OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) OrdersPerSymbol++;
        }
   
      if(OrdersPerSymbol==0) break;

      loopcount++;
      if(loopcount>maxloop) break;
                     
     }//while
  
  }



void CloseSell (string myInfo)
  {
   int gle;
   int cnt;
   int OrdersPerSymbol;

   int loopcount=0;

   string sTK=" Ticket="+OrderTicket();
   string sSL=" SL="+OrderStopLoss();
   string sTP=" TP="+OrderTakeProfit();
   string sPM;
   string sLL;
   string sER;
      
   sPM=" PM="+ProfitMade;
   sLL=" LL="+LossLimit;

   while(true)
     {
      OrderClose(OrderTicket(),OrderLots(),Ask,Slippage,Red);
      gle=GetLastError();
      sER=" error="+gle+" "+ErrorDescription(gle);
      
      if(gle==0)
        {
         logwrite(TradeComment,"CLOSE SELL "+myInfo + sTK + sSL + sTP + sPM + sLL);
         break;
        }
      else 
        {
         logwrite(TradeComment,"-----ERROR----- CLOSE SELL "+myInfo+ sER +" Ask="+Ask+ sTK + sSL + sTP + sPM + sLL);
         RefreshRates();
         Sleep(500);
        }

      // sometimes an order close is delayed, or a gap jumps to the LL2SL on the server
      // This keeps a server-closed order from hanging here
      OrdersPerSymbol=0;
      for(cnt=OrdersTotal();cnt>=0;cnt--)
        {
         OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
         if( OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) OrdersPerSymbol++;
        }
   
      if(OrdersPerSymbol==0) break;
                    
      loopcount++;
      if(loopcount>maxloop) break;
                 
     }//while                 
  }    
  


