//+------------------------------------------------------------------+ 
//|                                 exp_AccountProfitTrailing_v1.mq4 | 
//|                                                                * | 
//|                                                                * | 
//+------------------------------------------------------------------+ 
#property copyright "*" 
#property link      "*" 

//---- input parameters 
extern bool    TakeProfit_ON        =  false;         // Включения тейкпрофита по проценту Эквити/Баланс 
extern double  TakeProfitPercent    =  50.0;          // Процент прибыли 
extern string  s0                   =  "======================================================="; 
extern bool    StopLoss_ON          =  false;         // Включения стоплосс по проценту Эквити/Баланс 
extern double  StopLossPercent      =  20.0;          // Процент убытка 
extern string  s2                   =  "======================================================="; 
extern bool    Trailing_ON          =  false;         // Включения трейлинга по проценту Эквити/Баланс 
extern double  TrailingStartPercent =  10;            // Процент Эквити/Баланс при котором начинается перемещение уровня закрытия 
extern double  TrailingLevelPercent =  5;             // Величина снижения процента Эквити/Баланс, при котором выполняется закрытие 
extern string  s3                   =  "======================================================="; 
extern bool    ShowComment          =  True;          // Показывать комментарий. 
       int      NumberAccount        =  0;             // Номер торгового счёта. Работать только на указанном счете. При значении <=0 - номер счета не проверяется 
extern bool      UseSendMail          =  True;          // Использовать передачу почтовых сообщений 
extern double  MinCangesPercent     =  0.5;           // Минимальное изменение приыбыли при котором отсылается емайл 
extern int     Slippage             =  50;            // Допустимое проскальзывание 
extern int     NumberOfTry          =  5;             // Количество торговых попыток после ошибки 
extern int     PauseAfterError      =  75;            // Пауза после ошибки в секундах 
extern string  s4                   =  "======================================================="; 
extern int     InTesterDir          =  0;             // Работает только в тестере для проверки. 1 - бай, -1 - селл 

double CurrentProfitPC; 

double TrailingStopPercent; 
bool TrailingStopFlag; 
string cs; 

double LastProfitPC; 




//+------------------------------------------------------------------+ 
//| expert initialization function                                   | 
//+------------------------------------------------------------------+ 
int init() 
  { 
//---- 
   LastProfitPC=ND2(100.0*(AccountEquity()/AccountBalance())); 
//---- 
   return(0); 
  } 
//+------------------------------------------------------------------+ 
//| expert deinitialization function                                 | 
//+------------------------------------------------------------------+ 
int deinit() 
  { 
//---- 
    
//---- 
   return(0); 
  } 
//+------------------------------------------------------------------+ 
//| expert start function                                            | 
//+------------------------------------------------------------------+ 
int start(){ 

      if(NumberAccount>0){ 
         if(AccountNumber()!=NumberAccount){ 
            static bool lt=false; 
               if(!lt){ 
                  lt=true; 
                  Alert("Неправильный номер счета"); 
               } 
            return(0); 
         } 
      } 
      
   //============================================================================================== 
    
      if(IsTesting()){ 
         if(OrdersTotal()==0){ 
            if(InTesterDir==1){ 
               OrderSend(Symbol(),OP_BUY,0.1,NormalizeDouble(Ask,Digits),0,0,0,NULL,0,CLR_NONE);    
            }  
            if(InTesterDir==-1){ 
               OrderSend(Symbol(),OP_SELL,0.1,NormalizeDouble(Bid,Digits),0,0,0,NULL,0,CLR_NONE);    
            }            
         } 
      } 
    
   //==============================================================================================    
    
      CurrentProfitPC=ND2(100.0*(AccountEquity()/AccountBalance())); 

   //============================================================================================== 
    
      if(UseSendMail){ 
         if(ND2(MathAbs(CurrentProfitPC-LastProfitPC))>=MinCangesPercent){ 
            LastProfitPC=CurrentProfitPC; 
            SendMail("Account # "+AccountNumber(),"Current Profit: "+DS2(CurrentProfitPC-100)+" %"); 
         } 
      } 
    
   //==============================================================================================    
    
      cs=""; 

         if(ShowComment){ 
            cs=cs+"Текущая прибыль: "+DS2(CurrentProfitPC-100)+" %"+"\n"; 
         } 

         if(TakeProfit_ON){ 
            fTP(); 
         } 
      
         if(StopLoss_ON){ 
            fSL(); 
         } 
      
         if(Trailing_ON){ 
            fTS(); 
         } 
      
         if(ShowComment){ 
            Comment(cs); 
         }      

      return(0); 
} 
//+------------------------------------------------------------------+ 

