//+----------+
//|Shell_18a |
//+----------+
#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


/*

---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


---Basket---

   In DOLLARS, this will close all trades after so much profit or loss   
   

---BasketTrail---

   In DOLLARS or PIPS, trails the basket of trades (Symbol() and MagicNumber) until the
   basket retraces to the level of the trail.


---BreakEven---

   Move the stoploss to break-even once the trade is in profit by 'breakeven' pips


---TrailStop---

   Follow price action with a stoploss, keeping the stoploss within 'TrailStop' pips
   

---General---

Added Robert Hill's routine to manage 4 or 5 decimal place brokers



*/


// EA SPECIFIC
extern int     LSMAPeriod         =      55   ;
extern string  PriceCodes         = "0-close 1-open 2-hi 3-lo 4-median";
extern int     LSMAPrice          =       1   ;
extern bool    ReverseTrades      =    false  ;

// user input
extern double Lots                =       0.1 ;
extern double ProfitMade          =       0   ; 
extern double LossLimit           =       0   ;

extern bool   BasketAsPips        =     true  ;
extern double BasketProfit        =       0   ; 
extern double BasketLoss          =       0   ;
extern double BasketTrail         =       0   ;

extern double BreakEven           =       0   ;
extern bool   TrailAfterProfit    =    false  ;
extern double TrailStop           =       0   ;

extern bool   KillLogging         =     true  ;


// Trade control
double        lotsi;                                // used for Martingale loss recovery
double        myPoint;                              // support for 3/5 decimal places
int           Slippage=2;                           // how many pips of slippage can you tolorate
bool          TradeAllowed=false;                   // used to manage trades
bool          waitforreverse=true;                  // wait for signal reversal
int           loopcount;                            // count of order attempts
int           maxloop=25;                           // maximum number of attempts to handle errors
int           LL2SL=20;                             // LossLimit to StopLoss server spread
int           MagicNumber  = 142537;                // allows multiple experts to trade on same account
string        TradeComment = "_shell18a.txt";       // where to log information
string        pr;                                   // chart period for logging

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

//baskettrail
double btrail=0;                                    // trail the basket (Symbol() and MagicNumber)


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


#define goingup   1
#define goingdown 0

int prevDIR;
int currDIR;

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

int init()
  {
   // hide the unused procedure "Warning"s 
   // as it freaks non-programmers out badly
   if(0==1) iLSMA(0,1,1,1);

   // get normalized Point based on Broker decimal places
   myPoint = SetPoint();

   // chart period for logging
   switch (Period())
     {
     case    1: pr = "M1";  break;
     case    5: pr = "M5";  break;
     case   15: pr = "M15"; break;
     case   30: pr = "M30"; break;
     case   60: pr = "H1";  break;
     case  240: pr = "H4";  break;
     case 1440: pr = "D1";  break;
     }
   
   logwrite(TradeComment,"Init Complete");
   Comment(" ");

  }


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

int deinit()
  {
   logwrite(TradeComment,"DE-Init Complete");
   Comment(" ");
  }


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

