
//+----------------------------------------------------+
//|                 EAHelper, library for MetaTrader 4 |
//|         Copyright © 2010, asasa, trapule@gmail.com |
//+----------------------------------------------------+

#define EAHELPER_NAME_VERSION "EAHelper v1.0.9"

extern bool freshstart = true;
bool writelog          = true;
int  ordersendslippage = 20;
int  tickcoherencypips = 200;
int  ncommentlines     = 47;

#define LOG_SEP "---------------------------------------------------"
#define NTS             1
#define MAX_ORDERS      100

string eaname;
int timeframeint;
datetime timecurr;
int magicnum[NTS];
int tickcount = 0;
datetime timeprev;
double prevtick;
string custompar;
int loghandle = NULL;
string logfilename;
string errmsg = "";
string lastlog = "";
string latestcommentlines;
datetime bartimeprev;
string symbolstring;
int uninitreason;
int digits, lotdigits;
double tick, pip, minlot, maxlot;
double lotval, spread, stoplevel;
int bon[NTS], son[NTS], bsn[NTS], ssn[NTS], bln[NTS], sln[NTS];
int bot[NTS][MAX_ORDERS], sot[NTS][MAX_ORDERS];
int bst[NTS][MAX_ORDERS], sst[NTS][MAX_ORDERS];
int blt[NTS][MAX_ORDERS], slt[NTS][MAX_ORDERS];
int bonp[NTS], sonp[NTS], bsnp[NTS], ssnp[NTS], blnp[NTS], slnp[NTS];
int botp[NTS][MAX_ORDERS], sotp[NTS][MAX_ORDERS];
int bstp[NTS][MAX_ORDERS], sstp[NTS][MAX_ORDERS];
int bltp[NTS][MAX_ORDERS], sltp[NTS][MAX_ORDERS];

//--------------------------------------------------------------------

bool InitLog ()
{
  if (writelog)
  {
    int OpenMode;
        
    if (IsTesting()) 
      OpenMode = FILE_WRITE | FILE_BIN;
    else 
      OpenMode = FILE_WRITE | FILE_READ | FILE_BIN;
      
    logfilename = eaname + "_log_" +symbolstring+".txt";
    loghandle = FileOpen (logfilename, OpenMode);
    
    if (loghandle == -1)
    {
      errmsg = "[ERROR] Cannot create log file";
      Alert (errmsg);
      Print (errmsg);
      return (false);
    }
    
    FileSeek (loghandle, 0, SEEK_END);
  }

  return(true);
}

//--------------------------------------------------------------------

bool DeInitLog ()
{
  if (writelog)
    FileClose (loghandle);
    
  return (true);
}

//--------------------------------------------------------------------

bool LogMsg (string Msg)
{
  if (writelog)
  {
    lastlog = "#" + tickcount + ", "
              + TimeToStr(timecurr,TIME_DATE|TIME_MINUTES|TIME_SECONDS)
              +", "+Symbol()+", "+Msg;
    
    string OutMsg = lastlog + "\r\n";
    
    if (! IsTesting())
    {
      int IdxStart = 0;
      int IdxFound = 0;
      int LineCount = 0;
    
      while (true)
      {
        IdxFound = StringFind (latestcommentlines, "\n", IdxStart);
      
        if (IdxFound == -1)
          break;
        
        LineCount++;
        IdxStart = IdxFound+1;
      }

      if (LineCount >= ncommentlines)
          latestcommentlines = StringSubstr (latestcommentlines, StringFind(latestcommentlines,"\n",0)+1);
    
      latestcommentlines = latestcommentlines + OutMsg;    
    }
    
    if (! FileWriteString (loghandle, OutMsg, StringLen(OutMsg)))
    {
      int ErrCode = GetLastError();
      errmsg = "Error writing to output file, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+")";
      Alert (errmsg);
      Print (errmsg);
      return (false);
    }
    
    FileFlush (loghandle);
  }    
    
  return (true);
}

//--------------------------------------------------------------------

void LogRestartString (string EAName)
{
  LogMsg (LOG_SEP);
  LogMsg ("#### " + eaname + " restart");
  LogMsg ("#### Expert Advisor full name: " + EAName);
  LogMsg ("#### EAHelper library: " + EAHELPER_NAME_VERSION);
  LogMsg (LOG_SEP);
}

//--------------------------------------------------------------------

void LogAccountData ()
{
  LogMsg ("Account:");
  LogMsg ("  Time current = "+TimeToStr(TimeCurrent(),TIME_DATE|TIME_MINUTES|TIME_SECONDS));
  LogMsg ("  Account company = "+AccountCompany());
  LogMsg ("  Account server = "+AccountServer());
  //LogMsg ("  Account name = "+AccountName());
  //LogMsg ("  Account number = "+AccountNumber());
  LogMsg ("  Account currency = "+AccountCurrency());
  LogMsg ("  Account balance = "+dtsa(AccountBalance()));
  LogMsg ("  Account equity = "+dtsa(AccountEquity()));
  LogMsg ("  Account margin = "+dtsa(AccountMargin()));
  LogMsg ("  Account free margin = "+dtsa(AccountFreeMargin()));
  LogMsg ("  Account leverage = "+AccountLeverage());
  LogMsg ("  Terminal company = "+TerminalCompany());
  LogMsg ("  Terminal name = "+TerminalName());
  LogMsg ("  Terminal path = "+TerminalPath());
  LogMsg ("  Is connected = "+IsConnected());
  LogMsg ("  Is demo = "+IsDemo());
  LogMsg ("  Is dlls allowed = "+IsDllsAllowed());
  LogMsg ("  Is expert enabled = "+IsExpertEnabled());
  LogMsg ("  Is libraries allowed = "+IsLibrariesAllowed());
  LogMsg ("  Is optimization = "+IsOptimization());
  LogMsg ("  Is stopped = "+IsStopped());
  LogMsg ("  Is testing = "+IsTesting());
  LogMsg ("  Is trade allowed = "+IsTradeAllowed());
  LogMsg ("  Is visual mode = "+IsVisualMode());
  LogMsg ("  Uninitialize reason = "+UninitializeReason());
  LogMsg ("  Symbol = "+Symbol());
  LogMsg ("  Point = "+Point);
  LogMsg ("  Digits = "+Digits);
  LogMsg ("  Spread = "+MarketInfo(Symbol(),MODE_SPREAD));
  LogMsg ("  Stop level = "+MarketInfo(Symbol(),MODE_STOPLEVEL));
  LogMsg ("  Lot size = "+MarketInfo(Symbol(),MODE_LOTSIZE));
  LogMsg ("  Tick value = "+MarketInfo(Symbol(),MODE_TICKVALUE));
  LogMsg ("  Tick size = "+MarketInfo(Symbol(),MODE_TICKSIZE));
  LogMsg ("  Min lot = "+MarketInfo(Symbol(),MODE_MINLOT));
  LogMsg ("  Max lot = "+MarketInfo(Symbol(),MODE_MAXLOT));
  LogMsg ("  Lot step = "+MarketInfo(Symbol(),MODE_LOTSTEP));
  LogMsg ("  Margin required = "+MarketInfo(Symbol(),MODE_MARGINREQUIRED));
  LogMsg ("  Freeze level = "+MarketInfo(Symbol(),MODE_FREEZELEVEL));
  LogMsg ("  Global variables total = "+GlobalVariablesTotal());
}

//--------------------------------------------------------------------