void fTS(){ 
   if(CurrentProfitPC>=100.0+TrailingStartPercent){ 
         if(!TrailingStopFlag){ 
            TrailingStopPercent=CurrentProfitPC-TrailingLevelPercent; 
         } 
         else{ 
            TrailingStopPercent=MathMax(TrailingStopPercent,CurrentProfitPC-TrailingLevelPercent); 
         } 
      TrailingStopFlag=true; 
   } 
   if(TrailingStopFlag){ 
      if(CurrentProfitPC<=TrailingStopPercent){ 
         int rv=0; 
            if(UseSendMail){ 
               SendMail("Account # "+AccountNumber(),"Start to close all (TS)"); 
            } 
            for(int i=0;i<NumberOfTry;i++){ 
               rv=fOrderCloseDelAll(); 
                  if(rv==0){ 
                     break; 
                  } 
               Sleep(1000*PauseAfterError); 
            } 
            if(UseSendMail){ 
               if(rv==0){ 
                  SendMail("Account # "+AccountNumber(),"Close all OK"); 
               } 
               else{ 
                  SendMail("Account # "+AccountNumber(),"Close all ERROR");                
               } 
            }            
         TrailingStopFlag=false;    
      } 
   } 
   if(ShowComment){ 
      if(TrailingStopFlag){ 
         cs=cs+"Закрытие по трейлингу на уровне: "+DS2(TrailingStopPercent-100)+" %\n"; 
      }  
      else{ 
         cs=cs+"Прибыль еще не зафиксирована\n";          
      } 
   } 
} 


void fTP(){ 
   if(CurrentProfitPC>=100.0+TakeProfitPercent){ 
      int rv=0; 
         if(UseSendMail){ 
            SendMail("Account # "+AccountNumber(),"Start to close all (TP)"); 
         }      
         for(int i=0;i<NumberOfTry;i++){ 
            rv=fOrderCloseDelAll(); 
               if(rv==0){ 
                  break; 
               } 
            Sleep(1000*PauseAfterError); 
         } 
         if(UseSendMail){ 
            if(rv==0){ 
               SendMail("Account # "+AccountNumber(),"Close all OK"); 
            } 
            else{ 
               SendMail("Account # "+AccountNumber(),"Close all ERROR");                
            } 
         }          
   } 
} 

void fSL(){ 
   if(CurrentProfitPC<=100.0-StopLossPercent){ 
      int rv=0; 
         if(UseSendMail){ 
            SendMail("Account # "+AccountNumber(),"Start to close all (SL)"); 
         }      
         for(int i=0;i<NumberOfTry;i++){ 
            rv=fOrderCloseDelAll(); 
               if(rv==0){ 
                  break; 
               } 
            Sleep(1000*PauseAfterError); 
         } 
         if(UseSendMail){ 
            if(rv==0){ 
               SendMail("Account # "+AccountNumber(),"Close all OK"); 
            } 
            else{ 
               SendMail("Account # "+AccountNumber(),"Close all ERROR");                
            } 
         }          
   } 
} 


double ND2(double v){return(NormalizeDouble(v,2));} 