int start()
  {

   //**************ADDED FOR ACCOUNT SENTRY ***************
   if (GlobalVariableGet("GV_CloseAllAndHalt") > 0) return (0);
   //**************END ACCOUNT SENTRY HOOK ****************

   //procedures use lotsi, so keep it 
   //tracked with Lots
   lotsi=Lots;

   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;
   double CurrentBasket=0;
     
   // direction control
   bool BUYme=false;
   bool SELLme=false;

   //safety counter
   int   loopcount=0;

   double lsma0;
   double lsma1;
   double lsma2;
      

   OrdersPerSymbol=0;
   for(cnt=OrdersTotal();cnt>=0;cnt--)
     {
      OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
      if( OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) OrdersPerSymbol++;
     }


   // bar counting
   if(bartime!=iTime(Symbol(), 0, 0) ) 
     {
      bartime=iTime(Symbol(), 0, 0) ;
      bartick++; 

      //+-----------------------------+
      //| Code here will execute once |
      //| at the OPEN of a NEW BAR    |
      //+-----------------------------+
         
      lsma1=iLSMA(0, LSMAPeriod, LSMAPrice, 1);
      lsma0=iLSMA(0, LSMAPeriod, LSMAPrice, 0);

      if(lsma1<lsma0)
        {
         if(ReverseTrades) SELLme=true; else BUYme=true;
         currDIR=goingup;
        }
        
      if(lsma1>lsma0)
        {
         if(ReverseTrades) BUYme=true; else SELLme=true;
         currDIR=goingdown;
        }
      
      if(currDIR!=prevDIR)
        {
         CloseEverything();
         waitforreverse=false;
        }
      
      prevDIR=currDIR;

      // prevents 1st order when EA put on chart
      TradeAllowed=true;
         
      //+------------+
      //| End Insert |
      //+------------+

     }



     
   //+-----------------------------+
   //| Insert your indicator here  |
   //| And set either BUYme or     |
   //| SELLme true to place orders |
   //+-----------------------------+

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

   //ENTRY LONG (buy, Ask) 
   if( !waitforreverse && TradeAllowed && BUYme)
     {
      OpenBuy();
     }
        

   //ENTRY SHORT (sell, Bid)
   if( !waitforreverse && TradeAllowed && SELLme)
     {
      OpenSell();
     }


   //
   // Order Management
   //



   //Basket profit

   // Basket profit or loss - count the profit/loss
   // from this EA & Symbol()& magicnumber only
   // calculation can be done in PIPS or DOLLARS

   CurrentBasket=0;
   for(cnt=OrdersTotal();cnt>=0;cnt--)
     {
      OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);

      if( OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber && BasketAsPips)
        {
         // add up pips (This will be in pips something like 0.0098
         if(OrderType()==OP_BUY  ) CurrentBasket=CurrentBasket+( Ask-OrderOpenPrice() );
         if(OrderType()==OP_SELL ) CurrentBasket=CurrentBasket+( OrderOpenPrice()-Bid );
        }
      if( OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber && !BasketAsPips)
        {
         //add up dollars
         if(OrderType()==OP_BUY  ) CurrentBasket=CurrentBasket+(  OrderProfit() );
         if(OrderType()==OP_SELL ) CurrentBasket=CurrentBasket+(  OrderProfit() );
        }
     }

   //scale pips up to integers is AsPips
   if(BasketAsPips) CurrentBasket=CurrentBasket/myPoint;
   
   
   if( BasketProfit>0 && CurrentBasket>=BasketProfit ) 
     {
      CloseEverything();
      btrail=0;
      waitforreverse=true;
     }

   if( BasketLoss  >0 && CurrentBasket<=BasketLoss * (-1) )
     {
      CloseEverything();
      btrail=0;
      waitforreverse=true;
     }


   //basket trailing stop
   //calculate trail
   if(BasketTrail>0 && CurrentBasket>BasketTrail)
     {
      if(CurrentBasket>btrail)
        {
         btrail=CurrentBasket;
        }
     }
   //close on retrace
   if(btrail>0 && CurrentBasket<(btrail-BasketTrail))
     {
      CloseEverything();
      btrail=0;
      waitforreverse=true;
     }




   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()) ;

            // check for break even
            if( BreakEven>0 )                                                                       CheckBuyBreakEven(CurrentProfit);

            // check for trail before any profit
            if( TrailStop>0 && !TrailAfterProfit)                                                   CheckBuyTrailingStop();
            // check for trail after 'trail' pips of profit
            if( TrailStop>0 &&  TrailAfterProfit && Close[0]>OrderOpenPrice()+(TrailStop*myPoint) ) CheckBuyTrailingStop();
             
            // check for profit
            if(ProfitMade>0 && CurrentProfit>=(ProfitMade*myPoint))                                 CloseBuy("PROFIT");
              
            // check for loss
            if(LossLimit>0 && CurrentProfit<=(LossLimit*(-1)*myPoint))                              CloseBuy("LOSS");
              
           } // if BUY


         if(OrderType()==OP_SELL)
           {
            CurrentProfit=(OrderOpenPrice()-Ask);

            // check for break even
            if( BreakEven>0 )                                                                       CheckSellBreakEven(CurrentProfit);
           
            // check for trail before any profit
            if( TrailStop>0 && !TrailAfterProfit)                                                   CheckSellTrailingStop();
            // check for trail after 'trail' pips of profit
            if( TrailStop>0 &&  TrailAfterProfit && Close[0]<OrderOpenPrice()-(TrailStop*myPoint) ) CheckSellTrailingStop();


            // check for profit
            if( ProfitMade>0 && CurrentProfit>=(ProfitMade*myPoint) )                               CloseSell("PROFIT");

            // check for loss
            if( LossLimit>0 && CurrentProfit<=(LossLimit*(-1)*myPoint) )                            CloseSell("LOSS");
           } //if SELL
           

        } // if(OrderSymbol&&MagicNumber)
        

     } // for


  } // start()




//+-----------------+
//| CloseEverything |
//+-----------------+
// Closes all OPEN and PENDING orders

int CloseEverything()
  {
   int i;
    
   for(i=OrdersTotal();i>=0;i--)
     {

      OrderSelect(i, SELECT_BY_POS);
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
        {
         if(OrderType()==OP_BUY)       CloseBuy ("BASKET");
         if(OrderType()==OP_SELL)      CloseSell("BASKET");
         if(OrderType()==OP_BUYLIMIT)  OrderDelete( OrderTicket() );
         if(OrderType()==OP_SELLLIMIT) OrderDelete( OrderTicket() );
         if(OrderType()==OP_BUYSTOP)   OrderDelete( OrderTicket() );
         if(OrderType()==OP_SELLSTOP)  OrderDelete( OrderTicket() );
        }

      Sleep(1000);

     } //for
  
  } // closeeverything