void WriteDeInitToLog ()
{
  int DeInitReason = UninitializeReason();
  
  LogMsg (LOG_SEP);
  LogMsg ("#### " + eaname + " deinit");
  
  switch (DeInitReason)
  {
    case 1: LogMsg("Reason "+DeInitReason+": remove"); break;
    case 2: LogMsg("Reason "+DeInitReason+": recompile"); break;
    case 3: LogMsg("Reason "+DeInitReason+": chart change"); break;
    case 4: LogMsg("Reason "+DeInitReason+": chart close"); break;
    case 5: LogMsg("Reason "+DeInitReason+": parameters change"); break;
    case 6: LogMsg("Reason "+DeInitReason+": account deactivation"); break;
  }
  
  LogMsg (LOG_SEP);
  
  if (errmsg != "")
    LogMsg ("EXITED WITH ERROR CONDITION");
    
  return;
}

//--------------------------------------------------------------------

bool LogCurrentPos ()
{
  int i;
  
  for (i=0; i<bon[0]; i++)
  {
    OrderSelect (bot[0][i], SELECT_BY_TICKET);
    LogMsg ("Curr pos, BUY OPEN #"+OrderTicket()+", lots "+dtsl(OrderLots())+", profit "+dtsa(OrderProfit())+", pip "+(dtsa((Bid-OrderOpenPrice())/pip)));
  }

  for (i=0; i<son[0]; i++)
  {
    OrderSelect (sot[0][i], SELECT_BY_TICKET);
    LogMsg ("Curr pos, SELL OPEN #"+OrderTicket()+", lots "+dtsl(OrderLots())+", profit "+dtsa(OrderProfit())+", pip "+(dtsa((-Ask+OrderOpenPrice())/pip)));
  }

  if (bon[0]==0 && son[0]==0)  
    LogMsg ("Curr pos, FLAT");
  
  return (true);
}

//--------------------------------------------------------------------

bool LogHistory ()
{
  double PipRes;
  double TotPipPos=0.0, TotPipNeg=0.0;
  int NWinners=0, NLosers=0;
  int i, TotHistOrd;
  
  TotHistOrd = OrdersHistoryTotal();
  
  for (i=0; i<TotHistOrd; i++)
  {
    OrderSelect(i, SELECT_BY_POS, MODE_HISTORY);
    
    if ((OrderSymbol()!=Symbol()) || (OrderMagicNumber()!=magicnum[0]))
      continue;
    
    if (OrderType() == OP_BUY)
    {
      PipRes = (OrderClosePrice()-OrderOpenPrice())/pip;
      
      if (PipRes > 0.0)
      {
        NWinners++;
        TotPipPos+=PipRes;
      }
      else
      {
        NLosers++;
        TotPipNeg-=PipRes;
      }
    }
    else if (OrderType() == OP_SELL)
    {
      PipRes = (-OrderClosePrice()+OrderOpenPrice())/pip;
      
      if (PipRes > 0.0)
      {
        NWinners++;
        TotPipPos+=PipRes;
      }
      else
      {
        NLosers++;
        TotPipNeg-=PipRes;
      }
    }
  }

  LogMsg ("Order history, tot pos "+NWinners+" ("+dtsa(TotPipPos)+" pips), tot neg "+NLosers+" ("+dtsa(TotPipNeg)+" pips)");
  return (true);
}

//--------------------------------------------------------------------