int fOrderCloseDelAll(){ 
   int tErr=0; 
      for(int i=OrdersTotal()-1;i>=0;i--){ 
         if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)){ 
                  switch(OrderType()){    
                     case OP_BUY: 
                        RefreshRates(); 
                           if(!IsTradeContextBusy()){ 
                              if(!OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),Slippage,CLR_NONE)){ 
                                 int tCheck=GetLastError(); 
                                 Print("Error close BUY "+OrderTicket()+" "+fMyErDesc(tCheck)); 
                                 tErr=-1; 
                              } 
                           } 
                           else{ 
                              static int lt1=0; 
                                 if(TimeCurrent()>lt1+20){ 
                                    lt1=TimeCurrent(); 
                                    Print("Need close BUY "+OrderTicket()+". Trade Context Busy"); 
                                 }            
                              return(-2); 
                           }    
                     break; 
                     case OP_SELL: 
                        RefreshRates(); 
                           if(!IsTradeContextBusy()){                        
                              if(!OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),Slippage,CLR_NONE)){ 
                                 tCheck=GetLastError(); 
                                 Print("Error close SELL "+OrderTicket()+" "+fMyErDesc(tCheck)); 
                                 tErr=-1; 
                              }  
                           } 
                           else{ 
                              static int lt2=0; 
                                 if(TimeCurrent()>lt2+20){ 
                                    lt2=TimeCurrent(); 
                                    Print("Need close SELL "+OrderTicket()+". Trade Context Busy"); 
                                 }            
                              return(-2); 
                           }          
                     break; 
                     case OP_BUYSTOP: 
                           RefreshRates(); 
                              if(!IsTradeContextBusy()){ 
                                 if(!OrderDelete(OrderTicket())){ 
                                    tCheck=GetLastError(); 
                                    Print("Error delete BUYSTOP "+OrderTicket()+" "+fMyErDesc(tCheck)); 
                                    tErr=-1; 
                                 } 
                              } 
                              else{ 
                                 static int lt3=0; 
                                    if(TimeCurrent()>lt3+20){ 
                                       lt3=TimeCurrent(); 
                                       Print("Need delete BUYSTOP "+OrderTicket()+". Trade Context Busy"); 
                                    }            
                                 return(-2); 
                              }    
                     break; 
                     case OP_SELLSTOP: 
                           RefreshRates(); 
                              if(!IsTradeContextBusy()){                        
                                 if(!OrderDelete(OrderTicket())){ 
                                    tCheck=GetLastError(); 
                                    Print("Error delete SELLSTOP "+OrderTicket()+" "+fMyErDesc(tCheck)); 
                                    tErr=-1; 
                                 }  
                              } 
                              else{ 
                                 static int lt4=0; 
                                    if(TimeCurrent()>lt4+20){ 
                                       lt4=TimeCurrent(); 
                                       Print("Need delete SELLSTOP "+OrderTicket()+". Trade Context Busy"); 
                                    }            
                                 return(-2); 
                              }          
                     break; 
                     case OP_BUYLIMIT: 
                           RefreshRates(); 
                              if(!IsTradeContextBusy()){ 
                                 if(!OrderDelete(OrderTicket())){ 
                                    tCheck=GetLastError(); 
                                    Print("Error delete BUYLIMIT "+OrderTicket()+" "+fMyErDesc(tCheck)); 
                                    tErr=-1; 
                                 } 
                              } 
                              else{ 
                                 static int lt5=0; 
                                    if(TimeCurrent()>lt5+20){ 
                                       lt5=TimeCurrent(); 
                                       Print("Need delete BUYLIMIT "+OrderTicket()+". Trade Context Busy"); 
                                    }            
                                 return(-2); 
                              }    
                     break; 
                     case OP_SELLLIMIT: 
                           RefreshRates(); 
                              if(!IsTradeContextBusy()){                        
                                 if(!OrderDelete(OrderTicket())){ 
                                    tCheck=GetLastError(); 
                                    Print("Error delete SELLLIMIT "+OrderTicket()+" "+fMyErDesc(tCheck)); 
                                    tErr=-1; 
                                 }  
                              } 
                              else{ 
                                 static int lt6=0; 
                                    if(TimeCurrent()>lt6+20){ 
                                       lt6=TimeCurrent(); 
                                       Print("Need delete SELLSTOP "+OrderTicket()+". Trade Context Busy"); 
                                    }            
                                 return(-2); 
                              }          
                     break; 
                  }    
         } 
      } 
   return(tErr); 
}  