// log data to a file name passed in
// print everything regardless of log setting
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);
     }
  } 



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

      // PLACE order is independent of MODIFY order. 
      // This is mandatory for ECNs and acceptable for retail brokers

      loopcount=0;
      while(true)          
        {
         // place order - NO TP OR SL
         ticket=OrderSend(Symbol(),OP_BUY,lotsi,Ask,Slippage,0,0,TradeComment,MagicNumber,White);
         gle=GetLastError();
         if(gle==0)
           {
            logwrite(TradeComment,"BUY PLACED Ticket="+ticket+" Ask="+Ask+" Lots="+lotsi);
            TradeAllowed=false;
            break;
           }
          else 
           {
            logwrite(TradeComment,"-----ERROR-----  Placing BUY order: Lots="+lotsi+" Bid="+Bid+" Ask="+Ask+" ticket="+ticket+" Err="+gle+" "+ErrorDescription(gle)); 
            
            RefreshRates();
            Sleep(500);

            // give up after loopcount tries
            loopcount++;
            if(loopcount>maxloop)
              {
               logwrite(TradeComment,"-----ERROR-----  Giving up on placing BUY order"); 
               return(gle);
              }
           }
        }//while - place order 


      // modify the order for users TP & SL
      loopcount=0;
      while(true)
        {
         // don't set TP and SL both to zero, they're already there
         if(LossLimit==0 && ProfitMade==0) break;
         
         if(LossLimit  ==0) SL=0;
         if(ProfitMade ==0) TP=0;
         if(LossLimit   >0) SL=Ask-((LossLimit+LL2SL)*myPoint );
         if(ProfitMade  >0) TP=Ask+((ProfitMade+LL2SL)*myPoint );
         OrderModify(ticket,OrderOpenPrice(),SL,TP,0,White);
         gle=GetLastError();
         if(gle==0)
           {
            logwrite(TradeComment,"BUY MODIFIED Ticket="+ticket+" Ask="+Ask+" Lots="+lotsi+" SL="+SL+" TP="+TP);
            break;
           }
          else 
           {
            logwrite(TradeComment,"-----ERROR-----  Modifying 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)
              {
               logwrite(TradeComment,"-----ERROR-----  Giving up on modifying BUY order"); 
               return(gle);
              }
           }
        }//while - modify order
        
        
     }//BUYme




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

      // PLACE order is independent of MODIFY order. 
      // This is mandatory for ECNs and acceptable for retail brokers

      loopcount=0;
      while(true)
        {
         ticket=OrderSend(Symbol(),OP_SELL,lotsi,Bid,Slippage,0,0,TradeComment,MagicNumber,Red);
         gle=GetLastError();
         if(gle==0)
           {
            logwrite(TradeComment,"SELL PLACED Ticket="+ticket+" Bid="+Bid+" Lots="+lotsi);
            TradeAllowed=false;
            break;
           }
            else 
           {
            logwrite(TradeComment,"-----ERROR-----  placing 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)
              {
               logwrite(TradeComment,"-----ERROR-----  Giving up on placing SELL order"); 
               return(gle);
              }
           }
        }//while

      
      // modify the order for users TP & SL
      loopcount=0;
      while(true)
        {
         // don't set TP and SL both to zero, they're already there
         if(LossLimit==0 && ProfitMade==0) break;
         
         if(LossLimit  ==0) SL=0;
         if(ProfitMade ==0) TP=0;
         if(LossLimit   >0) SL=Bid+((LossLimit+LL2SL)*myPoint );
         if(ProfitMade  >0) TP=Bid-((ProfitMade+LL2SL)*myPoint );
         OrderModify(ticket,OrderOpenPrice(),SL,TP,0,Red);
         gle=GetLastError();
         if(gle==0)
           {
            logwrite(TradeComment,"SELL MODIFIED Ticket="+ticket+" Bid="+Bid+" Lots="+lotsi+" SL="+SL+" TP="+TP);
            break;
           }
            else 
           {
            logwrite(TradeComment,"-----ERROR-----  modifying 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)
              {
               logwrite(TradeComment,"-----ERROR-----  Giving up on placing SELL order"); 
               return(gle);
              }
           }

        }//while

     }//SELLme





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);
        }


      loopcount++;
      if(loopcount>maxloop)
        {
         logwrite(TradeComment,"-----ERROR-----  Giving up on closing SELL order"); 
         return(gle);
        }
                     
     }//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);
        }

      loopcount++;
      if(loopcount>maxloop)
        {
         logwrite(TradeComment,"-----ERROR-----  Giving up on closing SELL order"); 
         return(gle);
        }
                 
     }//while                 
  }      