void LogBlock (string Msg)
{
  LogMsg (LOG_SEP);
  LogMsg (Msg);
  LogMsg ("Current time "+TimeToStr(timecurr,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
  LogMsg ("Bid "+dtsp(Bid)+", Ask "+dtsp(Ask)+", Spread "+dtsa(spread));
  LogMsg ("Account equity "+dtsa(AccountEquity()));
  LogCurrentPos();
  LogHistory();
  return;
}

//--------------------------------------------------------------------

bool InitLibGlobals (string EAName, int BaseMagicNumber, int TimeFrame)
{
  GetState();

  timeframeint = TimeFrame;
  if (timeframeint != 0) symbolstring = Symbol()+timeframeint;
  else symbolstring = Symbol();
  magicnum[0] = CalcMagicNumber (BaseMagicNumber);
  timecurr = TimeCurrent();
  timeprev = timecurr;
  if (timeframeint != 0) bartimeprev = iTime(0,timeframeint,0);
  else bartimeprev = 0;
  prevtick = Bid;
  eaname = StringSubstr (EAName, 0, StringFind(EAName," v"));
  custompar = "UNDEF";
  uninitreason = UninitializeReason();
   
  InitLog();
  LogRestartString(EAName);
  LogAccountData();
}

//--------------------------------------------------------------------

bool TsCleanUp()
{
  string StateVarsFileName = GetStateVarsFileName();

  if (freshstart && (uninitreason==1 || uninitreason==5 || uninitreason==2)) {
    LogMsg ("Fresh start, clear everything");
      
    if (! RemoveAllOrders(magicnum[0])) {
      ErrorHandler ("Error in RemoveAllOrders");
      return (false);
    }
      
    FileDelete (StateVarsFileName);
    int Err = GetLastError();
      
    if (Err!=4100 && Err!=4101)
      LogMsg ("Existing state vars file correctly deleted");
    else
      LogMsg ("No state vars file to delete");
  }
  
  return (true);
}

//--------------------------------------------------------------------

bool TakeScreenShot (string Descr)
{
  if (! IsTesting())
  {
    string SSFileName = eaname + "_"+Descr+"_" +symbolstring+ "_" + TimeYear(timecurr) + "_" + TimeMonth(timecurr) + "_" + TimeDay(timecurr) 
                        + "_" + TimeHour(timecurr) + "_" + TimeMinute(timecurr) + "_" + TimeSeconds(timecurr) + ".gif";
    WindowScreenShot (SSFileName, 1920, 1200, -1, -1, CHART_CANDLE);
    LogMsg ("Graph screenshot written to \""+SSFileName+"\"");
  }
  
  return (true);
}

//--------------------------------------------------------------------

void ErrorHandler (string Msg)
{
  LogMsg (Msg);
  Print (Msg);
  errmsg = Msg;
}

//--------------------------------------------------------------------

void EnumOrders ()
{
  int i, j, NOrd, sel;
  
  for (i=0; i<NTS; i++)
  {
    bon[i] = 0; son[i] = 0;
    bsn[i] = 0; ssn[i] = 0;
    bln[i] = 0; sln[i] = 0;
  }

  NOrd = OrdersTotal();
  
  for (i=NOrd-1; i>=0; i--)
  {
    if (! OrderSelect (i, SELECT_BY_POS, MODE_TRADES))
    {
      ErrorHandler ("Ticket error in order selection, mode trades, pos "+i);
      return (-1);
    }
    
    for (j=0; j<NTS; j++)
    {
      if (OrderMagicNumber() == magicnum[j])
      {
        sel = j;
        break;
      }
    }
    
    if ((j==NTS) || (OrderSymbol()!=Symbol()))
      continue;
    
    if (OrderType()==OP_BUY)
    {
      bot[sel][bon[sel]] = OrderTicket();
      bon[sel]++;
    }
    else if (OrderType()==OP_SELL)
    {
      sot[sel][son[sel]] = OrderTicket();
      son[sel]++;
    }
    else if (OrderType()==OP_BUYSTOP)
    {
      bst[sel][bsn[sel]] = OrderTicket();
      bsn[sel]++;
    }
    else if (OrderType()==OP_SELLSTOP)
    {
      sst[sel][ssn[sel]] = OrderTicket();
      ssn[sel]++;
    }
    else if (OrderType()==OP_BUYLIMIT)
    {
      blt[sel][bln[sel]] = OrderTicket();
      bln[sel]++;
    }
    else if (OrderType()==OP_SELLLIMIT)
    {
      slt[sel][sln[sel]] = OrderTicket();
      sln[sel]++;
    }
  }
}

//--------------------------------------------------------------------

void GetState ()
{
  timecurr = TimeCurrent();
  tick = MarketInfo (Symbol(), MODE_TICKSIZE);
  minlot = MarketInfo (Symbol(), MODE_MINLOT);
  maxlot = MarketInfo (Symbol(), MODE_MAXLOT);
  lotval = MarketInfo(Symbol(),MODE_TICKVALUE);
  spread = MarketInfo (Symbol(), MODE_SPREAD);
  stoplevel = MarketInfo (Symbol(), MODE_STOPLEVEL);
    
  if (minlot == 0.01) 
    lotdigits = 2;
  else if (minlot == 0.1)
    lotdigits = 1;
  
  if (tick == 0.01)
    digits = 2;
  else if (tick == 0.001)
    digits = 3;
  else if (tick == 0.0001)
    digits = 4;
  else if (tick == 0.00001)
    digits = 5;
  
  if ((digits==2)||(digits==3))
    pip = 0.01;
  else if ((Digits==4)||(Digits==5))
    pip = 0.0001;
   
  if ((digits==5) || (digits==3))
  {
    spread = spread/10;
    stoplevel = stoplevel/10;
    lotval = lotval*10;
  }
  
  EnumOrders();
  
  return;
}

//--------------------------------------------------------------------

void SaveState ()
{
  int i, j;
  
  timeprev = timecurr;
  prevtick = Bid;
  
  for (i=0; i<NTS; i++)
  {
    bonp[i] = bon[i]; sonp[i] = son[i];
    bsnp[i] = bsn[i]; ssnp[i] = ssn[i];
    blnp[i] = bln[i]; slnp[i] = sln[i];
  
    for (j=0; j<bon[i]; j++)
      botp[i][j] = bot[i][j];

    for (j=0; j<son[i]; j++)
      sotp[i][j] = sot[i][j];

    for (j=0; j<bsn[i]; j++)
      bstp[i][j] = bst[i][j];

    for (j=0; j<ssn[i]; j++)
      sstp[i][j] = sst[i][j];

    for (j=0; j<bln[i]; j++)
      bltp[i][j] = blt[i][j];

    for (j=0; j<sln[i]; j++)
      sltp[i][j] = slt[i][j];
  }

  return;
}

//--------------------------------------------------------------------

bool VerifyError ()
{
  if (errmsg != "")
  {
    if (! IsTesting())
    {
      Comment (errmsg);
      PlaySound ("stops.wav");
    }
    
    return (false);
  }
  
  return (true);
}

//--------------------------------------------------------------------

string dtsp (double Price)
{
  return (DoubleToStr(Price,digits));
}

//--------------------------------------------------------------------

string dtsl (double Lots)
{
  return (DoubleToStr(Lots,lotdigits));
}

//--------------------------------------------------------------------

string dtsa (double Val)
{
  return (DoubleToStr(Val,2));
}

//--------------------------------------------------------------------

string OrderString (double Lots, double Price, double StopLoss, double TakeProfit)
{
  string StringVal = "lots "+dtsl(Lots)+", price "+dtsp(Price)+", sl "+dtsp(StopLoss)+", tp "+dtsp(TakeProfit)+" (bid "+dtsp(Bid)+", ask "+dtsp(Ask)+")"; 
  return (StringVal);
}

//--------------------------------------------------------------------

string OrderStringTicket (int Ticket)
{
  string StringVal;
  
  if (! OrderSelect (Ticket, SELECT_BY_TICKET))
    StringVal = "-";
  else
    StringVal = "lots "+dtsl(OrderLots())+", price "+dtsp(OrderOpenPrice())+", sl "+dtsp(OrderStopLoss())+", tp "+dtsp(OrderTakeProfit())+" (bid "+dtsp(Bid)+", ask "+dtsp(Ask)+")"; 
  
  return (StringVal);
}

//--------------------------------------------------------------------

double ndp (double Price)
{
  return (NormalizeDouble(Price,digits));
}

//--------------------------------------------------------------------

double ndl (double Lots)
{
  return (NormalizeDouble(Lots,lotdigits));
}

//--------------------------------------------------------------------

double ndi (double Val)
{
  return (NormalizeDouble(Val,0));
}

//--------------------------------------------------------------------

double nda (double Val)
{
  return (NormalizeDouble(Val,2));
}

//--------------------------------------------------------------------

bool CheckTickCoherency()
{
  double CurrTick = Bid;
  double PrevTick = prevtick;
  datetime CurrTime = timecurr;
  datetime PrevTime = timeprev;
  double TickDelta = MathAbs(CurrTick-PrevTick)/pip;
  
  if ((TickDelta>tickcoherencypips) && (TimeDay(CurrTime)==TimeDay(PrevTime)))
  {
    LogMsg (LOG_SEP);
    ErrorHandler ("Error, probably corrupted data, current tick "+dtsp(CurrTick)+" ("+TimeToStr(CurrTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+") , prev tick "+dtsp(PrevTick)+" ("+TimeToStr(PrevTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+")");
    return (false);
  }
  
  return (true);
}

//--------------------------------------------------------------------

void PrintComment ()
{
  if (! IsTesting()) 
  {    
    string sComment = eaname+" EA by LorenzoB (trapule@gmail.com)\n";
    sComment = sComment + "RUNNING, custom settings for "+custompar+", current time is " +TimeToStr(TimeCurrent(),TIME_SECONDS)+"\n";
    sComment = sComment + "Log:\n"+latestcommentlines;
    Comment(sComment);  
  }

  return;
}

//--------------------------------------------------------------------

void  PrintInitComment ()
{
  if (! IsTesting()) 
  {    
    string sComment = eaname+" EA by LorenzoB (trapule@gmail.com)\n";
    sComment = sComment + "RUNNING, custom settings for "+custompar+", current time is " +TimeToStr(TimeCurrent(),TIME_SECONDS)+"\n";
    sComment = sComment + "Waiting for first tick...\n";
    Comment(sComment);  
  }
  
  return;
}

//--------------------------------------------------------------------

int CalcMagicNumber (int BaseMagicNumber)
{
  if (timeframeint == 0)
    return (BaseMagicNumber);
  if (timeframeint == PERIOD_M1)
    return (BaseMagicNumber*10);
  else if (timeframeint == PERIOD_M5)
    return (BaseMagicNumber*10+1);
  else if (timeframeint == PERIOD_M15)
    return (BaseMagicNumber*10+2);
  else if (timeframeint == PERIOD_M30)
    return (BaseMagicNumber*10+3);
  else if (timeframeint == PERIOD_H1)
    return (BaseMagicNumber*10+4);
  else if (timeframeint == PERIOD_H4)
    return (BaseMagicNumber*10+5);
  else if (timeframeint == PERIOD_D1)
    return (BaseMagicNumber*10+6);
  else if (timeframeint == PERIOD_W1)
    return (BaseMagicNumber*10+7);
  else if (timeframeint == PERIOD_MN1)
    return (BaseMagicNumber*10+8);

  return (-1);
}

//--------------------------------------------------------------------

bool NewBar ()
{
  if (timeframeint == 0)
    return (false);
    
  datetime BarTime = iTime (Symbol(), timeframeint, 0);
  
  if ((bartimeprev!=0) && (bartimeprev!=BarTime))
  {
    bartimeprev = BarTime;
    return (true);
  }

  bartimeprev = BarTime;
  return (false);
}

//--------------------------------------------------------------------

double CalcFPLots (double EquityRiskPerc, double StopLossPips)
{
  double Lots = AccountEquity()*EquityRiskPerc/100/lotval/StopLossPips;
  
  if (Lots < minlot)
    Lots = minlot;
  else if (Lots > maxlot)
    Lots = maxlot;
    
  return (ndl(Lots));
}

//--------------------------------------------------------------------

bool RefreshAll ()
{
  RefreshRates();
  GetState();
  return (true);
}

//--------------------------------------------------------------------

string GetStateVarsFileName ()
{
  return (eaname + "_statevars_" +symbolstring+".dat");
}

//--------------------------------------------------------------------

bool IsThereStateVarsFile ()
{
  string StateVarsFileName = GetStateVarsFileName();
  int FileHandle = FileOpen (StateVarsFileName, FILE_READ);
  
  if (FileHandle == -1)
    return (false);

  FileClose (FileHandle);
  return (true);    
}

//--------------------------------------------------------------------

bool SaveLibExtPar (int StateVarsLogHandle)
{
  if (FileWriteInteger(StateVarsLogHandle,freshstart,LONG_VALUE) != 4) {
    ErrorHandler ("Error writing freshstart to state vars file");
    return (false);
  }

  if (FileWriteInteger(StateVarsLogHandle,writelog,LONG_VALUE) != 4) {
    ErrorHandler ("Error writing writelog to state vars file");
    return (false);
  }

  if (FileWriteInteger(StateVarsLogHandle,ncommentlines,LONG_VALUE) != 4) {
    ErrorHandler ("Error writing ncommentlines to state vars file");
    return (false);
  }

  if (FileWriteInteger(StateVarsLogHandle,ordersendslippage,LONG_VALUE) != 4) {
    ErrorHandler ("Error writing ordersendslippage to state vars file");
    return (false);
  }

  if (FileWriteInteger(StateVarsLogHandle,tickcoherencypips,LONG_VALUE) != 4) {
    ErrorHandler ("Error writing tickcoherencypips to state vars file");
    return (false);
  }
  
  return (true);
}

//--------------------------------------------------------------------

void ReloadLibExtPar (int StateVarsLogHandle)
{
  freshstart = FileReadInteger(StateVarsLogHandle,LONG_VALUE);
  writelog = FileReadInteger(StateVarsLogHandle,LONG_VALUE);
  ncommentlines = FileReadInteger(StateVarsLogHandle,LONG_VALUE);
  ordersendslippage = FileReadInteger(StateVarsLogHandle,LONG_VALUE);
  tickcoherencypips = FileReadInteger(StateVarsLogHandle,LONG_VALUE);
  return;
}

//--------------------------------------------------------------------

void LogLibExtPar ()
{
  LogMsg ("  freshstart = "+freshstart);
  LogMsg ("  writelog = "+writelog);
  LogMsg ("  ncommentlines = "+ncommentlines);
  LogMsg ("  ordersendslippage = "+ordersendslippage);
  LogMsg ("  tickcoherencypips = "+tickcoherencypips);
  return;
}

//--------------------------------------------------------------------

void LogReloadedLibExtPar ()
{
  LogMsg ("Reloaded freshstart value "+freshstart);
  LogMsg ("Reloaded writelog value "+writelog);
  LogMsg ("Reloaded ncommentlines value "+ncommentlines);
  LogMsg ("Reloaded ordersendslippage value "+ordersendslippage);
  LogMsg ("Reloaded tickcoherencypips value "+tickcoherencypips);
  return;
}

//--------------------------------------------------------------------

bool VerifyLibExternalParameters ()
{
  if (ncommentlines<=0 || ncommentlines>200)
  {
    ErrorHandler ("Invalid parameter value for ncommentlines, "+ncommentlines);
    return (false);
  }

  if (ordersendslippage<0)
  {
    ErrorHandler ("Invalid parameter value for ordersendslippage, "+ordersendslippage);
    return (false);
  }

  if (tickcoherencypips<1)
  {
    ErrorHandler ("Invalid parameter value for tickcoherencypips, "+tickcoherencypips);
    return (false);
  }
  
  return (true);
}

//--------------------------------------------------------------------

bool PreProcessTick ()
{
  tickcount++;
  if (! VerifyError()) return (false);
  GetState();
  if (! CheckTickCoherency()) return (false);
  return (true);
}

//--------------------------------------------------------------------

void PostProcessTick ()
{
  SaveState();
  PrintComment();
}

//--------------------------------------------------------------------

bool SaveIntVal (int FileHandle, int Value, string ValueName)
{
  if (FileWriteInteger(FileHandle,Value,LONG_VALUE) != 4) {
    ErrorHandler ("Error writing "+ValueName+" to file");
    FileClose (FileHandle);
    return (false);
  }
  
  return (true);
}

//--------------------------------------------------------------------

bool SaveDoubleVal (int FileHandle, double Value, string ValueName)
{
  if (FileWriteDouble(FileHandle,Value,DOUBLE_VALUE) != 8) {
    ErrorHandler ("Error writing "+ValueName+" to file");
    FileClose (FileHandle);
    return (false);
  }
  
  return (true);
}

//--------------------------------------------------------------------

bool IsOrderPresent (string Symb, int OpType, double Lots, double PriceValue, double StopLossValue, double TakeProfitValue, int MagicNumber)
{
  int NOrd = OrdersTotal();
  int i;
  
  if (NOrd == 0)
    return (false);
     
  for (i=NOrd-1; i>=0; i--)
  {
    OrderSelect (i, SELECT_BY_POS, MODE_TRADES);

    if ((OrderSymbol()==Symb) && (OrderMagicNumber()==MagicNumber) && (OrderType()==OpType)
       && (OrderLots()==Lots) && (OrderOpenPrice()==PriceValue) && (OrderStopLoss()==StopLossValue)
       && (OrderTakeProfit()==TakeProfitValue))     
    {
      return (true);
    }
  }
  
  return (false);
}

//--------------------------------------------------------------------

int OrderSendSafe (int OpType, double Lots, double Price, double StopLoss, double TakeProfit, int TSIndex)
{
  int Ticket, ErrCode;
  bool bModRes;

  double PriceValue = ndp (Price);
  double StopLossValue = ndp (StopLoss);
  double TakeProfitValue = ndp (TakeProfit);
  double LotsValue = ndl (Lots);
  
  int Slippage;
  
  if (digits==5 || digits==3)
    Slippage = ordersendslippage*10;
  else 
    Slippage = ordersendslippage;

  if (OpType == OP_BUY)
    LogMsg ("Sending buy open order, "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
  else if (OpType == OP_SELL)
    LogMsg ("Sending sell open order, "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
  else if (OpType == OP_BUYSTOP)
    LogMsg ("Sending buy stop order, "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
  else if (OpType == OP_SELLSTOP)
    LogMsg ("Sending sell stop order, "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
  else if (OpType == OP_BUYLIMIT)
    LogMsg ("Sending buy limit order, "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
  else if (OpType == OP_SELLLIMIT)
    LogMsg ("Sending sell limit order, "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));

  if (LotsValue == 0)
  {
    if (OpType == OP_BUY)
      LogMsg ("Skipping buy order (lots=0)");
    else if (OpType == OP_SELL)
      LogMsg ("Skipping sell order (lots=0)");
    if (OpType == OP_BUYSTOP)
      LogMsg ("Skipping buy stop order (lots=0)");
    else if (OpType == OP_SELLSTOP)
      LogMsg ("Skipping sell stop order (lots=0)");
    if (OpType == OP_BUYLIMIT)
      LogMsg ("Skipping buy limit order (lots=0)");
    else if (OpType == OP_SELLLIMIT)
      LogMsg ("Skipping sell limit order (lots=0)");
      
    return (-2);
  }
  else
  {
    if (LotsValue < minlot)
    {
      LotsValue = ndl(minlot);
      LogMsg ("Lots value too low, adjusting value to min allowed "+dtsl(LotsValue));
    }
    else if (Lots > maxlot)
    {
      LotsValue = ndl(maxlot);
      LogMsg ("Lots value too high, adjusting value to max allowed "+dtsl(LotsValue));
    }
  }

  bool OrderOk = false;
  bool LeaveLoop = false;

  while (! LeaveLoop)
  {
    LogMsg ("Starting order send safe, part 1");
    
    if (OpType == OP_BUYSTOP)
    {  
      if (ndp(PriceValue-stoplevel*pip) < ndp(Ask))
      {
        PriceValue = ndp (Ask+stoplevel*pip);
        LogMsg ("Price too near to ask, moving entry price to "+dtsp(PriceValue));
      }
    }
    else if (OpType == OP_SELLSTOP)
    {
      if (ndp(PriceValue+stoplevel*pip) > ndp(Bid))
      {
        PriceValue = ndp (Bid-stoplevel*pip);
        LogMsg ("Price too near to bid, moving entry price to "+dtsp(PriceValue));
      }
    }

    if (OpType == OP_BUY)
    {
      PriceValue = Ask;
      LogMsg ("Set entry price to Ask");
    }
    else if (OpType == OP_SELL)
    {
      PriceValue = Bid;
      LogMsg ("Set entry price to Bid");
    }

    Ticket = OrderSend (Symbol(), OpType, LotsValue, PriceValue, Slippage, 0.0, 0.0, (eaname+" "+symbolstring), magicnum[TSIndex]);
    
    if (Ticket < 0)
    {
      ErrCode = GetLastError();
      
      if (ErrCode == 146)
      {
        LogMsg ("Trade context busy, waiting 1 second...");
        Sleep (1000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 136)
      {
        LogMsg ("Off quotes error, waiting 5 seconds...");
        Sleep (5000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 128)
      {
        LogMsg ("Trade timeout (dangerous), refresh rates immediately...");
        //Sleep (30000);
        //LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 129)
      {
        LogMsg ("Invalid price, refresh rates immediately...");
        RefreshAll();
        
         if (OpType == OP_BUY)
           PriceValue = ndp (Ask);
         else if (OpType == OP_SELL)
           PriceValue = ndp (Bid);
         else
         {
           LogMsg ("Invalid condition");
           return (-1);
         }
         
         LogMsg ("New market price "+dtsp(PriceValue));
      }
      else if (ErrCode == 132)
      {
        LogMsg ("Market closed, waiting 30 seconds...");
        Sleep (30000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 138)
      {
        LogMsg ("Requoted from broker, refresh rates immediately...");
        RefreshAll();
      }
      else if (ErrCode == 6)
      {
        LogMsg ("No connection with trade server, waiting 1 second...");
        Sleep (1000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 2)
      {
        LogMsg ("Common error. All attempts to trade must be stopped until reasons are clarified. Restart of operation system and client terminal will possibly be needed.");
        LogMsg ("Instead, try to repeat operation (very dangerous), waiting 10 seconds...");
        Sleep (10000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 1)
      {
        LogMsg ("No error returned but the result is unknown (dangerous), waiting 2 seconds...");
        Sleep (2000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        LogMsg ("Verify order presence...");
        
        bool Present = IsOrderPresent (Symbol(), OpType, LotsValue, PriceValue, 0.0, 0.0, magicnum[TSIndex]);
        
        if (Present)
        {
          LogMsg ("Order found, continue");
          OrderOk = true;
          LeaveLoop = true;
        }
        else
        {
          LogMsg ("Order not found, try to resend");
        }
      }
      else
      {
        LogMsg ("Error code not managed in order safe send part 1, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+")");
        
        if (ErrCode == 134)
          LogMsg ("Not enough money in the account for this order");
          
        LeaveLoop = true;
      }
    }
    else
    {
      LogMsg ("Order send safe part 1, order correctly sent");
      OrderOk = true;
      LeaveLoop = true;
    }
  }

  if ((StopLossValue!=0.0 || TakeProfitValue!=0.0) && (OrderOk==true))
  {
    OrderOk = false;
    LeaveLoop = false;

    while (! LeaveLoop)
    {
      LogMsg ("Starting order send safe, part 2");
      
      if (OpType == OP_BUY)
      {
        if ((StopLossValue != 0.0) && ((Bid-StopLossValue)/pip<stoplevel))
        {
          StopLossValue = ndp (Bid-stoplevel*pip);
          LogMsg ("Stop loss too near to bid, moving stop loss price to "+dtsp(StopLossValue));
        }
    
        if ((TakeProfitValue != 0.0) && ((-Ask+TakeProfitValue)/pip<stoplevel))
        {
          TakeProfitValue = ndp (Ask+stoplevel*pip);
          LogMsg ("Take profit too near to ask, moving take profit price to "+dtsp(TakeProfitValue));
        }
      }
      else if (OpType == OP_SELL)
      {
        if ((StopLossValue != 0.0) && ((-Ask+StopLossValue)/pip<stoplevel))
        {
          StopLossValue = ndp (Ask+stoplevel*pip);
          LogMsg ("Stop loss too near to ask, moving stop loss price to "+dtsp(StopLossValue));
        }
    
        if ((TakeProfitValue != 0.0) && ((Bid-TakeProfitValue)/pip<stoplevel))
        {
          TakeProfitValue = ndp (Bid-stoplevel*pip);
          LogMsg ("Take profit too near to bid, moving take profit price to "+dtsp(TakeProfitValue));
        }
      }
    
      LogMsg ("Trying to add stop loss ("+dtsp(StopLossValue)+") and take profit ("+dtsp(TakeProfitValue)+"), Bid "+dtsp(Bid)+", Ask "+dtsp(Ask));
      bModRes = OrderModify (Ticket, PriceValue, StopLossValue, TakeProfitValue, 0);
    
      if (bModRes == false)
      {
        ErrCode = GetLastError();
      
        if (ErrCode == 146)
        {
          LogMsg ("Trade context busy, waiting 1 second...");
          Sleep (1000);
          LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else if (ErrCode == 136)
        {
          LogMsg ("Off quotes error, waiting 5 seconds...");
          Sleep (5000);
          LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else if (ErrCode == 128)
        {
          LogMsg ("Trade timeout (dangerous), refresh rates immediately...");
          //Sleep (30000);
          //LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else if (ErrCode == 132)
        {
          LogMsg ("Market closed, waiting 30 seconds...");
          Sleep (30000);
          LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else if (ErrCode == 138)
        {
          LogMsg ("Requoted from broker, refresh rates immediately...");
          LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else if (ErrCode == 6)
        {
          LogMsg ("No connection with trade server, waiting 1 second...");
          Sleep (1000);
          LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else if (ErrCode == 2)
        {
          LogMsg ("Common error. All attempts to trade must be stopped until reasons are clarified. Restart of operation system and client terminal will possibly be needed.");
          LogMsg ("Instead, try to repeat operation (very dangerous), waiting 10 seconds...");
          Sleep (10000);
          LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else if (ErrCode == 130)
        {
          LogMsg ("Invalid stops, refresh rates immediately...");
          LogMsg ("Refresh rates...");
          RefreshAll();
        }
        else
        {
          LogMsg ("Error code not managed in order safe send part 2, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+")");
          LeaveLoop = true;
        }
      }
      else
      {
        LogMsg ("Order send safe part 2, stop loss and/or take profit added successfully");
        OrderOk = true;
        LeaveLoop = true;
      }
    }
  }

  if (OpType == OP_BUY)
  {
    if (OrderOk != true)
    {
      ErrorHandler ("Error in buy open order, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+"), "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
      return (-1);
    }
    else
    {
      LogMsg ("Buy open order #"+Ticket+" executed correctly, "+OrderStringTicket(Ticket));
      return (Ticket);
    }
  }

  if (OpType == OP_SELL)
  {
    if (OrderOk != true)
    {
      ErrorHandler ("Error in sell open order, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+"), "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
      return (-1);
    }
    else
    {
      LogMsg ("Sell open order #"+Ticket+" executed correctly, "+OrderStringTicket(Ticket));
      return (Ticket);
    }
  }

  if (OpType == OP_BUYSTOP)
  {
    if (OrderOk != true)
    {
      ErrorHandler ("Error in buy stop order send, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+"), "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
      return (-1);
    }
    else
    {
      LogMsg ("Buy stop order #"+Ticket+" correctly sent, "+OrderStringTicket(Ticket));
      return (Ticket);
    }
  }

  if (OpType == OP_SELLSTOP)
  {
    if (OrderOk != true)
    {
      ErrorHandler ("Error in sell stop order send, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+"), "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
      return (-1);
    }
    else
    {
      LogMsg ("Sell stop order #"+Ticket+" correctly sent, "+OrderStringTicket(Ticket));
      return (Ticket);
    }
  }

  if (OpType == OP_BUYLIMIT)
  {
    if (OrderOk != true)
    {
      ErrorHandler ("Error in buy limit order send, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+"), "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
      return (-1);
    }
    else
    {
      LogMsg ("Buy limit order #"+Ticket+" correctly sent, "+OrderStringTicket(Ticket));
      return (Ticket);
    }
  }

  if (OpType == OP_SELLLIMIT)
  {
    if (OrderOk != true)
    {
      ErrorHandler ("Error in sell limit order send, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+"), "+OrderString(LotsValue,PriceValue,StopLossValue,TakeProfitValue));
      return (-1);
    }
    else
    {
      LogMsg ("Sell limit order #"+Ticket+" correctly sent, "+OrderStringTicket(Ticket));
      return (Ticket);
    }
  }

  return (-1);
}

//--------------------------------------------------------------------

bool OrderCloseSafe (int Ticket, double Lots, double Price)
{
  double PriceVal = ndp (Price);
  double LotsVal = ndl (Lots);
  bool RetVal;
  int Type, ErrCode;

  LogMsg ("Try to close order #"+Ticket+", lots "+dtsl(LotsVal)+", price "+dtsp(PriceVal)+" (bid "+dtsp(Bid)+", ask "+dtsp(Ask)+")");
  LogMsg ("Order data: "+OrderStringTicket(Ticket));
  
  int Slippage;
  
  if (digits==5 || digits==3)
    Slippage = ordersendslippage*10;
  else 
    Slippage = ordersendslippage;
    
  if (! OrderSelect (Ticket, SELECT_BY_TICKET))
  {
    ErrorHandler ("Invalid ticket in order close, ticket #"+Ticket);
    return (false);
  }

  Type = OrderType();
  
  if ((Type!=OP_BUY) && (Type!=OP_SELL))
  {
    ErrorHandler ("Invalid order type in order close");
    return (false);
  }
  
  bool OrderOk = false;

  while (! OrderOk)
  {
    RetVal = OrderClose (Ticket, LotsVal, PriceVal, Slippage);
    
    if (RetVal != true)
    {
      ErrCode = GetLastError();
      
      if (ErrCode == 128)
      {
        LogMsg ("Trade timeout (dangerous), refresh rates immediately...");
        //Sleep (5000);
        //LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 129)
      {
        LogMsg ("Invalid price, refresh rates immediately...");
        RefreshAll();
        
         if (Type == OP_BUY)
           PriceVal = ndp (Bid);
         else if (Type == OP_SELL)
           PriceVal = ndp (Ask);
         else
         {
           LogMsg ("Invalid condition");
           return (-1);
         }
         
         LogMsg ("New market price "+dtsp(PriceVal));
      }
      else if (ErrCode == 132)
      {
        LogMsg ("Market closed, waiting 30 seconds...");
        Sleep (30000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 136)
      {
        LogMsg ("Off quotes error, waiting 5 seconds...");
        Sleep (5000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 138)
      {
        LogMsg ("Requoted from broker, refresh rates immediately...");
        RefreshAll();
      }
      else if (ErrCode == 146)
      {
        LogMsg ("Trade context busy, waiting 1 second...");
        Sleep (1000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 6)
      {
        LogMsg ("No connection with trade server, waiting 1 second...");
        Sleep (1000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else if (ErrCode == 2)
      {
        LogMsg ("Common error. All attempts to trade must be stopped until reasons are clarified. Restart of operation system and client terminal will possibly be needed.");
        LogMsg ("Instead, try to repeat operation (very dangerous), waiting 10 seconds...");
        Sleep (10000);
        LogMsg ("Refresh rates...");
        RefreshAll();
      }
      else
      {
        ErrorHandler ("Error in order close, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+")");
        return (false);
      }
    }
    else
    {
      OrderOk = true;
    }
  }

  if (Type == OP_BUY)
    LogMsg ("Buy open order #"+Ticket+" correctly closed, lots "+dtsl(LotsVal)+", price "+dtsp(PriceVal));
  else if (Type == OP_SELL)
    LogMsg ("Sell open order #"+Ticket+" correctly closed, lots "+dtsl(LotsVal)+", price "+dtsp(PriceVal));

  return (true);
}

//--------------------------------------------------------------------

bool OrderDeleteSafe (int Ticket)
{
  bool RetVal;
  int Type;
  int ErrCode;
  int Retries;
  
  if (! OrderSelect (Ticket, SELECT_BY_TICKET))
  {
    ErrorHandler ("Invalid ticket in order delete, ticket #"+Ticket);
    return (false);
  }
  
  Type = OrderType();
  
  if ((Type!=OP_BUYSTOP) && (Type!=OP_SELLSTOP) && (Type!=OP_BUYLIMIT) && (Type!=OP_SELLLIMIT))
  {
    ErrorHandler ("Invalid order type in order delete");
    return (false);
  }

  while (true)
  {
    RetVal = OrderDelete (Ticket);
  
    if (RetVal != true)
    {
      ErrCode = GetLastError();
      
      if (ErrCode == 146)
      {
        LogMsg (LOG_SEP);
        
        if (Retries > 1000)
        {
          LogMsg ("Max retries reached");
        }
        else
        {
          LogMsg ("Cannot delete order, error code "+ErrCode+" (context busy)");
          LogMsg ("Wait and retry");
          Retries++;
          Sleep (1000);
          RefreshAll();
          continue;
        }
      }
      else if (ErrCode == 128)
      {
        LogMsg ("Trade timeout (dangerous), refresh rates immediately...");
        //LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 132)
      {
        LogMsg ("Market closed, waiting 30 seconds...");
        Sleep (30000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 136)
      {
        LogMsg ("Off quotes error, waiting 5 seconds...");
        Sleep (5000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 6)
      {
        LogMsg ("No connection with trade server, waiting 1 second...");
        Sleep (1000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 2)
      {
        LogMsg ("Common error. All attempts to trade must be stopped until reasons are clarified. Restart of operation system and client terminal will possibly be needed.");
        LogMsg ("Instead, try to repeat operation (very dangerous), waiting 10 seconds...");
        Sleep (10000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }

      ErrorHandler ("Error in order delete, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+")");
      return (false);
    }

    break;
  }
    
  if (Type == OP_BUYSTOP)
    LogMsg ("Buy stop order #"+Ticket+" correctly deleted");
  else if (Type == OP_SELLSTOP)
    LogMsg ("Sell stop order #"+Ticket+" correctly deleted");
  else if (Type == OP_BUYLIMIT)
    LogMsg ("Buy limit order #"+Ticket+" correctly deleted");
  else if (Type == OP_SELLLIMIT)
    LogMsg ("Sell limit order #"+Ticket+" correctly deleted");
      
  return (true);
}

//--------------------------------------------------------------------

bool OrderModifySafe (int Ticket, double Price, double StopLoss, double TakeProfit)
{
  double PriceVal = ndp (Price);
  double StopLossVal = ndp (StopLoss);
  double TakeProfitVal = ndp (TakeProfit);
  bool RetVal;
  int Type, ErrCode;
  
  if (! OrderSelect (Ticket, SELECT_BY_TICKET))
  {
    ErrorHandler ("Invalid ticket in order close, ticket #"+Ticket);
    return (false);
  }
  
  double LotsVal = ndl (OrderLots());
  Type = OrderType();

  LogMsg ("Try to modify order #"+Ticket+", price "+dtsp(PriceVal)+", stop loss "+dtsp(StopLossVal)+", take profit "+dtsp(TakeProfitVal)+" (Bid "+dtsp(Bid)+", Ask "+dtsp(Ask)+")");

  if (Type == OP_BUY)
  {
    if ((ndp(Bid)-StopLossVal)/pip <= stoplevel)
    {
      StopLossVal = ndp(Bid-stoplevel*pip);
      LogMsg ("Stop too near to current price, move stop to nearest allowed value "+dtsp(StopLossVal));
    }
  }
  else if (Type == OP_SELL)
  {
    if ((-ndp(Ask)+StopLossVal)/pip <= stoplevel)
    {
      StopLossVal = ndp(Ask+stoplevel*pip);
      LogMsg ("Stop too near to current price, move stop to nearest allowed value "+dtsp(StopLossVal));
    }
  }
  
  while (true)
  {
    RetVal = OrderModify (Ticket, PriceVal, StopLossVal, TakeProfitVal, 0);
  
    if (RetVal != true)
    {
      ErrCode = GetLastError();

      if (ErrCode == 130)
      {
        LogMsg ("Invalid stops, refresh rates immediately and recalculate stops...");
        RefreshAll();

        if (Type == OP_BUY)
        {
          if ((ndp(Bid)-StopLossVal)/pip <= stoplevel)
          {
            StopLossVal = ndp(Bid-stoplevel*pip);
            LogMsg ("Stop too near to current price, move stop to nearest allowed value "+dtsp(StopLossVal));
          }
        }
        else if (Type == OP_SELL)
        {
          if ((-ndp(Ask)+StopLossVal)/pip <= stoplevel)
          {
            StopLossVal = ndp(Ask+stoplevel*pip);
            LogMsg ("Stop too near to current price, move stop to nearest allowed value "+dtsp(StopLossVal));
          }
        }        

        continue;
      }
      else if (ErrCode == 128)
      {
        LogMsg ("Trade timeout (dangerous), refresh rates immediately...");
        //LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 132)
      {
        LogMsg ("Market closed, waiting 30 seconds...");
        Sleep (30000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 136)
      {
        LogMsg ("Off quotes error, waiting 5 seconds...");
        Sleep (5000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 146)
      {
        LogMsg ("Trade context busy, waiting 1 second...");
        Sleep (1000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 6)
      {
        LogMsg ("No connection with trade server, waiting 1 second...");
        Sleep (1000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 2)
      {
        LogMsg ("Common error. All attempts to trade must be stopped until reasons are clarified. Restart of operation system and client terminal will possibly be needed.");
        LogMsg ("Instead, try to repeat operation (very dangerous), waiting 10 seconds...");
        Sleep (10000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      else if (ErrCode == 1)
      {
        LogMsg ("No error returned but the result is unknown (dangerous), waiting 2 seconds...");
        LogMsg ("We suppose the order has not been modified, retry (dangerous)");
        Sleep (2000);
        LogMsg ("Refresh rates...");
        RefreshAll();
        continue;
      }
      
      ErrorHandler ("Error in order modify, error code " +ErrCode+ " (" +GetErrorCodeDescription(ErrCode)+"), Ticket "+Ticket+", PriceVal "+dtsp(PriceVal)+", StopLossVal "+dtsp(StopLossVal)+", TakeProfitVal "+dtsp(TakeProfitVal));
      return (false);
    }

    if (Type == OP_BUY)
      LogMsg ("Buy open order #"+Ticket+" correctly modified, "+OrderStringTicket(Ticket));
    else if (Type == OP_SELL)
      LogMsg ("Sell open order #"+Ticket+" correctly modified, "+OrderStringTicket(Ticket));
    else if (Type == OP_BUYSTOP)
      LogMsg ("Buy stop order #"+Ticket+" correctly modified, "+OrderStringTicket(Ticket));
    else if (Type == OP_SELLSTOP)
      LogMsg ("Sell stop order #"+Ticket+" correctly modified, "+OrderStringTicket(Ticket));
    else if (Type == OP_BUYLIMIT)
      LogMsg ("Buy limit order #"+Ticket+" correctly modified, "+OrderStringTicket(Ticket));
    else if (Type == OP_SELLLIMIT)
      LogMsg ("Sell limit order #"+Ticket+" correctly modified, "+OrderStringTicket(Ticket));

    return (true);
  }
}

//--------------------------------------------------------------------

bool RemoveAllOrders (int MagicNumber)
{
  int NOrd = OrdersTotal();
  int i;
  
  if (NOrd > 0)
    LogMsg ("Remove all orders");
  else
  {
    LogMsg ("No orders to remove");
    return (true);
  }
    
  for (i=NOrd-1; i>=0; i--)
  {
    if (! OrderSelect (i, SELECT_BY_POS, MODE_TRADES))
    {
      ErrorHandler ("Ticket error in order selection, mode trades, pos "+i);
      return (-1);
    }

    if ((OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber))
    {
      if (OrderType()==OP_BUY)
      {
        if (! OrderCloseSafe (OrderTicket(), OrderLots(), Bid))
          return (false);
      }
      else if (OrderType()==OP_SELL)
      {
        if (! OrderCloseSafe (OrderTicket(), OrderLots(), Ask))
          return (false);
      }
      else if (OrderType()==OP_BUYSTOP)
      {
        if (! OrderDeleteSafe (OrderTicket()))
          return (false);
      }
      else if (OrderType()==OP_SELLSTOP)
      {
        if (! OrderDeleteSafe (OrderTicket()))
          return (false);
      }
      else if (OrderType()==OP_BUYLIMIT)
      {
        if (! OrderDeleteSafe (OrderTicket()))
          return (false);
      }
      else if (OrderType()==OP_SELLLIMIT)
      {
        if (! OrderDeleteSafe (OrderTicket()))
          return (false);
      }
    }
  }

  GetState();
  LogMsg ("All existing orders correctly removed");  
  return (true);
}

//--------------------------------------------------------------------

bool TrailStopLoss (int Ticket, double StopLossPips, double BreakEvenStopLossPips)
{
  if (! OrderSelect (Ticket, SELECT_BY_TICKET))
  {
    ErrorHandler ("Invalid ticket for order select");
    return (false);
  }
  
  if (StopLossPips == 0.0)
  {
    ErrorHandler ("Cannot trail stop loss, StopLossPips parameter is 0.0");
    return (false);
  }
  
  if (OrderType() == OP_BUY)
  {
    if ((BreakEvenStopLossPips != 0.0) && (OrderStopLoss() < OrderOpenPrice()) && (Ask-OrderOpenPrice() >= BreakEvenStopLossPips*pip))
    {
      LogMsg ("Trail stop loss to breakeven (prev "+dtsp(OrderStopLoss())+", new "+dtsp(OrderOpenPrice()+1*pip)+")");

      if (! OrderModifySafe(Ticket,OrderOpenPrice(),OrderOpenPrice()+1*pip,OrderTakeProfit()))
        return (false);
        
      LogMsg ("Stop loss correctly trailed");
    }
    
    if ((Ask-OrderStopLoss())>(StopLossPips+1)*pip)
    {
      LogMsg ("Try to trail stop loss (prev "+dtsp(OrderStopLoss())+", new "+dtsp(Ask-StopLossPips*pip)+")");

      if (! OrderModifySafe(Ticket,OrderOpenPrice(),Ask-StopLossPips*pip,OrderTakeProfit()))
        return (false);
        
      LogMsg ("Stop loss correctly trailed");
    }
  }
  else if (OrderType() == OP_SELL)
  {
    if ((BreakEvenStopLossPips != 0.0) && (OrderStopLoss() > OrderOpenPrice()) && (-Bid+OrderOpenPrice() >= BreakEvenStopLossPips*pip))
    {
      LogMsg ("Trail stop loss to breakeven (prev "+dtsp(OrderStopLoss())+", new "+dtsp(OrderOpenPrice()-1*pip)+")");

      if (! OrderModifySafe(Ticket,OrderOpenPrice(),OrderOpenPrice()-1*pip,OrderTakeProfit()))
        return (false);
        
      LogMsg ("Stop loss correctly trailed");
    }

    if ((OrderStopLoss()-Bid)>(StopLossPips+1)*pip)
    {
      LogMsg ("Try to trail stop loss (prev "+dtsp(OrderStopLoss())+", new "+dtsp(Bid+StopLossPips*pip)+")");

      if (! OrderModifySafe(Ticket,OrderOpenPrice(),Bid+StopLossPips*pip,OrderTakeProfit()))
        return (false);
        
      LogMsg ("Stop loss correctly trailed");
    }
  }
  else
  {
    ErrorHandler ("Invalid order type for stop loss trail");
    return (false);
  }
  
  return (true);
}
 
//--------------------------------------------------------------------

string GetErrorCodeDescription (int ErrorCode)
{
  if (ErrorCode == 0) return ("No error returned");
  if (ErrorCode == 1) return ("No error returned, but the result is unknown");
  if (ErrorCode == 2) return ("Common error");
  if (ErrorCode == 3) return ("Invalid trade parameters");
  if (ErrorCode == 4) return ("Trade server is busy");
  if (ErrorCode == 5) return ("Old version of the client terminal");
  if (ErrorCode == 6) return ("No connection with trade server");
  if (ErrorCode == 7) return ("Not enough rights");
  if (ErrorCode == 8) return ("Too frequent requests");
  if (ErrorCode == 9) return ("Malfunctional trade operation");
  if (ErrorCode == 64) return ("Account disabled");
  if (ErrorCode == 65) return ("Invalid account");
  if (ErrorCode == 128) return ("Trade timeout");
  if (ErrorCode == 129) return ("Invalid price");
  if (ErrorCode == 130) return ("Invalid stops");
  if (ErrorCode == 131) return ("Invalid trade volume");
  if (ErrorCode == 132) return ("Market is closed");
  if (ErrorCode == 133) return ("Trade is disabled");
  if (ErrorCode == 134) return ("Not enough money");
  if (ErrorCode == 135) return ("Price changed");
  if (ErrorCode == 136) return ("Off quotes");
  if (ErrorCode == 137) return ("Broker is busy");
  if (ErrorCode == 138) return ("Requote");
  if (ErrorCode == 139) return ("Order is locked");
  if (ErrorCode == 140) return ("Long positions only allowed");
  if (ErrorCode == 141) return ("Too many requests");
  if (ErrorCode == 145) return ("Modification denied because order too close to market");
  if (ErrorCode == 146) return ("Trade context is busy");
  if (ErrorCode == 147) return ("Expirations are denied by broker");
  if (ErrorCode == 148) return ("The amount of open and pending orders has reached the limit set by the broker");
  if (ErrorCode == 149) return ("An attempt to open a position opposite to the existing one when hedging is disabled");
  if (ErrorCode == 150) return ("An attempt to close a position contravening the FIFO rule");
  if (ErrorCode == 4000) return ("No error");
  if (ErrorCode == 4001) return ("Wrong function pointer");
  if (ErrorCode == 4002) return ("Array index is out of range");
  if (ErrorCode == 4003) return ("No memory for function call stack");
  if (ErrorCode == 4004) return ("Recursive stack overflow");
  if (ErrorCode == 4005) return ("Not enough stack for parameter");
  if (ErrorCode == 4006) return ("No memory for parameter string");
  if (ErrorCode == 4007) return ("No memory for temp string");
  if (ErrorCode == 4008) return ("Not initialized string");
  if (ErrorCode == 4009) return ("Not initialized string in array");
  if (ErrorCode == 4010) return ("No memory for array string");
  if (ErrorCode == 4011) return ("Too long string");
  if (ErrorCode == 4012) return ("Remainder from zero divide");
  if (ErrorCode == 4013) return ("Zero divide");
  if (ErrorCode == 4014) return ("Unknown command");
  if (ErrorCode == 4015) return ("Wrong jump (never generated error)");
  if (ErrorCode == 4016) return ("Not initialized array");
  if (ErrorCode == 4017) return ("DLL calls are not allowed");
  if (ErrorCode == 4018) return ("Cannot load library");
  if (ErrorCode == 4019) return ("Cannot call function");
  if (ErrorCode == 4020) return ("Expert function calls are not allowed");
  if (ErrorCode == 4021) return ("Not enough memory for temp string returned from function");
  if (ErrorCode == 4022) return ("System is busy (never generated error)");
  if (ErrorCode == 4050) return ("Invalid function parameters count");
  if (ErrorCode == 4051) return ("Invalid function parameter value");
  if (ErrorCode == 4052) return ("String function internal error");
  if (ErrorCode == 4053) return ("Some array error");
  if (ErrorCode == 4054) return ("Incorrect series array using");
  if (ErrorCode == 4055) return ("Custom indicator error");
  if (ErrorCode == 4056) return ("Arrays are incompatible");
  if (ErrorCode == 4057) return ("Global variables processing error");
  if (ErrorCode == 4058) return ("Global variable not found");
  if (ErrorCode == 4059) return ("Function is not allowed in testing mode");
  if (ErrorCode == 4060) return ("Function is not confirmed");
  if (ErrorCode == 4061) return ("Send mail error");
  if (ErrorCode == 4062) return ("String parameter expected");
  if (ErrorCode == 4063) return ("Integer parameter expected");
  if (ErrorCode == 4064) return ("Double parameter expected");
  if (ErrorCode == 4065) return ("Array as parameter expected");
  if (ErrorCode == 4066) return ("Requested history data in updating state");
  if (ErrorCode == 4067) return ("Some error in trading function");
  if (ErrorCode == 4099) return ("End of file");
  if (ErrorCode == 4100) return ("Some file error");
  if (ErrorCode == 4101) return ("Wrong file name");
  if (ErrorCode == 4102) return ("Too many opened files");
  if (ErrorCode == 4103) return ("Cannot open file");
  if (ErrorCode == 4104) return ("Incompatible access to a file");
  if (ErrorCode == 4105) return ("No order selected");
  if (ErrorCode == 4106) return ("Unknown symbol");
  if (ErrorCode == 4107) return ("Invalid price");
  if (ErrorCode == 4108) return ("Invalid ticket");
  if (ErrorCode == 4109) return ("Trade is not allowed. Enable checkbox \"Allow live trading\" in the expert properties");
  if (ErrorCode == 4110) return ("Longs are not allowed. Check the expert properties");
  if (ErrorCode == 4111) return ("Shorts are not allowed. Check the expert properties");
  if (ErrorCode == 4200) return ("Object exists already");
  if (ErrorCode == 4201) return ("Unknown object property");
  if (ErrorCode == 4202) return ("Object does not exist");
  if (ErrorCode == 4203) return ("Unknown object type");
  if (ErrorCode == 4204) return ("No object name");
  if (ErrorCode == 4205) return ("Object coordinates error");
  if (ErrorCode == 4206) return ("No specified subwindow");
  if (ErrorCode == 4207) return ("Some error in object function");
  return ("Not a known error code");
}

//--------------------------------------------------------------------

// EOF