string DS2(double v){return(DoubleToStr(v,2));} 


string fMyErDesc(int aErrNum){ 
    
   // fMyErDesc(GetLastError()); 
    
   string pref="Err Num: "+aErrNum+" - "; 
   switch(aErrNum){ 
      case 0: return(pref+"NO ERROR"); 
      case 1: return(pref+"NO RESULT");                                  
      case 2: return(pref+"COMMON ERROR");                              
      case 3: return(pref+"INVALID TRADE PARAMETERS");                  
      case 4: return(pref+"SERVER BUSY");                                
      case 5: return(pref+"OLD VERSION");                                
      case 6: return(pref+"NO CONNECTION");                              
      case 7: return(pref+"NOT ENOUGH RIGHTS");                          
      case 8: return(pref+"TOO FREQUENT REQUESTS");                      
      case 9: return(pref+"MALFUNCTIONAL TRADE");                        
      case 64: return(pref+"ACCOUNT DISABLED");                          
      case 65: return(pref+"INVALID ACCOUNT");                          
      case 128: return(pref+"TRADE TIMEOUT");                            
      case 129: return(pref+"INVALID PRICE");                            
      case 130: return(pref+"INVALID STOPS");                            
      case 131: return(pref+"INVALID TRADE VOLUME");                    
      case 132: return(pref+"MARKET CLOSED");                            
      case 133: return(pref+"TRADE DISABLED");                          
      case 134: return(pref+"NOT ENOUGH MONEY");                        
      case 135: return(pref+"PRICE CHANGED");                            
      case 136: return(pref+"OFF QUOTES");                              
      case 137: return(pref+"BROKER BUSY");                              
      case 138: return(pref+"REQUOTE");                                  
      case 139: return(pref+"ORDER LOCKED");                            
      case 140: return(pref+"LONG POSITIONS ONLY ALLOWED");              
      case 141: return(pref+"TOO MANY REQUESTS");                        
      case 145: return(pref+"TRADE MODIFY DENIED");                      
      case 146: return(pref+"TRADE CONTEXT BUSY");                      
      case 147: return(pref+"TRADE EXPIRATION DENIED");                  
      case 148: return(pref+"TRADE TOO MANY ORDERS");                    
      //---- mql4 run time errors 
      case 4000: return(pref+"NO MQLERROR");                            
      case 4001: return(pref+"WRONG FUNCTION POINTER");                  
      case 4002: return(pref+"ARRAY INDEX OUT OF RANGE");                
      case 4003: return(pref+"NO MEMORY FOR FUNCTION CALL STACK");      
      case 4004: return(pref+"RECURSIVE STACK OVERFLOW");                
      case 4005: return(pref+"NOT ENOUGH STACK FOR PARAMETER");          
      case 4006: return(pref+"NO MEMORY FOR PARAMETER STRING");          
      case 4007: return(pref+"NO MEMORY FOR TEMP STRING");              
      case 4008: return(pref+"NOT INITIALIZED STRING");                  
      case 4009: return(pref+"NOT INITIALIZED ARRAYSTRING");            
      case 4010: return(pref+"NO MEMORY FOR ARRAYSTRING");              
      case 4011: return(pref+"TOO LONG STRING");                        
      case 4012: return(pref+"REMAINDER FROM ZERO DIVIDE");              
      case 4013: return(pref+"ZERO DIVIDE");                            
      case 4014: return(pref+"UNKNOWN COMMAND");                        
      case 4015: return(pref+"WRONG JUMP");                              
      case 4016: return(pref+"NOT INITIALIZED ARRAY");                  
      case 4017: return(pref+"DLL CALLS NOT ALLOWED");                  
      case 4018: return(pref+"CANNOT LOAD LIBRARY");                    
      case 4019: return(pref+"CANNOT CALL FUNCTION");                    
      case 4020: return(pref+"EXTERNAL EXPERT CALLS NOT ALLOWED");      
      case 4021: return(pref+"NOT ENOUGH MEMORY FOR RETURNED STRING");  
      case 4022: return(pref+"SYSTEM BUSY");                            
      case 4050: return(pref+"INVALID FUNCTION PARAMETERS COUNT");      
      case 4051: return(pref+"INVALID FUNCTION PARAMETER VALUE");        
      case 4052: return(pref+"STRING FUNCTION INTERNAL ERROR");          
      case 4053: return(pref+"SOME ARRAY ERROR");                        
      case 4054: return(pref+"INCORRECT SERIES ARRAY USING");            
      case 4055: return(pref+"CUSTOM INDICATOR ERROR");                  
      case 4056: return(pref+"INCOMPATIBLE ARRAYS");                    
      case 4057: return(pref+"GLOBAL VARIABLES PROCESSING ERROR");      
      case 4058: return(pref+"GLOBAL VARIABLE NOT FOUND");              
      case 4059: return(pref+"FUNCTION NOT ALLOWED IN TESTING MODE");    
      case 4060: return(pref+"FUNCTION NOT CONFIRMED");                  
      case 4061: return(pref+"SEND MAIL ERROR");                        
      case 4062: return(pref+"STRING PARAMETER EXPECTED");              
      case 4063: return(pref+"INTEGER PARAMETER EXPECTED");              
      case 4064: return(pref+"DOUBLE PARAMETER EXPECTED");              
      case 4065: return(pref+"ARRAY AS PARAMETER EXPECTED");            
      case 4066: return(pref+"HISTORY WILL UPDATED");                    
      case 4067: return(pref+"TRADE ERROR");                            
      case 4099: return(pref+"END OF FILE");                            
      case 4100: return(pref+"SOME FILE ERROR");                        
      case 4101: return(pref+"WRONG FILE NAME");                        
      case 4102: return(pref+"TOO MANY OPENED FILES");                  
      case 4103: return(pref+"CANNOT OPEN FILE");                        
      case 4104: return(pref+"INCOMPATIBLE ACCESS TO FILE");            
      case 4105: return(pref+"NO ORDER SELECTED");                      
      case 4106: return(pref+"UNKNOWN SYMBOL");                          
      case 4107: return(pref+"INVALID PRICE PARAM");                    
      case 4108: return(pref+"INVALID TICKET");                          
      case 4109: return(pref+"TRADE NOT ALLOWED");                      
      case 4110: return(pref+"LONGS  NOT ALLOWED");                      
      case 4111: return(pref+"SHORTS NOT ALLOWED");                      
      case 4200: return(pref+"OBJECT ALREADY EXISTS");                  
      case 4201: return(pref+"UNKNOWN OBJECT PROPERTY");                
      case 4202: return(pref+"OBJECT DOES NOT EXIST");                  
      case 4203: return(pref+"UNKNOWN OBJECT TYPE");                    
      case 4204: return(pref+"NO OBJECT NAME");                          
      case 4205: return(pref+"OBJECT COORDINATES ERROR");                
      case 4206: return(pref+"NO SPECIFIED SUBWINDOW");                  
      case 4207: return(pref+"SOME OBJECT ERROR");    
      default: return(pref+"WRONG ERR NUM");                
   } 
} 