void CheckBuyBreakEven(double CurrProfit) 
  {
   double SL;  //stoploss
   double TP;  //takeprofit
   
   int  gle;   // getlasterror number

   // This event sets OrderStopLoss equal to OrderOpenPrice (plus spread)
   // thus it will only ever get executed one time per ticket

   if (CurrProfit >= BreakEven*myPoint && OrderOpenPrice()>OrderStopLoss())
     {
      SL=OrderOpenPrice()+(Ask-Bid);
      TP=OrderTakeProfit();
      OrderModify(OrderTicket(),OrderOpenPrice(),SL,TP, White);
      gle=GetLastError();
      if(gle==0)
        {
         logwrite(TradeComment,"MODIFY BUY BE Ticket="+OrderTicket()+" SL="+SL+" TP="+TP);
        }
       else 
        {
         logwrite(TradeComment,"-----ERROR----- MODIFY BUY  BE Bid="+Bid+" error="+gle+" "+ErrorDescription(gle));
        }

     }

  }  //CheckBreakEven



void CheckSellBreakEven(double CurrProfit) 
  {
   double SL;  //stoploss
   double TP;  //takeprofit
   
   int  gle;   // getlasterror number

   // This event sets OrderStopLoss equal to OrderOpenPrice (plus spread)
   // thus it will only ever get executed one time per ticket

   if (CurrProfit >= BreakEven*myPoint && OrderOpenPrice()<OrderStopLoss())
     {
      SL=OrderOpenPrice()-(Ask-Bid);
      TP=OrderTakeProfit();
      OrderModify(OrderTicket(),OrderOpenPrice(),SL,TP, Red);
      gle=GetLastError();
      if(gle==0)
        {
         logwrite(TradeComment,"MODIFY SELL BE Ticket="+OrderTicket()+" SL="+SL+" TP="+TP);
        }
       else 
        {
         logwrite(TradeComment,"-----ERROR----- MODIFY SELL BE Ask="+Ask+" error="+gle+" "+ErrorDescription(gle));
        }

     }

  }//checksellbreakevn




void CheckBuyTrailingStop()
  {
   double SL;  //stoploss
   double TP;  //takeprofit
   
   int  gle;   // getlasterror number

   // check for trailing stop
   if( OrderStopLoss()==0 || OrderStopLoss() < Bid-(TrailStop*myPoint) )
     {
      SL=Bid-(TrailStop*myPoint);
      TP=OrderTakeProfit();
      OrderModify(OrderTicket(),OrderOpenPrice(),SL,TP,0,White);
      gle=GetLastError();
      if(gle==0)
        {
         logwrite(TradeComment,"MODIFY BUY TS Ticket="+OrderTicket()+" SL="+SL+" TP="+TP);
        }
       else 
        {
         logwrite(TradeComment,"-----ERROR----- MODIFY BUY TS Bid="+Bid+" error="+gle+" "+ErrorDescription(gle)+" ");
        }

     }

  }//CheckBuyTrailingStop



void CheckSellTrailingStop()
  {
   double SL;  //stoploss
   double TP;  //takeprofit
   
   int  gle;   // getlasterror number

   // check for trailing stop
   if( OrderStopLoss()==0 || OrderStopLoss() > Ask+(TrailStop*myPoint) )
     {
      SL=Ask+(TrailStop*myPoint);
      TP=OrderTakeProfit();
      OrderModify(OrderTicket(),OrderOpenPrice(),SL,TP,0,Red);
      gle=GetLastError();
      if(gle==0)
        {
         logwrite(TradeComment,"MODIFY SELL TS Ticket="+OrderTicket()+" SL="+SL+" TP="+TP);
        }
       else 
        {
         logwrite(TradeComment,"-----ERROR----- MODIFY SELL TS Ask="+Ask+" error="+gle+" "+ErrorDescription(gle));
        }

     }

  }//checkselltrailingstop




// Function to correct the value of Point
// for brokers that add an extra digit to price
// Courtesy of Robert Hill

double SetPoint()
{
   double mPoint;
  
   if (Digits < 4)
      mPoint = 0.01;
   else
      mPoint = 0.0001;
  
   return(mPoint);
}


double iLSMA(int TimeFrame, int LSMAPeriod, int LSMAPrice,int shift)
  {
   double wt;

   double ma1=iMA(NULL,TimeFrame,LSMAPeriod,0,MODE_SMA ,LSMAPrice,shift);
   double ma2=iMA(NULL,TimeFrame,LSMAPeriod,0,MODE_LWMA,LSMAPrice,shift);
   wt = MathFloor((3.0*ma2-2.0*ma1)/Point)*Point;
   return(wt);
  } 


