//+------------------------------------------------------------------+
//| NZDUSD WFM1000 0.109322 - WF R4 45P EA
//|
//| Generated by StrategyQuant X Build 126 at 05/17/2020 08:25
//|
//| This EA uses 3rd party library OrderReliable for industry-strength 
//| order handling. OrderReliable library is normally distributed under 
//| GPL license. Its author provided it under MIT license limited to 
//| usage only within strategies generated by StrategyQuant.  
//|   
//| This means that this library doesn't restrict you to use your EA 
//| generated by SQ comercially in any way you want.  
//| But if you'll use any method from OrderReliable library outside 
//| of this EA in your own EAs or projects, its use will be governed 
//| by its standard GPL license.  
//| 
//| Scroll to the very bottom of this source code for the full text 
//| of OrderReliable license. 
//+------------------------------------------------------------------+

#property copyright "StrategyQuant.com"
#property link      "http://www.StrategyQuant.com"

#include <stdlib.mqh>
#include <stderror.mqh>

const int SLPTTYPE_RANGE = 0;
const int SLPTTYPE_LEVEL = 1;

//+------------------------------------------------------------------+
// -- Variables
//+------------------------------------------------------------------+
extern string CustomComment = "NZDUSD WFM1000 0.109322 - WF R4 45P";

extern int MagicNumber = 2028007;
bool LongEntrySignal = false;
bool ShortEntrySignal = false;
bool LongExitSignal = false;
bool ShortExitSignal = false;

//+------------------------------------------------------------------+
// Money Management variables
//+------------------------------------------------------------------+
extern string smm = "----------- Money Management - Fixed Amount -----------";
extern double mmRiskedMoney = 294;
extern int mmDecimals = 2;
extern double mmLotsIfNoMM = 0;
extern double mmMaxLots = 1.0;


//+------------------------------------------------------------------+
// Trading Options variables
//+------------------------------------------------------------------+

extern string seod = "----------- Exit At End Of Day -----------";
extern bool ExitAtEndOfDay = false;
extern string EODExitTime = "23:00";

extern string seof = "----------- Exit On Friday -----------";
extern bool ExitOnFriday = true;
extern string FridayExitTime = "23:00";

extern string sltr = "----------- Limit Time Range -----------";
extern bool LimitTimeRange = true;
extern string SignalTimeRangeFrom = "01:00";
extern string SignalTimeRangeTo = "23:15";
extern bool ExitAtEndOfRange = false;

extern string smtpd = "----------- Max Trades Per Day -----------";
extern int MaxTradesPerDay = 0;
extern string smmslpt = "----------- Min/Max SL/PT -----------";
extern int MinimumSL = 0;   //Minimum SL in pips
extern int MinimumPT = 0;   //Minimum PT in pips
extern int MaximumSL = 0;   //Maximum SL in pips
extern int MaximumPT = 0;   //Maximum PT in pips


      
extern string slts = "----------- Use Tick size from SQ (for CFDs) -----------";
// For exotic pairs (usually non forex CFDs) the default method of computing 
// tick size in EA might not work correctly.
// By turning this on and specifying MainChartTickSizeSQ value you can let EA 
// use the correct tick size          
extern bool UseSQTickSize = false;                                                                             
extern double MainChartTickSizeSQ = 1.0E-4;

//+------------------------------------------------------------------+
// OrderReliable library variables
//+------------------------------------------------------------------+
string 	OrderReliableVersion = "v36";
string 	OrderReliable_Fname = "OrderReliable (Function Name Not Set)";

int 	retry_attempts 		      = 10000; // general retry attempts
int 	retry_attempts_bad_price   = 3;  // retry attempts if the error is bad price
double 	sleep_time 			      = 2.0;
double 	sleep_maximum 		      = 20.0;


int 	ErrorLevel 			= 3;

bool	UseLimitToMarket 	= false;
bool	UseForTesting 		= false;
bool	AddSpreadToComment	= false;

//+------------------------------------------------------------------+
// -- SQ internal variables
// add word "extern" in front of the variable you want
// to make configurable
//+------------------------------------------------------------------+
int sqMaxEntrySlippage = 5;          //Max tolerated entry slippage in pips. Zero means unlimited slippage
int sqMaxCloseSlippage = 0;          //Max tolerated close slippage in pips. Zero means unlimited slippage       
bool autoCorrectMaxSlippage = true;  //If set to true, it will automatically adjust max slippage according to symbol digits (*10 for 3 and 5 digit symbols)  

//+------------------------------------------------------------------+
// Verbose mode values:
// 0 - don't print messages to log at all
// 1 - print messages to log only when trading (not when backtesting)
// 2 - print messages to log always
//+------------------------------------------------------------------+
int sqVerboseMode = 2;

extern bool sqDisplayInfoPanel = true;
extern bool ModifyInsteadOfReplacing = false;

extern int OpenBarDelay = 0; // open bar delay in minutes
// it can be used for Daily strategies to trigger trading a few minutes later -
// because brokers sometimes have technical delay after midnight and we have to postpone order execution

int sqLabelCorner = 1;
int sqOffsetHorizontal = 5;
int sqOffsetVertical = 20;
color sqLabelColor = White;

datetime _sqLastOpenBarTime = 0;
bool _sqIsBarOpen = false;
double gPointCoef = 0;
int _ticket;                     
bool cond[100];

double initialBalance = 0;                    
    
bool openingOrdersAllowed = true;
bool firstCall = true;

////////
//+------------------------------------------------------------------+
// -- Functions
//+------------------------------------------------------------------+

void OnTick() {

   sqInitStart();       
   
   sqManageOrders(MagicNumber);
   
   openingOrdersAllowed = sqHandleTradingOptions();

   //------------------------
   // Rule: Trading signals
   //------------------------
   if (sqIsBarOpen()) {
     // init signals only on bar open 
     LongEntrySignal = ((sqIsRising("sqOsMA(NULL,0, 114, 164, 215, PRICE_HIGH, 1)", 5, true, 6))
      && (sqIsFalling("sqKeltnerChannel(NULL,0, 120, 0.51, 1, 6)", 11, true, 6)));

     ShortEntrySignal = ((sqIsFalling("sqOsMA(NULL,0, 153, 266, 215, PRICE_LOW, 1)", 5, true, 6))
      && (sqIsRising("sqKeltnerChannel(NULL,0, 120, 0.51, 0, 6)", 11, true, 6)));

     LongExitSignal = false;

     ShortExitSignal = false;

   } 


                
   //------------------------
   // Rule: Long entry
   //------------------------
   if (sqIsBarOpen()
      && LongEntrySignal)
   {
      // Action #1
      _ticket = sqOpenOrder(OP_BUYSTOP, "Current", sqMMFixedAmount("Current",OP_BUYSTOP,(sqMA(NULL,0, 104, 0, MODE_SMMA, PRICE_MEDIAN, 3) - (0.7 * sqATR(NULL,0, 68, 4))),sqGetSLLevel("Current", OP_BUYSTOP, (sqMA(NULL,0, 104, 0, MODE_SMMA, PRICE_MEDIAN, 3) - (0.7 * sqATR(NULL,0, 68, 4))), 2, 15.2 * sqATR(NULL,0,552,1)),mmRiskedMoney,mmDecimals,mmLotsIfNoMM,mmMaxLots), (sqMA(NULL,0, 104, 0, MODE_SMMA, PRICE_MEDIAN, 3) - (0.7 * sqATR(NULL,0, 68, 4))), MagicNumber, "", 0, true, false, CLR_NONE);

      if(_ticket > 0 && OrderSelect(_ticket, SELECT_BY_TICKET)) {
         // set or initialize all order exit methods (SL, PT, Trailing Stop, Exit After Bars, etc.)
         sqSetOrderExpiration(_ticket, 3);

         // StopLoss & ProfitTarget
         sqSetSLandPT(_ticket, sqGetSLLevel("Current", OP_BUYSTOP, OrderOpenPrice(), 2, 15.2 * sqATR(NULL,0,552,1)), sqGetPTLevel("Current", OP_BUYSTOP, OrderOpenPrice(), 2, 22.31 * sqATR(NULL,0,1020,1)));

      }



  }

                
   //------------------------
   // Rule: Short entry
   //------------------------
   if (sqIsBarOpen()
      && (ShortEntrySignal
      && (!(LongEntrySignal))))
   {
      // Action #1
      _ticket = sqOpenOrder(OP_SELLSTOP, "Current", sqMMFixedAmount("Current",OP_SELLSTOP,(sqMA(NULL,0, 104, 0, MODE_SMMA, PRICE_MEDIAN, 3) + (0.7 * sqATR(NULL,0, 68, 4))),sqGetSLLevel("Current", OP_SELLSTOP, (sqMA(NULL,0, 104, 0, MODE_SMMA, PRICE_MEDIAN, 3) + (0.7 * sqATR(NULL,0, 68, 4))), 2, 15.2 * sqATR(NULL,0,552,1)),mmRiskedMoney,mmDecimals,mmLotsIfNoMM,mmMaxLots), (sqMA(NULL,0, 104, 0, MODE_SMMA, PRICE_MEDIAN, 3) + (0.7 * sqATR(NULL,0, 68, 4))), MagicNumber, "", 0, true, false, CLR_NONE);

      if(_ticket > 0 && OrderSelect(_ticket, SELECT_BY_TICKET)) {
         // set or initialize all order exit methods (SL, PT, Trailing Stop, Exit After Bars, etc.)

         // StopLoss & ProfitTarget
         sqSetSLandPT(_ticket, sqGetSLLevel("Current", OP_SELLSTOP, OrderOpenPrice(), 2, 15.2 * sqATR(NULL,0,552,1)), sqGetPTLevel("Current", OP_SELLSTOP, OrderOpenPrice(), 2, 22.31 * sqATR(NULL,0,1020,1)));

      }



  }

                
   //------------------------
   // Rule: Long exit
   //------------------------
   if (sqIsBarOpen()
      && ((LongExitSignal
      && (!(LongEntrySignal)))
      && sqMarketPositionIsLong(MagicNumber, "Any", "")))
   {
      // Action #1
      sqClosePosition(OrderLots(), // size: SQ.Formulas.CloseSize.FullPosition
                    MagicNumber, // magic number
                    "Any", // symbol
                    1, // direction
                    "" // comment
       );

  }

                
   //------------------------
   // Rule: Short exit
   //------------------------
   if (sqIsBarOpen()
      && ((ShortExitSignal
      && (!(ShortEntrySignal)))
      && sqMarketPositionIsShort(MagicNumber, "Any", "")))
   {
      // Action #1
      sqClosePosition(OrderLots(), // size: SQ.Formulas.CloseSize.FullPosition
                    MagicNumber, // magic number
                    "Any", // symbol
                    -1, // direction
                    "" // comment
       );

  }

                
   return;
}

//+------------------------------------------------------------------+

int OnInit() {
   VerboseLog("--------------------------------------------------------");
   VerboseLog("Starting the EA");

   gPointCoef = calculatePointCoef(Symbol());

   VerboseLog("--------------------------------------------------------");

   if(sqDisplayInfoPanel) {
     sqInitInfoPanel();
   }

   SQTime = new CSQTime();

   if(!IsTesting() && !IsOptimization()) {                                         
      initTimer();
   }
           
   initialBalance = AccountInfoDouble(ACCOUNT_BALANCE);
   
   objExitAtEndOfDay = new CExitAtEndOfDay();
   objExitOnFriday = new CExitOnFriday();
   objLimitTimeRange = new CLimitTimeRange();
   objMaxTradesPerDay = new CMaxTradesPerDay();
   objMinMaxSLPT = new CMinMaxSLPT();
   
   
   return(0);
}

//+------------------------------------------------------------------+

void OnDeinit(const int reason) {
   sqDeinitInfoPanel();
   
   delete SQTime;
   delete objExitAtEndOfDay;
   delete objExitOnFriday;
   delete objLimitTimeRange;
   delete objMaxTradesPerDay;
   delete objMinMaxSLPT;
   
   return;
}
    
//+------------------------------------------------------------------+

void initTimer(){
   int period = 20 * 3600;                //20 hours
   period += MathRand() % (10 * 3600);    //add another 0-10 hours randomly

   if(!EventSetTimer(period)){
      VerboseLog("Cannot set timer. Error code: ", GetLastError());
   }
}

//+------------------------------------------------------------------+

void OnTimer(){
   //clear unused variables
   int deletedCount = 0;
   
   VerboseLog("Clearing variables...");
   
   for(int a=GlobalVariablesTotal() - 1; a>=0; a--){
      string variableName = GlobalVariableName(a);
      
      if(GlobalVariableCheck(variableName)){
         int endPos = StringFind(variableName, "_");
         if(endPos <= 0) continue;
         
         int ticketNo = StrToInteger(StringSubstr(variableName, 0, endPos));
         
         bool variableUsed = false;
         
         for(int i=0; i < OrdersTotal(); i++) {
            if(OrderSelect(i, SELECT_BY_POS) == true) {
               if(OrderTicket() == ticketNo){
                  variableUsed = true;
                  break;
               }
            }
         }
         
         ResetLastError();
         
         if(!variableUsed){
            if(GlobalVariableDel(variableName)){
               deletedCount++;
            }
            else {
               VerboseLog("Cannot delete variable. Error code: ", GetLastError());
            }
         }
      }
   }
   
   VerboseLog(IntegerToString(deletedCount), " variables cleared");
}

//+------------------------------------------------------------------+

bool sqHandleTradingOptions() {
   bool continueWithBarUpdate = true;

   if(!objExitAtEndOfDay.onBarUpdate()) continueWithBarUpdate = false;
   if(!objExitOnFriday.onBarUpdate()) continueWithBarUpdate = false;
   if(!objLimitTimeRange.onBarUpdate()) continueWithBarUpdate = false;
   if(!objMaxTradesPerDay.onBarUpdate()) continueWithBarUpdate = false;
   if(!objMinMaxSLPT.onBarUpdate()) continueWithBarUpdate = false;

   return(continueWithBarUpdate);
}   

//+------------------------------------------------------------------+

bool checkMagicNumber(int magicNo){
    if(magicNo == MagicNumber){
         return true;
    }
    return false;
}

//+------------------------------------------------------------------+

double sqGetMarketTickSize(string symbol){
   symbol = correctSymbol(symbol);
    
      if(symbol == Symbol()){
         return MainChartTickSizeSQ;
      }
    
   
   return -1;
}

//+------------------------------------------------------------------+

double sqGetGlobalSL(string symbol, int orderType, double price) {
   return(sqGetSLLevel(symbol, orderType, price, 1, 0));
}

//+------------------------------------------------------------------+

double sqGetGlobalPT(string symbol, int orderType, double price) {
   return(sqGetPTLevel(symbol, orderType, price, 1, 0));
}

//+------------------------------------------------------------------+

double sqGetIndicatorByIdentification(string idHash, int shift) {
   if(idHash == "sqOsMA(NULL,0, 114, 164, 215, PRICE_HIGH, 1)") {
      return (sqOsMA(NULL,0, 114, 164, 215, PRICE_HIGH, 1+shift));
   }
   if(idHash == "sqKeltnerChannel(NULL,0, 120, 0.51, 1, 6)") {
      return (sqKeltnerChannel(NULL,0, 120, 0.51, 1, 6+shift));
   }
   if(idHash == "sqOsMA(NULL,0, 153, 266, 215, PRICE_LOW, 1)") {
      return (sqOsMA(NULL,0, 153, 266, 215, PRICE_LOW, 1+shift));
   }
   if(idHash == "sqKeltnerChannel(NULL,0, 120, 0.51, 0, 6)") {
      return (sqKeltnerChannel(NULL,0, 120, 0.51, 0, 6+shift));
   }

   return(0);
}


//+------------------------------------------------------------------+

double sqGetValueByIdentification(string idHash) {

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   if(idHash == sqStringHash("22.31 * sqATR(NULL,0,1020,1)")) {
      return (22.31 * sqATR(NULL,0,1020,1));
   }

   if(idHash == sqStringHash("15.2 * sqATR(NULL,0,552,1)")) {
      return (15.2 * sqATR(NULL,0,552,1));
   }

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   if(idHash == sqStringHash("22.31 * sqATR(NULL,0,1020,1)")) {
      return (22.31 * sqATR(NULL,0,1020,1));
   }

   if(idHash == sqStringHash("15.2 * sqATR(NULL,0,552,1)")) {
      return (15.2 * sqATR(NULL,0,552,1));
   }

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   if(idHash == sqStringHash("0")) {
      return (0);
   }

   return(0);
}

//----------------------------------------------------------------------------

void sqManageOrders(int magicNumber) {
   if(_sqIsBarOpen) {
      for(int i=OrdersTotal()-1; i>=0; i--) {
         if (OrderSelect(i,SELECT_BY_POS)==true) {
            if(OrderMagicNumber() != magicNumber) {
               continue;
            }  
               
            int ticket = OrderTicket();

            if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
               // handle Exit Methods only for live orders

               sqManageSL2BE(ticket);
               sqManageTrailingStop(ticket);            
               sqManageExitAfterXBars(ticket);               
            }

            sqManageOrderExpiration(ticket);
         }

         if(OrdersTotal() <= 0) return;
      }
   }
}

//----------------------------------------------------------------------------

void sqResetGlobalVariablesForTicket(int ticket) {
   sqSetGlobalVariable(ticket, "sqOrderExpiration", 0);
   sqSetGlobalVariable(ticket, "sqOrderOpenTime", Time[0]);

   sqSetGlobalVariable(ticket, "MoveSL2BE", 0);    
   sqSetGlobalVariable(ticket, "SL2BEAddPips", 0);
   sqSetGlobalVariable(ticket, "TrailingStop", 0);       
   sqSetGlobalVariable(ticket, "TrailingActivation", 0);
   sqSetGlobalVariable(ticket, "ExitAfterBars", 0);
  
}

//+------------------------------------------------------------------+

void sqInitStart() {
   // changed recognition of bar open to support also range/renko charts
   if(_sqLastOpenBarTime == 0) {
      _sqLastOpenBarTime = Time[0];
      _sqIsBarOpen = true;
   } 
   else {
      if(_sqLastOpenBarTime != Time[0]) {
        bool processBarOpen = true;

        if(OpenBarDelay > 0) {
           // set bar to open after X minutes from real open
           processBarOpen = false;
  
           int diffInSeconds = TimeCurrent() - Time[0];
           if(diffInSeconds >= OpenBarDelay * 60) {
              processBarOpen = true;
           }
        }
  
        if(processBarOpen) {
           _sqIsBarOpen = true;
           _sqLastOpenBarTime = Time[0];      
        } 
      } 
      else {
         _sqIsBarOpen = false;
      }
   }

   if(_sqIsBarOpen && Bars<30) {
      Print("NOT ENOUGH DATA: Less Bars than 30");
      return;
   }

   if(!IsTesting() && !IsOptimization()) {
      sqTextFillOpens();
      if(_sqIsBarOpen) {
         sqTextFillTotals();
      }
   }
}

//+------------------------------------------------------------------+

bool sqIsBarOpen() {
   return(_sqIsBarOpen);
}

//+------------------------------------------------------------------+

int sqGetOrderExpiration(int ticket) {
   return (sqGetGlobalVariable(ticket, "sqOrderExpiration"));
}

//+------------------------------------------------------------------+

void sqSetOrderExpiration(int ticket, int bars) {
   sqSetGlobalVariable(ticket, "sqOrderExpiration", bars);
}

//+------------------------------------------------------------------+

void sqManageOrderExpiration(int ticket) {
   int tempValue = 0;                                                           
   int barsOpen = 0;

   // Stop/Limit Order Expiration
   if(OrderType() != OP_BUY && OrderType() != OP_SELL) {
      // handle only pending orders
      tempValue = sqGetOrderExpiration(ticket);
      if(tempValue > 0) {
         barsOpen = sqGetOpenBarsForOrder(ticket, tempValue+10);
         if(barsOpen >= tempValue) {
            Verbose("Order with ticket: ", ticket, " expired");
            sqDeletePendingOrder(ticket);
         }
      }
   }
}

//----------------------------------------------------------------------------

double sqIndicatorHighest(int period, int nthValue, string indicatorIdentification) {     
   if(period > 1000) {
        Alert("Period used for sqIndicatorHighest function is too high. Max value is 1000");
        period = 1000;
   }
   
   if(nthValue < 0 || nthValue >= period) {
	   return(-1);
   }
   
   double indicatorValues[1000];
   int i;

   for(i=0; i<1000; i++) {
      indicatorValues[i] = -2147483647;
   }

   for(i=0; i<period; i++) {
      indicatorValues[i] = sqGetIndicatorByIdentification(indicatorIdentification, i);
   }

   ArraySort(indicatorValues, WHOLE_ARRAY,0,MODE_DESCEND);

   if(nthValue < 0 || nthValue >= period) {
      return(-1);
   }

   return(indicatorValues[nthValue]);
}

//----------------------------------------------------------------------------

double sqIndicatorLowest(int period, int nthValue, string indicatorIdentification) {           
   if(period > 1000) {
        Alert("Period used for sqIndicatorLowest function is too high. Max value is 1000");
        period = 1000;
   }
   
   if(nthValue < 0 || nthValue >= period) {
	   return(-1);
   }
   
   double indicatorValues[1000];
   int i;

   for(i=0; i<1000; i++) {
      indicatorValues[i] = 2147483647;
   }

   for(i=0; i<period; i++) {
      indicatorValues[i] = sqGetIndicatorByIdentification(indicatorIdentification, i);
   }

   ArraySort(indicatorValues,WHOLE_ARRAY,0,MODE_ASCEND);

   if(nthValue < 0 || nthValue >= period) {
      return(-1);
   }

   return(indicatorValues[nthValue]);
}

//----------------------------------------------------------------------------

double sqIndicatorAverage(int period, int maMethod, string indicatorIdentification) {
   double indicatorValues[10000];

   for(int i=0; i<period; i++) {
      indicatorValues[i] = sqGetIndicatorByIdentification(indicatorIdentification, i);
   }

   double maValue = iMAOnArray(indicatorValues, period, period, 0, maMethod, 0);

   return(maValue);
}


//----------------------------------------------------------------------------

double sqIndicatorRecent(int barsBack, string indicatorIdentification) {
   return(sqGetIndicatorByIdentification(indicatorIdentification, barsBack));
}

//----------------------------------------------------------------------------

bool sqIsRising(string indicatorIdentification, int bars, bool allowSameValues, int shift) {
   bool atLeastOnce = false;

   double previousValue = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentification, bars+shift-1), 6);

   for(int i=1; i<bars; i++) {
      double currentValue = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentification, bars+shift-1-i), 6);

      if(currentValue < previousValue) {
         // indicator was falling
         return(false);
      }
      if(currentValue == previousValue && allowSameValues == false) {
         // indicator was the same, not allowed
         return(false);
      }
      if(currentValue > previousValue) {
         atLeastOnce = true;
      }

      previousValue = currentValue;
   }

   if(atLeastOnce) {
      return(true);
   }

   // indicator was not rising once
   return(false);
}

//----------------------------------------------------------------------------

bool sqIsFalling(string indicatorIdentification, int bars, bool allowSameValues, int shift) {
   bool atLeastOnce = false;

   double previousValue = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentification, bars+shift-1), 6);

   for(int i=1; i<bars; i++) {
      double currentValue = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentification, bars+shift-1-i), 6);

      if(currentValue > previousValue) {
         // indicator was rising
         return(false);
      }
      if(currentValue == previousValue && allowSameValues == false) {
         // indicator was the same, not allowed
         return(false);
      }
      if(currentValue < previousValue) {
         atLeastOnce = true;
      }

      previousValue = currentValue;
   }

   if(atLeastOnce) {
      return(true);
   }

   // indicator was not falling once
   return(false);
}

//+------------------------------------------------------------------+

void sqSetGlobalVariable(int ticket, string name, double value) {
   GlobalVariableSet(ticket+"_"+name, value);
}

//+------------------------------------------------------------------+

double sqGetGlobalVariable(int ticket, string name) {
   return (GlobalVariableGet(ticket+"_"+name));
}

//+------------------------------------------------------------------+

int sqStringHash(string str){
   int i, h, k;
   for (i=0; i<StringLen(str); i++){
      k = StringGetChar(str, i);
      h = (h << 5) + h + k;
   }
   return(h);
}

//+------------------------------------------------------------------+

void sqClosePosition(double size, int magicNumber, string symbol, int direction, string comment) {
   Verbose("Closing order with Magic Number: ", magicNumber, ", symbol: ", symbol, ", direction: ", direction, ", comment: ", comment);

   if(!sqSelectOrder(magicNumber, symbol, direction, comment, false, false)) {
      Verbose("Order cannot be found");
   } else {
      if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
         sqClosePositionAtMarket(size);
      } else {
         sqDeletePendingOrder(OrderTicket());
      }
   }

   Verbose("Closing order finished ----------------");
}


//+------------------------------------------------------------------+

int sqGetOpenBarsForOrder(int ticket, int expBarsPeriod) {
   datetime opTime;

   if (OrderType() == OP_BUY || OrderType() == OP_SELL) {             //live order
       opTime = OrderOpenTime();
   }
   else {                                                         //pending order
       opTime = sqGetGlobalVariable(ticket, "sqOrderOpenTime");
   }

   int numberOfBars = 0;
   for(int i=0; i<expBarsPeriod+10; i++) {
      if(opTime < Time[i]) {
         numberOfBars++;
      }
   }

   return(numberOfBars);
}

//+------------------------------------------------------------------+

void Verbose(string st1, string st2="", string s3="", string s4="", string s5="", string s6="", string s7="", string s8="", string s9="", string s10="", string s11="", string s12="", string s13="", string s14="" ) {
   if(sqVerboseMode == 0) {
      return;
   } 
    
   if(sqVerboseMode == 1 && (IsTesting() || IsOptimization())) {
      return;
   }
   
   Print("- SQ LOG ", TimeToStr(TimeCurrent()), " ", st1, st2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14);
}


//+------------------------------------------------------------------+

void VerboseLog(string s1, string s2="", string s3="", string s4="", string s5="", string s6="", string s7="", string s8="", string s9="", string s10="", string s11="", string s12="" ) {
   if(sqVerboseMode != 1) {
      Log(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12);
   }

   Verbose(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12);
}

//+------------------------------------------------------------------+

void Log(string s1, string s2="", string s3="", string s4="", string s5="", string s6="", string s7="", string s8="", string s9="", string s10="", string s11="", string s12="" ) {
   Print(TimeToStr(TimeCurrent()), " ", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12);
}

//+------------------------------------------------------------------+

void sqLog(string st1, string st2="", string s3="", string s4="", string s5="", string s6="", string s7="", string s8="", string s9="", string s10="", string s11="", string s12="" ) {
   Print(TimeToStr(TimeCurrent()), " ", st1, st2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12);
}


//+------------------------------------------------------------------+

void sqLogToFile(string fileName, string st1, string st2="", string s3="", string s4="", string s5="", string s6="", string s7="", string s8="", string s9="", string s10="", string s11="", string s12="" ) {
   int handle = FileOpen(fileName, FILE_READ | FILE_WRITE, ";");
   if(handle>0) {
      FileSeek(handle,0,SEEK_END);
      FileWrite(handle, TimeToStr(TimeCurrent()), " ", st1, st2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12);
      FileClose(handle);
   }
}

//+------------------------------------------------------------------+

bool sqSelectOrder(int magicNumber, string symbol, int direction, string comment, bool goFromNewest=true, bool skipPending=true, bool skipFilled=false) {
    int cc = 0;
    if(goFromNewest){
       for (cc = OrdersTotal() - 1; cc >= 0; cc--) {
           if(orderFits(cc, magicNumber, symbol, direction, comment, skipPending, skipFilled)){
              return true;
           }
       }
    }
    else {
       for (cc = 0; cc < OrdersTotal(); cc++) {
           if(orderFits(cc, magicNumber, symbol, direction, comment, skipPending, skipFilled)){
              return true;
           }
       }
    }
    return(false);
}

//+------------------------------------------------------------------+

bool orderFits(int index, int magicNumber, string symbol, int direction, string comment, bool skipPending=true, bool skipFilled=false){
    if (OrderSelect(index, SELECT_BY_POS)) {
       // skip pending orders
       if(skipPending && (OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP || OrderType() == OP_SELLLIMIT)) {
          return false;
       }
       // skip filled orders
       if(skipFilled && (OrderType() == OP_BUY || OrderType() == OP_SELL)) {
          return false;
       }

       if(direction != 0) {
          if(direction > 0 && (OrderType() != OP_BUY && OrderType() != OP_BUYSTOP && OrderType() != OP_BUYLIMIT)) return false;
          if(direction < 0 && (OrderType() != OP_SELL && OrderType() != OP_SELLSTOP && OrderType() != OP_SELLLIMIT)) return false;
       }

       if(magicNumber != 0) {
          if(!checkMagicNumber(OrderMagicNumber()) || OrderMagicNumber() != magicNumber) return false;
       }

       if(symbol != "Any") {
          if(symbol == "NULL" || symbol == "Current") {
            symbol = Symbol();
          }
  
          if(OrderSymbol() != symbol) return false;
       }

     if(comment != "") {
       if(StringFind(OrderComment(), comment) == -1) return false;
     }

     // otherwise we found the order
     return(true);
   }
   else return false;
}

//+------------------------------------------------------------------+

void sqCloseAllPositions(string symbol, int magicNumber, int direction, string comment) {
   int count = 100; // maximum number of positions to close
   int lastTicket = -1;

   while(count > 0) {
      count--;
      if(!sqSelectOrder(magicNumber, symbol, direction, comment, false, false)) {
         // no position found
         break;
      }

      if(lastTicket == OrderTicket()) {
         // trying to close the same position one more time, there must be some error
         break;
      }
      lastTicket = OrderTicket();

      if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
         sqClosePositionAtMarket(OrderLots());
      } else {
         sqDeletePendingOrder(OrderTicket());
      }
   }
}

//+------------------------------------------------------------------+

void sqCloseBestPosition(string symbol, int magicNumber, int direction, string comment) {
   double maxPL = -100000000;
   int ticket = 0;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }
         else if(!checkMagicNumber(OrderMagicNumber())) continue;
         
         if(OrderProfit() > maxPL) {
            // found order with better profit
            maxPL = OrderProfit();
            ticket = OrderTicket();
            Verbose("Better position found, ticket: ", ticket,", PL: ", maxPL);
         }
      }
   }

   if(ticket > 0) {
      if(OrderSelect(ticket, SELECT_BY_TICKET)) {
         sqClosePositionAtMarket(OrderLots());
      }
   }
}

//+------------------------------------------------------------------+

void sqCloseWorstPosition(string symbol, int magicNumber, int direction, string comment) {
   double minPL = 100000000;
   int ticket = 0;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }  
         else if(!checkMagicNumber(OrderMagicNumber())) continue;
         
         if(OrderProfit() < minPL) {
            // found order with worse profit
            minPL = OrderProfit();
            ticket = OrderTicket();
            Verbose("Worse position found, ticket: ", ticket,", PL: ", minPL);
         }
      }
   }

   if(ticket > 0) {
      if(OrderSelect(ticket, SELECT_BY_TICKET)) {
         sqClosePositionAtMarket(OrderLots());
      }
   }
}

//+------------------------------------------------------------------+

int sqGetMarketPosition(string symbol, int magicNumber, string comment) {
   if(sqSelectOrder(magicNumber, symbol, 0, comment, false)) {
      if(OrderType() == OP_BUY) {
         return(1);
      } else {
         return(-1);
      }
   }
   return(0);
}                     

//+------------------------------------------------------------------+

bool sqMarketPositionIsShort(int magicNo, string symbol, string comment){
   return sqSelectOrder(magicNo, symbol, -1, comment, false);
}    

//+------------------------------------------------------------------+

bool sqMarketPositionIsNotShort(int magicNo, string symbol, string comment){
   if(sqSelectOrder(magicNo, symbol, 1, comment, false)) {
      return false; 	
	 }
	 
	 return true;
}     

//+------------------------------------------------------------------+

bool sqMarketPositionIsLong(int magicNo, string symbol, string comment){
   return sqSelectOrder(magicNo, symbol, 1, comment, false);
}     

//+------------------------------------------------------------------+

bool sqMarketPositionIsNotLong(int magicNo, string symbol, string comment){
   if(sqSelectOrder(magicNo, symbol, 1, comment, false)) {
      return false; 	
	 }
	 
	 return true;
}     

//+------------------------------------------------------------------+

bool sqMarketPositionIsFlat(int magicNo, string symbol, string comment){
   return sqGetMarketPosition(symbol, magicNo, comment) == 0;
}

//+------------------------------------------------------------------+

double sqGetOrderOpenPrice(string symbol, int magicNumber, int direction, string comment) {
   if(sqSelectOrder(magicNumber, symbol, direction, comment, false)) {
      return(OrderOpenPrice());
   }
   return(-1);
}

//+------------------------------------------------------------------+

double sqGetOrderStopLoss(string symbol, int magicNumber, int direction, string comment) {
   if(sqSelectOrder(magicNumber, symbol, direction, comment, false)) {
      return(OrderStopLoss());
   }
   return(-1);
}

//+------------------------------------------------------------------+

double sqGetOrderProfitTarget(string symbol, int magicNumber, int direction, string comment) {
   if(sqSelectOrder(magicNumber, symbol, direction, comment, false)) {
      return(OrderTakeProfit());
   }
   return(-1);
}

//+------------------------------------------------------------------+

double sqGetMarketPositionSize(string symbol, int magicNumber, int direction, string comment) {
   double lots = 0;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(direction != 0) {
            if(direction > 0 && OrderType() != OP_BUY) continue;
            if(direction < 0 && OrderType() != OP_SELL) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         } 
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
            if(StringFind(OrderComment(), comment) == -1) continue;
         }

         lots += OrderLots();
      }
   }

   return(lots);
}

//+------------------------------------------------------------------+

double sqGetOpenPL(string symbol, int magicNumber, int direction, string comment) {
   double pl = 0;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(direction != 0) {
            if(direction > 0 && OrderType() != OP_BUY) continue;
            if(direction < 0 && OrderType() != OP_SELL) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }    
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
            if(StringFind(OrderComment(), comment) == -1) continue;
         }

         pl += OrderProfit();
      }
   }

   return(pl);
}

//+------------------------------------------------------------------+

double sqGetOpenPLInPips(string symbol, int magicNumber, int direction, string comment) {
   double pl = 0;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(direction != 0) {
            if(direction > 0 && OrderType() != OP_BUY) continue;
            if(direction < 0 && OrderType() != OP_SELL) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }  
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
            if(StringFind(OrderComment(), comment) == -1) continue;
         }

         if(OrderType() == OP_BUY) {
            pl += sqGetBid(OrderSymbol()) - OrderOpenPrice();
         } else {
            pl += OrderOpenPrice() - sqGetAsk(OrderSymbol());
         }
      }
   }

   return(sqConvertToPips(OrderSymbol(), pl));
}

//+------------------------------------------------------------------+

int sqGetClosedPLInPips(string symbol, int magicNumber, int direction, string comment, int shift) {
   int index = 0;

   for(int i=OrdersHistoryTotal(); i>=0; i--) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==true ) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(direction != 0) {
            if(direction > 0 && OrderType() != OP_BUY) continue;
            if(direction < 0 && OrderType() != OP_SELL) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }     
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
            if(StringFind(OrderComment(), comment) == -1) continue;
         }

         if(index == shift) {
            if(OrderType() == OP_BUY) {
               return(sqConvertToPips(OrderSymbol(), OrderClosePrice() - OrderOpenPrice()));
            } else {
               return(sqConvertToPips(OrderSymbol(), OrderOpenPrice() - OrderClosePrice()));
            }
         }

         index++;
      }
   }

   return(0);
}

//+------------------------------------------------------------------+

int sqGetClosedPLInMoney(string symbol, int magicNumber, int direction, string comment, int shift) {
   int index = 0;

   for(int i=OrdersHistoryTotal(); i>=0; i--) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==true ) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(direction != 0) {
            if(direction > 0 && OrderType() != OP_BUY) continue;
            if(direction < 0 && OrderType() != OP_SELL) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }      
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
            if(StringFind(OrderComment(), comment) == -1) continue;
         }

         if(index == shift) {
            return(OrderProfit());
         }

         index++;
      }
   }

   return(0);
}

//+------------------------------------------------------------------+

int sqGetMarketPositionCount(string symbol, int magicNumber, int direction, string comment) {
   double count = 0;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP ||OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(direction != 0) {
            if(direction > 0 && OrderType() != OP_BUY) continue;
            if(direction < 0 && OrderType() != OP_SELL) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }   
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
            if(StringFind(OrderComment(), comment) == -1) continue;
         }

         count++;
      }
   }

   return(count);
}

//+------------------------------------------------------------------+

int sqGetBarsSinceOpen(string symbol, int magicNumber, int direction, string comment) {
   if(sqSelectOrder(magicNumber, symbol, direction, comment, false, false)) {
      datetime opTime = OrderOpenTime();

      int numberOfBars = 0;
      for(int i=0; i<10000; i++) {
         if(opTime < Time[i]) {
            numberOfBars++;
         }
      }

      return(numberOfBars);
   }

   return(-1);
}

//+------------------------------------------------------------------+

int sqGetBarsSinceClose(string symbol, int magicNumber, int direction, string comment) {
   if(sqSelectOrderInHistory(magicNumber, symbol, direction, comment)) {
      datetime clTime = OrderCloseTime();

      int numberOfBars = 0;
      for(int i=0; i<10000; i++) {
         if(clTime < Time[i]) {
            numberOfBars++;
         }
      }

      return(numberOfBars);
   }

   return(-1);
}


//+------------------------------------------------------------------+

int sqGetLastOrderType(string symbol, int magicNumber, string comment) {
   if(sqSelectOrderInHistory(magicNumber, symbol, 0, comment)) {
      if(OrderType() == OP_BUY || OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT) {
         return(1);
      } else {
         return(-1);
      }
   }

   return(0);
}

//+------------------------------------------------------------------+

bool sqSelectOrderInHistory(int magicNumber, string symbol, int direction, string comment) {
   for (int cc = OrdersHistoryTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS,MODE_HISTORY)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP || OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(direction != 0) {
            if(direction > 0 && OrderType() != OP_BUY) continue;
            if(direction < 0 && OrderType() != OP_SELL) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }   
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

       if(symbol != "Any") {
         if(symbol == "NULL" || symbol == "Current") {
            symbol = Symbol();
         }

         if(OrderSymbol() != symbol) continue;
       }

       if(comment != "") {
         if(StringFind(OrderComment(), comment) == -1) continue;
       }

       // otherwise we found the order
       return(true);
     }
   }

   return(false);
}

//+------------------------------------------------------------------+

bool sqSelectPendingOrderByType(int magicNumber, string symbol, int orderType, string comment) {
   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {
         if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
            continue;
         }
         
         if(orderType != 0) {
            if(OrderType() != orderType) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }   
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == NULL || symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
            if(StringFind(OrderComment(), comment) == -1) continue;
         }

         // otherwise we found the order
         return(true);
      }
   }

   return(false);
}

//+------------------------------------------------------------------+

bool sqSelectPendingOrderByDir(int magicNumber, string symbol, int direction, string comment) {
   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS)) {
         if(OrderType() == OP_BUY || OrderType() == OP_SELL) {
            continue;
         }
       
         if(direction != 0) {
            int orderDirection = sqGetDirectionFromOrderType(OrderType());
            if(orderDirection != direction) continue;
         }

         if(magicNumber != 0) {
            if(OrderMagicNumber() != magicNumber) continue;
         }     
         else if(!checkMagicNumber(OrderMagicNumber())) continue;

         if(symbol != "Any") {
            if(symbol == NULL || symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         if(comment != "") {
           if(StringFind(OrderComment(), comment) == -1) continue;
         }

         // otherwise we found the order
         return(true);
      }
   }

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

bool sqClosePositionAtMarket(double size) {
   int ticket = OrderTicket();

   Verbose("Closing order with ticket: ", ticket);

   int orderType = OrderType();

   if(orderType != OP_BUY && orderType != OP_SELL) {
      Verbose("Trying to close non-live order");
      return(false);
   }
   if(!sqCheckConnected()) {
      return(false);
   }

   bool result;

   double price = sqGetClosePrice(orderType, OrderSymbol(), 0);
   result = OrderCloseReliable(ticket, size, price, correctSlippage(sqMaxCloseSlippage, OrderSymbol()));
   if(result) {
      Verbose("Order deleted successfuly");
      return(true);
   }

   return(false);
}

//+------------------------------------------------------------------+

double sqGetAsk(string symbol) {
   if(symbol == "NULL" || symbol == "Current") {
     return(NormalizeDouble(Ask, Digits));
   } else {
     return(NormalizeDouble(MarketInfo(symbol,MODE_ASK), Digits));
   }
}

//+------------------------------------------------------------------+

double sqGetBid(string symbol) {
   if(symbol == "NULL" || symbol == "Current") {
     return(NormalizeDouble(Bid, Digits));
   } else {
     return(NormalizeDouble(MarketInfo(symbol,MODE_BID), Digits));
   }
}

//+------------------------------------------------------------------+

bool sqDeletePendingOrder(int ticket) {
   Verbose(" Deleting pending order, ticket: " + ticket);

   int orderType = OrderType();

   if(orderType == OP_BUY || orderType == OP_SELL) {
      Verbose("Trying to delete non-pending order");
      return(false);
   }
   if(!sqCheckConnected()) {
      return(false);
   }
         
   bool result = OrderDeleteReliable(ticket);
   if(result) {
      Verbose("Order deleted successfuly");
      return(true);
   }
   
   return(false);
}

//+------------------------------------------------------------------+

int sqOpenOrder(int orderType, string symbol, double size, double price, int magicNumber, string comment, datetime expirationInTime, bool replaceExisting, bool allowDuplicateTrades, color arrowColor) {
   if(symbol == "NULL" || symbol == "Current") {
      symbol = Symbol();
   }

   double ask = sqGetAsk(symbol);
   double bid = sqGetBid(symbol);
   
   int direction = sqGetDirectionFromOrderType(orderType);
   
   double marketPrice = direction == 1 ? ask : bid;

   price = price > 0 ? price : marketPrice;                
   price = sqFixMarketPrice(price, symbol);
   
   openingOrdersAllowed = openingOrdersAllowed && sqHandleTradingOptions();
   
   if(!openingOrdersAllowed) return(0);                                                        

   Verbose("Opening order type ", OrderTypeToString(orderType)," with price ", price, ". Current market prices: ", ask, " / ", bid);

   // check if live order exists
   if(sqSelectOrder(magicNumber, symbol, 0, comment) && !allowDuplicateTrades) {
      Verbose("Order with these parameters already exists and duplicate trades are not allowed. Canceling order...");
      Verbose("----------------------------------");
      return(0);
   }

   // check if pending order exists
   if(sqSelectOrder(magicNumber, symbol, direction, comment, false, false, true)) {
      if(replaceExisting) {
      
         if(ModifyInsteadOfReplacing) {
            // modify existing pending order
            if(OrderModifyReliable(OrderTicket(), price, 0, 0, expirationInTime)) {
               // reset global variables for this order
               sqResetGlobalVariablesForTicket(OrderTicket());
               return ( OrderTicket() );
            
            } else {
               Verbose("Modifying order failed, deleting it");
               sqDeletePendingOrder(OrderTicket());
               return(0);
            }
         } else {
            // delete existing pending order
            sqDeletePendingOrder(OrderTicket());
         }

      } else {
         Verbose("Pending Order with these parameters already exists, and replace is not allowed. Canceling order...", " ----------------");
         return(0);
      }
   }

   if(!checkOrderPriceValid(orderType, symbol, price, marketPrice)){
      return 0;
   }
   
   string commentToUse = "";
   if(comment != ""){
      commentToUse = comment;
   }
   else {
      commentToUse = CustomComment;
      StringReplace(commentToUse, "Optimization", "Opt.");     //shorten the name of optimized strategies
   }
   commentToUse = StringSubstr(commentToUse, 0, 30);           //limit the length to 30 characters

   int ticket = OrderSendReliable(symbol, orderType, size, price, correctSlippage(sqMaxEntrySlippage, symbol), 0, 0, commentToUse, magicNumber, expirationInTime, arrowColor);

   if(ticket > 0) {
      // reset global variables for this order
      sqResetGlobalVariablesForTicket(ticket);
   }

	 return(ticket);
}

//----------------------------------------------------------------------------

int correctSlippage(int slippage, string symbol = NULL){
    if(slippage <= 0) return 100000;
    
    if(autoCorrectMaxSlippage){
       int realDigits = (int) MarketInfo(correctSymbol(symbol), MODE_DIGITS);
       if(realDigits > 0 && realDigits != 2 && realDigits != 4) {
          return slippage * 10;
       }
    }
    
    return slippage;
}

//----------------------------------------------------------------------------

bool checkOrderPriceValid(int orderType, string symbol, double price, double marketPrice){
   if(orderType == OP_BUY || orderType == OP_SELL){
      if(marketPrice == price){
         return true;
      }
      else {
         Verbose("Strategy tried to place market order at incorrect price. Market price: ", marketPrice, ", order price: ", price);
         return false;
      }
   }
   
   return checkStopPriceValid(orderType, symbol, price, marketPrice, "stop/limit order");
}

//----------------------------------------------------------------------------

bool checkStopPriceValid(int orderType, string symbol, double price, double marketPrice, string name){
   int stopLevel = (int) MarketInfo(symbol, MODE_STOPLEVEL);
   double point = MarketInfo(symbol, MODE_POINT);
   double minDistance = point * stopLevel;
   double priceLevel;
   
   if(orderType == OP_BUYLIMIT || orderType == OP_SELLSTOP){
      priceLevel = marketPrice - minDistance;
      
      if(price <= priceLevel){
         return true;
      }
      else {
         Verbose("Strategy tried to place ", name, " at incorrect price. Market price: ", marketPrice, ", max. price allowed: ", priceLevel, ", ", name, " price: ", price);
         return false;
      }
   }
   else if(orderType == OP_BUYSTOP || orderType == OP_SELLLIMIT){
      priceLevel = marketPrice + minDistance;
      
      if(price >= priceLevel){
         return true;
      }
      else {
         Verbose("Strategy tried to place ", name, " at incorrect price. Market price: ", marketPrice, ", min. price allowed: ", priceLevel, ", ", name," price: ", price);
         return false;
      }
   }
   else return true;
}   

//+------------------------------------------------------------------+

int sqGetDirectionFromOrderType(int orderType) {
   if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
      return(1);
   } else {
      return(-1);
   }
}

//+------------------------------------------------------------------+

bool sqIsPendingOrder(int orderType) {
   if(orderType != OP_BUY && orderType != OP_SELL) {
      return(true);
   }
   return(false);
}

//+------------------------------------------------------------------+

void sqSetSLandPT(int ticket, double sl, double pt) {
   if(sl == 0 && pt == 0) return;
   
   if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
      Verbose("Cannot select order with ticket: ",ticket);
      return;
   }
   
   if(sl == OrderOpenPrice()) {
      Verbose("SL is the same as order price, cannot set it, so we'll delete the order!");
      if(!OrderDeleteReliable(ticket)) {
         Verbose("Warning! Cannot delete order and SL/PT was not set! Error: ", GetLastError());
      }

      return;
   }

   if(pt == OrderOpenPrice()) {
      pt = 0;
   }
   
   if(sl > 0 || pt > 0) {
      bool result = sqOrderModify(ticket, OrderOpenPrice(), sqFixMarketPrice(sl, Symbol()), sqFixMarketPrice(pt, Symbol()));
      if(!result) {
         Verbose("Cannot set SL / PT for this order, deleting it!");
         if(!OrderDeleteReliable(ticket)) {
            Verbose("Warning! Cannot delete order and Sl/PT was not set! Error: ", GetLastError());
         }
      }
   }
}

//+------------------------------------------------------------------+

bool sqOrderModifySL(int ticket,double stopLoss, int type) {
   if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
      Verbose("Cannot select order with ticket: ",ticket);
   }

   if(type == SLPTTYPE_RANGE) {
      // convert range to price level
      if(sqGetDirectionFromOrderType(OrderType()) == 1) {
         // it is long order
         stopLoss = OrderOpenPrice() - stopLoss;
      } else {
         stopLoss = OrderOpenPrice() + stopLoss;
      }
   }

   return(sqOrderModify(ticket, OrderOpenPrice(), sqFixMarketPrice(stopLoss, Symbol()), OrderTakeProfit()));
}

//+------------------------------------------------------------------+

bool sqOrderModifyPT(int ticket,double profitTarget, int type) {
   if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
      Verbose("Cannot select order with ticket: ",ticket);
   }

   if(type == SLPTTYPE_RANGE) {
      // convert range to price level
      if(sqGetDirectionFromOrderType(OrderType()) == 1) {
         // it is long order
         profitTarget = OrderOpenPrice() + profitTarget;
      } else {
         profitTarget = OrderOpenPrice() - profitTarget;
      }
   }

   return(sqOrderModify(ticket, OrderOpenPrice(), OrderStopLoss(), sqFixMarketPrice(profitTarget, Symbol())));
}

//+------------------------------------------------------------------+

bool sqOrderModify(int ticket, double price, double stopLoss, double profitTarget) {
   if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
      Verbose("Cannot select order with ticket: ",ticket);
   }

   int digits = MarketInfo(OrderSymbol(), MODE_DIGITS);
   if (digits > 0) {
      stopLoss = NormalizeDouble(stopLoss, digits);
      profitTarget = NormalizeDouble(profitTarget, digits);
	}

   Verbose("Modifying order with ticket: ", ticket, ", SL: ", stopLoss, " and PT: ", profitTarget);

   int orderType = OrderType();

   if(!sqCheckConnected()) {
      return(false);
   }

   bool result;
   double closestPossibleSL, closestPossiblePT;

   double point = MarketInfo(OrderSymbol(), MODE_POINT);
   double minStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL);
   
   if(stopLoss > 0) {
      // check if SL isn't too close to price
      if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
         if(orderType == OP_BUY) {
            closestPossibleSL = NormalizeDouble(Bid-minStopLevel*point, digits);
         } else {
            closestPossibleSL = NormalizeDouble(OrderOpenPrice()-minStopLevel*point, digits);
         }

         if(stopLoss > closestPossibleSL) {
            Verbose("Cannot modify SL, it cannot be closer than minimum allowed: ", stopLoss, " > ", closestPossibleSL);
            return(false);
         }

      } else {
         if(orderType == OP_SELL) {
            closestPossibleSL = NormalizeDouble(Ask+minStopLevel*point, digits);
         } else {
            closestPossibleSL = NormalizeDouble(OrderOpenPrice()+minStopLevel*point, digits);
         }

         if(stopLoss < closestPossibleSL) {
            Verbose("Cannot modify SL, it cannot be closer than minimum allowed: ", stopLoss, " < ", closestPossibleSL);
            return(false);
         }
      }
   }
   
   if(profitTarget > 0) {
      // check if PT isn't too close to price
      if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
         if(orderType == OP_BUY) {
            closestPossiblePT = NormalizeDouble(Bid+minStopLevel*point, digits);
         } else {
            closestPossiblePT = NormalizeDouble(OrderOpenPrice()+minStopLevel*point, digits);
         }

         if(profitTarget < closestPossiblePT) {
            Verbose("Cannot modify PT, it cannot be closer than minimum allowed: ", profitTarget, " < ", closestPossiblePT);
            return(false);
         }

      } else {
         if(orderType == OP_SELL) {
            closestPossiblePT = NormalizeDouble(Ask-minStopLevel*point, digits);
         } else {
            closestPossiblePT = NormalizeDouble(OrderOpenPrice()-minStopLevel*point, digits);
         }

         if(profitTarget > closestPossiblePT) {
            Verbose("Cannot modify PT, it cannot be closer than minimum allowed: ", profitTarget, " > ", closestPossiblePT);
            return(false);
         }
      }
   }
      
   result = OrderModifyReliable(ticket, price, sqFixMarketPrice(stopLoss, Symbol()), sqFixMarketPrice(profitTarget, Symbol()), OrderExpiration());
   if(result) {
      Verbose("Order modified successfuly");
      return(true);
   }

   return(false);
}

//+------------------------------------------------------------------+

double sqGetPrice(int orderType, string symbol, double price) {
   if(price == 0 && (orderType == OP_BUY || orderType == OP_SELL)) {
      // get newest Ask/Bid price for market order
      RefreshRates();
      if(orderType == OP_BUY) {
         price = sqGetAsk(symbol);
      } else {
         price = sqGetBid(symbol);
      }
   }
   
   int digits = MarketInfo(symbol, MODE_DIGITS);

  	if (digits > 0) price = NormalizeDouble(price, digits);

   return(price);
}


//+------------------------------------------------------------------+

double sqGetClosePrice(int orderType, string symbol, double price) {
   if(price == 0 && (orderType == OP_BUY || orderType == OP_SELL)) {
      // get newest Ask/Bid price for market order
      RefreshRates();
      if(orderType == OP_BUY) {
         price = sqGetBid(symbol);
      } else {
         price = sqGetAsk(symbol);
      }
   }
   
   int digits = MarketInfo(symbol, MODE_DIGITS);

  	if (digits > 0) price = NormalizeDouble(price, digits);

   return(price);
}

//+------------------------------------------------------------------+

bool sqCheckConnected() {
   if (!IsConnected()) {
      Verbose("Not connected!");
      return(false);
   }
   if (IsStopped()) {
      Verbose("EA stopped!");
      return(false);
   }

   return(true);
}

//+------------------------------------------------------------------+

double sleepPeriod = 500; // 0.5 s
double maxSleepPeriod = 20000; // 20 s.

void sqSleep() {
   //if(IsTesting()) return;

   Sleep(sleepPeriod);

   int periods = maxSleepPeriod / sleepPeriod;

   for(int i=0; i<periods; i++) {
      if (MathRand() > 16383) {
         // 50% chance of quitting
         break;
      }

      Sleep(sleepPeriod);
   }
}

//+------------------------------------------------------------------+

string sqGetOrderTypeAsString(int type) {
   switch(type) {
      case OP_BUY: return("Buy");
      case OP_SELL: return("Sell");
      case OP_BUYLIMIT: return("Buy Limit");
      case OP_BUYSTOP: return("Buy Stop");
      case OP_SELLLIMIT: return("Sell Limit");
      case OP_SELLSTOP: return("Sell Stop");
   }

   return("Unknown");
}

//+------------------------------------------------------------------+

void sqInitInfoPanel() {
      ObjectCreate("line1", OBJ_LABEL, 0, 0, 0);
      ObjectSet("line1", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("line1", OBJPROP_YDISTANCE, sqOffsetVertical + 0 );
      ObjectSet("line1", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("line1", "NZDUSD WFM1000 0.109322 - WF R4 45P", 9, "Tahoma", sqLabelColor);

      ObjectCreate("linec", OBJ_LABEL, 0, 0, 0);
      ObjectSet("linec", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("linec", OBJPROP_YDISTANCE, sqOffsetVertical + 16 );
      ObjectSet("linec", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("linec", "Generated by StrategyQuant EA Wizard", 8, "Tahoma", sqLabelColor);

      ObjectCreate("line2", OBJ_LABEL, 0, 0, 0);
      ObjectSet("line2", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("line2", OBJPROP_YDISTANCE, sqOffsetVertical + 28);
      ObjectSet("line2", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("line2", "------------------------------------------", 8, "Tahoma", sqLabelColor);

      ObjectCreate("lines", OBJ_LABEL, 0, 0, 0);
      ObjectSet("lines", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("lines", OBJPROP_YDISTANCE, sqOffsetVertical + 44);
      ObjectSet("lines", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("lines", "Last Signal:  -", 9, "Tahoma", sqLabelColor);

      ObjectCreate("lineopl", OBJ_LABEL, 0, 0, 0);
      ObjectSet("lineopl", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("lineopl", OBJPROP_YDISTANCE, sqOffsetVertical + 60);
      ObjectSet("lineopl", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("lineopl", "Open P/L: -", 8, "Tahoma", sqLabelColor);

      ObjectCreate("linea", OBJ_LABEL, 0, 0, 0);
      ObjectSet("linea", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("linea", OBJPROP_YDISTANCE, sqOffsetVertical + 76);
      ObjectSet("linea", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("linea", "Account Balance: -", 8, "Tahoma", sqLabelColor);

      ObjectCreate("lineto", OBJ_LABEL, 0, 0, 0);
      ObjectSet("lineto", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("lineto", OBJPROP_YDISTANCE, sqOffsetVertical + 92);
      ObjectSet("lineto", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("lineto", "Total profits/losses so far: -/-", 8, "Tahoma", sqLabelColor);

      ObjectCreate("linetp", OBJ_LABEL, 0, 0, 0);
      ObjectSet("linetp", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("linetp", OBJPROP_YDISTANCE, sqOffsetVertical + 108);
      ObjectSet("linetp", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("linetp", "Total P/L so far: -", 8, "Tahoma", sqLabelColor);
}

//+------------------------------------------------------------------+

void sqDeinitInfoPanel() {
   ObjectDelete("line1");
   ObjectDelete("linec");
   ObjectDelete("line2");
   ObjectDelete("lines");
   ObjectDelete("lineopl");
   ObjectDelete("linea");
   ObjectDelete("lineto");
   ObjectDelete("linetp");
}

//+------------------------------------------------------------------+

void sqTextFillOpens() {
   ObjectSetText("lineopl", "Open P/L: "+DoubleToStr(sqGetOpenPLInMoney(0), 2), 8, "Tahoma", sqLabelColor);
   ObjectSetText("linea", "Account Balance: "+DoubleToStr(AccountBalance(), 2) , 8, "Tahoma", sqLabelColor);
}

//+------------------------------------------------------------------+

void sqTextFillTotals() {
   ObjectSetText("lineto", "Total profits/losses so far: "+sqGetTotalProfits(0, 100)+"/"+sqGetTotalLosses(0, 100), 8, "Tahoma", sqLabelColor);
   ObjectSetText("linetp", "Total P/L so far: "+DoubleToStr(sqGetTotalClosedPLInMoney(0, 1000), 2), 8, "Tahoma", sqLabelColor);
}


//+------------------------------------------------------------------+

double sqGetOpenPLInMoney(int orderMagicNumber) {
   double pl = 0;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (!OrderSelect(cc, SELECT_BY_POS) ) continue;
      if(OrderType() != OP_BUY && OrderType() != OP_SELL) continue;
      if(OrderSymbol() != Symbol()) continue;
      if(orderMagicNumber != 0 && OrderMagicNumber() != orderMagicNumber) continue;

      pl += OrderProfit();
   }

   return(pl);
}

//+------------------------------------------------------------------+

int sqGetTotalProfits(int orderMagicNumber, int numberOfLastOrders) {
   double pl = 0;
   int count = 0;
   int profits = 0;

   for(int i=OrdersHistoryTotal(); i>=0; i--) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==true && OrderSymbol() == Symbol()) {

         if(orderMagicNumber == 0 || OrderMagicNumber() == orderMagicNumber) {
            // return the P/L of last order
            // or return the P/L of last order with given Magic Number
            count++;

            if(OrderType() == OP_BUY) {
               pl = (OrderClosePrice() - OrderOpenPrice());
            } else {
               pl = (OrderOpenPrice() - OrderClosePrice());
            }

            if(pl > 0) {
               profits++;
            }

            if(count >= numberOfLastOrders) break;
         }
      }
   }

   return(profits);
}

//+------------------------------------------------------------------+

int sqGetTotalLosses(int orderMagicNumber, int numberOfLastOrders) {
   double pl = 0;
   int count = 0;
   int losses = 0;

   for(int i=OrdersHistoryTotal(); i>=0; i--) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==true && OrderSymbol() == Symbol()) {

         if(orderMagicNumber == 0 || OrderMagicNumber() == orderMagicNumber) {
            // return the P/L of last order
            // or return the P/L of last order with given Magic Number
            count++;

            if(OrderType() == OP_BUY) {
               pl = (OrderClosePrice() - OrderOpenPrice());
            } else {
               pl = (OrderOpenPrice() - OrderClosePrice());
            }

            if(pl < 0) {
               losses++;
            }

            if(count >= numberOfLastOrders) break;
         }
      }
   }

   return(losses);
}


//+------------------------------------------------------------------+

int sqGetTotalClosedPLInMoney(int orderMagicNumber, int numberOfLastOrders) {
   double pl = 0;
   int count = 0;

   for(int i=OrdersHistoryTotal(); i>=0; i--) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==true && OrderSymbol() == Symbol()) {
         if(orderMagicNumber == 0 || OrderMagicNumber() == orderMagicNumber) {
            // return the P/L of last order or the P/L of last order with given Magic Number

            count++;
            pl = pl + OrderProfit();

            if(count >= numberOfLastOrders) break;
         }
      }
   }

   return(pl);
}

//+------------------------------------------------------------------+

double sqGetSLLevel(string symbol, int orderType, double price, int valueInPips, double value) {
   return(sqGetSLPTLevel(-1.0, symbol, orderType, price, valueInPips, value));
}

//+------------------------------------------------------------------+

double sqGetPTLevel(string symbol, int orderType, double price, int valueInPips, double value) {
   return(sqGetSLPTLevel(1.0, symbol, orderType, price, valueInPips, value));
}

//+------------------------------------------------------------------+

/**
* valueType: 1 - pips, 2 - real pips (ATR range), 3 - price level
*/
double sqGetSLPTLevel(double SLorPT, string symbol, int orderType, double price, int valueType, double value) {
   if(symbol == "NULL" || symbol == "Current") {
      symbol = Symbol();
   }

   if(valueType == 1) {
      // convert from pips to real points
      value = sqConvertToRealPips(symbol, value);
   }
   
   if(price == 0) {
      // price can be zero for market order
      if(orderType == OP_BUY) {
         price = sqGetAsk(symbol);
      } else {
         price = sqGetBid(symbol);
      }
   }
   
   double slptValue = value;
   
   if(valueType != 3) {
      if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
         slptValue = price + (SLorPT * value);
      } else {
         slptValue = price - (SLorPT * value);
      }
   }

   // check that SL / PT is within predefined boundaries
   double minSLPTValue, maxSLPTValue;
   
   if(SLorPT < 0) {
      // it is SL
      
      if(MinimumSL <= 0) {
         minSLPTValue = slptValue;
      } else {
         if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
            minSLPTValue = price + (SLorPT * MinimumSL * gPointCoef);
            slptValue = MathMin(slptValue, minSLPTValue);
            
         } else {
         
            minSLPTValue = price - (SLorPT * MinimumSL * gPointCoef);
            slptValue = MathMax(slptValue, minSLPTValue);
         }
   
      }
      
      if(MaximumSL <= 0) {
         maxSLPTValue = slptValue;
      } else {
         if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
            maxSLPTValue = price + (SLorPT * MaximumSL * gPointCoef);
            slptValue = MathMax(slptValue, maxSLPTValue);

         } else {
            maxSLPTValue = price - (SLorPT * MaximumSL * gPointCoef);
            slptValue = MathMin(slptValue, maxSLPTValue);
         }

      }
      
   } else {
      // it is PT

      if(MinimumPT <= 0) {
         minSLPTValue = slptValue;
      } else {
         if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
            minSLPTValue = price + (SLorPT * MinimumPT * gPointCoef);
            slptValue = MathMax(slptValue, minSLPTValue);
            
         } else {
            minSLPTValue = price - (SLorPT * MinimumPT * gPointCoef);
            slptValue = MathMin(slptValue, minSLPTValue);
         }

      }
      
      if(MaximumPT <= 0) {
         maxSLPTValue = slptValue;
      } else {

         if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
            maxSLPTValue = price + (SLorPT * MaximumPT * gPointCoef);
            slptValue = MathMin(slptValue, maxSLPTValue);

         } else {
         
            maxSLPTValue = price - (SLorPT * MaximumPT * gPointCoef);
            slptValue = MathMax(slptValue, maxSLPTValue);
         }
      }
   }
               
   return (slptValue);
}             

//+------------------------------------------------------------------+

double sqConvertToPips(string symbol, double value) {
   if(symbol == "NULL" || symbol == "Current") {
      return(value / gPointCoef);
   }

   // recognize point coeficient         
   double ticksize = sqGetMarketTickSize(symbol);
   if(ticksize < 0){
      ticksize = calculatePointCoef(symbol);
   }

   return(value / ticksize);
}                  
                         
//+------------------------------------------------------------------+

double sqConvertToRealPips(string symbol, double value) {
   if(symbol == "NULL" || symbol == "Current" || symbol == "Same as main chart") {
      return NormalizeDouble(gPointCoef * value, 6);
   }

   double pointCoef = sqGetPointCoef(symbol);

   return NormalizeDouble(pointCoef * value, 6);
}

//+------------------------------------------------------------------+

double sqGetPointCoef(string symbol) {
   if(symbol == "NULL" || symbol == "Current" || symbol == "Same as main chart") {
      return(gPointCoef);
   }

   return calculatePointCoef(symbol);
}

//+------------------------------------------------------------------+

double calculatePointCoef(string symbol){
   if(symbol == "NULL" || symbol == "Current" || symbol == "Same as main chart") {
      symbol = Symbol();
   }
   
   if(UseSQTickSize) {
      double ticksize = sqGetMarketTickSize(symbol);
      if(ticksize >= 0){
         return ticksize;
      }
   }
      
   int realDigits = (int) MarketInfo(symbol, MODE_DIGITS);
   if(realDigits > 0 && realDigits != 2 && realDigits != 4) {
       realDigits -= 1;
   }
   return 1.0 / MathPow(10, realDigits);
}

//+------------------------------------------------------------------+

bool sqDoublesAreEqual(double n1, double n2) {
   string st1 = DoubleToStr(n1, Digits);
   string st2 = DoubleToStr(n2, Digits);

   return (st1 == st2);
}

//+------------------------------------------------------------------+

double sqHighest(string symbol, int timeframe, int computedFrom, int period, int shift) {
   double maxnum = -100000000;
   double val;

   for(int i=shift; i<shift+period; i++) {
      val = sqGetValue(symbol, timeframe, computedFrom, i);

      if(val > maxnum) {
         maxnum = val;
      }
   }

   return(maxnum);
}

//+------------------------------------------------------------------+

double sqHighestIndex(string symbol, int timeframe, int computedFrom, int period, int shift) {
   double maxnum = -100000000;
   int index;
   double val;

   for(int i=shift; i<shift+period; i++) {
      val = sqGetValue(symbol, timeframe, computedFrom, i);

      if(val > maxnum) {
         maxnum = val;
         index = i;
      }
   }

   return(index);
}

//+------------------------------------------------------------------+

double sqLowest(string symbol, int timeframe, int computedFrom, int period, int shift) {
   double minnum = 100000000;
   double val;

   for(int i=shift; i<shift+period; i++) {
      val = sqGetValue(symbol, timeframe, computedFrom, i);

      if(val < minnum) {
         minnum = val;
      }
   }

   return(minnum);
}

//+------------------------------------------------------------------+

double sqLowestIndex(string symbol, int timeframe, int computedFrom, int period, int shift) {
   double minnum = 100000000;
   int index;
   double val;

   for(int i=shift; i<shift+period; i++) {
      val = sqGetValue(symbol, timeframe, computedFrom, i);

      if(val < minnum) {
         minnum = val;
         index = i;
      }
   }

   return(index);
}

//+------------------------------------------------------------------+

double sqGetValue(string symbol, int timeframe, int computedFrom, int shift) {
   double val = 0;
   
   if(symbol == "NULL" || symbol == "Current") {
      switch(computedFrom) {
         case PRICE_OPEN: val = iOpen(NULL, timeframe, shift); break;
         case PRICE_HIGH: return iHigh(NULL, timeframe, shift); break;
         case PRICE_LOW: val = iLow(NULL, timeframe, shift); break;
         case PRICE_CLOSE: val = iClose(NULL, timeframe, shift); break;
         case PRICE_MEDIAN: val = (iHigh(NULL, timeframe, shift)+iLow(NULL, timeframe, shift))/2; break;
         case PRICE_TYPICAL: val = (iHigh(NULL, timeframe, shift)+iLow(NULL, timeframe, shift)+iClose(NULL, timeframe, shift))/3; break;
         case PRICE_WEIGHTED: val = (iHigh(NULL, timeframe, shift)+iLow(NULL, timeframe, shift)+iClose(NULL, timeframe, shift)+iClose(NULL, timeframe, shift))/4; break;
      }

   } else {
      switch(computedFrom) {
         case PRICE_OPEN: val = iOpen(symbol, timeframe, shift); break;
         case PRICE_HIGH: val = iHigh(symbol, timeframe, shift); break;
         case PRICE_LOW: val = iLow(symbol, timeframe, shift); break;
         case PRICE_CLOSE: val = iClose(symbol, timeframe, shift); break;
         case PRICE_MEDIAN: val = (iHigh(symbol, timeframe, shift)+iLow(symbol, timeframe, shift))/2; break;
         case PRICE_TYPICAL: val = (iHigh(symbol, timeframe, shift)+iLow(symbol, timeframe, shift)+iClose(symbol, timeframe, shift))/3; break;
         case PRICE_WEIGHTED: val = (iHigh(symbol, timeframe, shift)+iLow(symbol, timeframe, shift)+iClose(symbol, timeframe, shift)+iClose(symbol, timeframe, shift))/4; break;
      }
   }

   return roundValue(val);
}

//+------------------------------------------------------------------+

void sqDrawUpArrow(int shift) {
   string name = StringConcatenate("Arrow_", MathRand());

   ObjectCreate(name, OBJ_ARROW, 0, Time[shift], Low[shift]-20*Point); //draw an up arrow
   ObjectSet(name, OBJPROP_STYLE, STYLE_SOLID);
   ObjectSet(name, OBJPROP_ARROWCODE, SYMBOL_ARROWUP);
   ObjectSet(name, OBJPROP_COLOR, Green);
}

//+------------------------------------------------------------------+

void sqDrawDownArrow(int shift) {
   string name = StringConcatenate("Arrow_", MathRand());

   ObjectCreate(name, OBJ_ARROW, 0, Time[shift], High[shift]+140*Point); //draw an down arrow
   ObjectSet(name, OBJPROP_STYLE, STYLE_SOLID);
   ObjectSet(name, OBJPROP_ARROWCODE, SYMBOL_ARROWDOWN);
   ObjectSet(name, OBJPROP_COLOR, Red);
}

//+------------------------------------------------------------------+

double sqDaily(string symbol, int tf, string mode, int shift) {
   if(symbol == "NULL" || symbol == "Current") {
      if(mode == "Open") {
         return(iOpen(NULL, PERIOD_D1, shift));
      }
      if(mode == "Close") {
         return(iClose(NULL, PERIOD_D1, shift));
      }
      if(mode == "High") {
         return(iHigh(NULL, PERIOD_D1, shift));
      }
      if(mode == "Low") {
         return(iLow(NULL, PERIOD_D1, shift));
      }

   } else {
      if(mode == "Open") {
         return(iOpen(symbol, PERIOD_D1, shift));
      }
      if(mode == "Close") {
         return(iClose(symbol, PERIOD_D1, shift));
      }
      if(mode == "High") {
         return(iHigh(symbol, PERIOD_D1, shift));
      }
      if(mode == "Low") {
         return(iLow(symbol, PERIOD_D1, shift));
      }
   }                 

   return(-1);
}

//+------------------------------------------------------------------+

int getHHMM(string time){
   string result[];           
   int k = StringSplit(time, ':', result);
   
   if(k == 2){
      int hour = StrToInteger(StringSubstr(result[0], 0, 1) == "0" ? StringSubstr(result[0], 1, 1) : result[0]);
      int minute = StrToInteger(StringSubstr(result[1], 0, 1) == "0" ? StringSubstr(result[1], 1, 1) : result[0]);
      return (hour * 100) + minute;
   }
   else {
      Print("Incorrect time value format. Value: '" + time + "'");
      return 0;
   }
}

//+------------------------------------------------------------------+

string sqGetDate(int day, int month, int year) {
   string strMonth = month;
   if(month < 10) strMonth = "0"+strMonth;

   string strDay = day;
   if(day < 10) strDay = "0"+strDay;

   return(StringConcatenate(year, ".", strMonth, ".", strDay));
}

//+------------------------------------------------------------------+

string sqGetTime(int hour, int minute, int second) {
   string strHour = hour;
   if(hour < 10) strHour = "0"+strHour;

   string strMinute = minute;
   if(minute < 10) strMinute = "0"+strMinute;

   string strSecond = second;
   if(second < 10) strSecond = "0"+strSecond;

   return(StringConcatenate(strHour, ":", strMinute, ":", strSecond));
}

//+------------------------------------------------------------------+

int getSQTime(datetime time){
   int minutesToday = (time / 60) % (24 * 60);
   int hours = minutesToday / 60;
   int minutes = minutesToday % 60;
   
   return hours*100 + minutes;
}

//+------------------------------------------------------------------+

double sqSafeDivide(double var1, double var2) {
   if(var2 == 0) return(100000000);
   return(var1/var2);
}

//+------------------------------------------------------------------+
//+ Candle Pattern functions
//+------------------------------------------------------------------+

bool sqBearishEngulfing(string symbol, int timeframe, int shift) {
   double O = sqOpen(symbol, timeframe, shift);
   double O1 = sqOpen(symbol, timeframe, shift+1);
   double C = sqClose(symbol, timeframe, shift);
   double C1 = sqClose(symbol, timeframe, shift+1);

   double ocDiff = NormalizeDouble(O-C, _Digits);
   double o1c1Diff = NormalizeDouble(C1-O1, _Digits);

   if ((C1>O1)&&(O>C)&&(O>=C1)&&(O1>=C)&&(ocDiff>o1c1Diff)) {
      return(true);
   }

   return(false);
}

//+------------------------------------------------------------------+

bool sqBullishEngulfing(string symbol, int timeframe, int shift) {
   double O = sqOpen(symbol, timeframe, shift);
   double O1 = sqOpen(symbol, timeframe, shift+1);
   double C = sqClose(symbol, timeframe, shift);
   double C1 = sqClose(symbol, timeframe, shift+1);

   double coDiff = NormalizeDouble(C-O, _Digits);
   double o1c1Diff = NormalizeDouble(O1-C1, _Digits);
   
   if ((O1>C1)&&(C>O)&&(C>=O1)&&(C1>=O)&&(coDiff>o1c1Diff)) {
      return(true);
   }

   return(false);
}

//+------------------------------------------------------------------+

bool sqDarkCloudCover(string symbol, int timeframe, int shift) {
   double L = sqLow(symbol, timeframe, shift);
   double H = sqHigh(symbol, timeframe, shift);

   double O = sqOpen(symbol, timeframe, shift);
   double O1 = sqOpen(symbol, timeframe, shift+1);
   double C = sqClose(symbol, timeframe, shift);
   double C1 = sqClose(symbol, timeframe, shift+1);
   
 	double tickSize = sqGetPointCoef(symbol);

 	double Piercing_Line_Ratio = 0.5f;
 	double Piercing_Candle_Length = 10.0f;
 	
 	double HL = NormalizeDouble(H-L, _Digits);
 	double OC = NormalizeDouble(O-C, _Digits);
 	double OC_HL = HL != 0 ? NormalizeDouble(OC/HL, 6) : 0;
 	double O1C1_D2 = NormalizeDouble((O1+C1)/2, _Digits);
 	double PCL_MTS = NormalizeDouble(Piercing_Candle_Length*tickSize, _Digits);
 			
 	if(C1 > O1 && O1C1_D2 > C && O > C && C > O1 && OC_HL > Piercing_Line_Ratio && HL >= PCL_MTS) {
 		return true;
 	}

   return(false);
}

//+------------------------------------------------------------------+

bool sqDoji(string symbol, int timeframe, int shift) {
   double diff = NormalizeDouble(MathAbs(sqOpen(symbol, timeframe, shift) - sqClose(symbol, timeframe, shift)), _Digits);
   double coef = NormalizeDouble(sqGetPointCoef(symbol) * 0.6, _Digits); 
   
   if(diff < coef) {
      return(true);
   }
   
   return(false);
}

//+------------------------------------------------------------------+

bool sqHammer(string symbol, int timeframe, int shift) {
   double H = sqHigh(symbol, timeframe, shift);
   double L = sqLow(symbol, timeframe, shift);
   double L1 = sqLow(symbol, timeframe, shift+1);
   double L2 = sqLow(symbol, timeframe, shift+2);
   double L3 = sqLow(symbol, timeframe, shift+3);

   double O = sqOpen(symbol, timeframe, shift);
   double C = sqClose(symbol, timeframe, shift);
   double CL = H-L;

   double BodyLow, BodyHigh;
   double Candle_WickBody_Percent = 0.9;
   double CandleLength = 12;

   if (O > C) {
      BodyHigh = O;
      BodyLow = C;
   } else {
      BodyHigh = C;
      BodyLow = O;
   }

   double LW = NormalizeDouble(BodyLow - L, _Digits);
   double UW = NormalizeDouble(H - BodyHigh, _Digits);
   double BLa = NormalizeDouble(MathAbs(O - C), _Digits);
   double BL90 = NormalizeDouble(BLa * Candle_WickBody_Percent, _Digits);
   
   double pipValue = sqGetPointCoef(symbol);
   
   double LW_D2 = NormalizeDouble(LW / 2, _Digits);
   double LW_D3 = NormalizeDouble(LW / 3, _Digits);
   double LW_D4 = NormalizeDouble(LW / 4, _Digits);
   double BL90_M2 = NormalizeDouble(2 * BL90, _Digits);
   double CL_MPV = NormalizeDouble(CandleLength * pipValue, _Digits);
     
   if(L <= L1 && L < L2 && L < L3)  {
 		if(LW_D2 > UW && UW > BL90_M2 && CL >= CL_MPV && O != C && LW_D3 <= UW && LW_D4 <= UW)  {
    	  	return(true);
      }
      if(LW_D3 > UW && UW > BL90_M2 && CL >= CL_MPV && O != C && LW_D4 <= UW)  {
      	return(true);
      }
      if(LW_D4 > UW && UW > BL90_M2 && CL >= CL_MPV && O != C)  {
    	  	return(true);
      }
   }
     
   return(false);
}

//+------------------------------------------------------------------+

bool sqPiercingLine(string symbol, int timeframe, int shift) {
   double L = sqLow(symbol, timeframe, shift);
   double H = sqHigh(symbol, timeframe, shift);

   double O = sqOpen(symbol, timeframe, shift);
   double O1 = sqOpen(symbol, timeframe, shift+1);
   double C = sqClose(symbol, timeframe, shift);
   double C1 = sqClose(symbol, timeframe, shift+1);
   
 	double tickSize = sqGetPointCoef(symbol);

 	double Piercing_Line_Ratio = 0.5f;
 	double Piercing_Candle_Length = 10.0f;
 	
 	double HL = NormalizeDouble(H-L, _Digits);
 	double CO = NormalizeDouble(C-O, _Digits);
 	double CO_HL = HL != 0 ? NormalizeDouble(CO/HL, 6) : 0;
 	double O1C1_D2 = NormalizeDouble((O1+C1)/2, _Digits);
 	double PCL_MTS = NormalizeDouble(Piercing_Candle_Length*tickSize, _Digits);
 			
 	if(C1 < O1 && O1C1_D2 < C && O < C && CO_HL > Piercing_Line_Ratio && HL >= PCL_MTS) {
 		return true;
 	}

   return(false);
}

//+------------------------------------------------------------------+

bool sqShootingStar(string symbol, int timeframe, int shift) {
   double L = sqLow(symbol, timeframe, shift);
   double H = sqHigh(symbol, timeframe, shift);
   double H1 = sqHigh(symbol, timeframe, shift + 1);
   double H2 = sqHigh(symbol, timeframe, shift + 2);
   double H3 = sqHigh(symbol, timeframe, shift + 3);

   double O = sqOpen(symbol, timeframe, shift);
   double C = sqClose(symbol, timeframe, shift);
   double CL = NormalizeDouble(H - L, _Digits);

   double BodyLow, BodyHigh;
   double Candle_WickBody_Percent = 0.9;
   double CandleLength = 12;

   if (O > C) {
      BodyHigh = O;
      BodyLow = C;
   } else {
      BodyHigh = C;
      BodyLow = O;
   }

   double LW = NormalizeDouble(BodyLow - L, _Digits);
   double UW = NormalizeDouble(H - BodyHigh, _Digits);
   double BLa = NormalizeDouble(MathAbs(O - C), _Digits);
   double BL90 = NormalizeDouble(BLa * Candle_WickBody_Percent, _Digits);
   
   double pipValue = sqGetPointCoef(symbol);
   
   double UW_D2 = NormalizeDouble(UW / 2, _Digits);
   double UW_D3 = NormalizeDouble(UW / 3, _Digits);
   double UW_D4 = NormalizeDouble(UW / 4, _Digits);
   double BL90_M2 = NormalizeDouble(2 * BL90, _Digits);
   double CL_MPV = NormalizeDouble(CandleLength * pipValue, _Digits);

   if(H >= H1 && H > H2 && H > H3)  {
      if(UW_D2 > LW && UW > BL90_M2 && CL >= CL_MPV && O != C && UW_D3 <= LW && UW_D4 <= LW)  {
         return(true);
      }
      if(UW_D3 > LW && UW > BL90_M2 && CL >= CL_MPV && O != C && UW_D4 <= LW)  {
         return(true);
      }
      if(UW_D4 > LW && UW > BL90_M2 && CL >= CL_MPV && O != C)  {
         return(true);
      }
   }

   return(false);
} 


//+------------------------------------------------------------------+

double sqOpen(string symbol, int timeframe, int shift) {
   if(symbol == "NULL") {
      return (iOpen(NULL, timeframe, shift));
   } else {
      return (iOpen(symbol, timeframe, shift));
   }
}   

//+------------------------------------------------------------------+

double sqHigh(string symbol, int timeframe, int shift) {
   if(symbol == "NULL") {
      return (iHigh(NULL, timeframe, shift));
   } else {
      return (iHigh(symbol, timeframe, shift));
   }
}   

//+------------------------------------------------------------------+

double sqLow(string symbol, int timeframe, int shift) {
   if(symbol == "NULL") {
      return (iLow(NULL, timeframe, shift));
   } else {
      return (iLow(symbol, timeframe, shift));
   }
}   

//+------------------------------------------------------------------+

double sqClose(string symbol, int timeframe, int shift) {
   if(symbol == "NULL") {
      return (iClose(NULL, timeframe, shift));
   } else {
      return (iClose(symbol, timeframe, shift));
   }
}   


//+------------------------------------------------------------------+

bool sqTradeRecentlyClosed(string symbol, int magicNumber, bool checkThisBar, bool checkThisMinute) {
	int ordersChecked = 0;

	string strCurrentTimeMinutes = TimeToStr( TimeCurrent(), TIME_DATE|TIME_MINUTES);

   for (int cc = OrdersHistoryTotal() - 1; cc >= 0; cc--) {
      if (OrderSelect(cc, SELECT_BY_POS,MODE_HISTORY)) {

         // skip pending orders
         if(OrderType() == OP_BUYSTOP || OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLSTOP || OrderType() == OP_SELLLIMIT) {
            continue;
         }

         if(magicNumber != 0 && OrderMagicNumber() != magicNumber) continue;

         if(symbol != "Any") {
            if(symbol == "NULL" || symbol == "Current") {
               symbol = Symbol();
            }

            if(OrderSymbol() != symbol) continue;
         }

         ordersChecked++;
			if(ordersChecked > 10) {
				// check only the very last 10 orders
				break;
			}

			if(checkThisBar) {
				if(OrderCloseTime() >= Time[0]) {
					// order finished this bar
					return(true);
				}
			}

			if(checkThisMinute) {
			   string strCloseTimeMinutes = TimeToStr( OrderCloseTime(), TIME_DATE|TIME_MINUTES);

				if(strCurrentTimeMinutes == strCloseTimeMinutes) {
					// order finished this minute
					return(true);
				}
			}
		}
   }

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

double roundDown(double value, int decimals) {
  	double p = 0;
  	
  	switch(decimals) {
  		case 0: return (int) value; 
  		case 1: p = 10; break;
  		case 2: p = 100; break;
  		case 3: p = 1000; break;
  		case 4: p = 10000; break;
  		case 5: p = 100000; break;
  		case 6: p = 1000000; break;
  		default: p = MathPow(10, decimals);
  	}

  	value = value * p;
  	double tmp = MathFloor(value + 0.00000001);
  	return NormalizeDouble(tmp/p, decimals);
}

//+------------------------------------------------------------------+

class CSQTime {
public:

   datetime setHHMM(datetime time, string hhmm) {
      string date = TimeToStr(time,TIME_DATE);//"yyyy.mm.dd"
      return (StrToTime(date + " " + hhmm));
   }

   //+------------------------------------------------------------------+

   datetime correctDayStart(datetime time) {
      MqlDateTime strTime;
      TimeToStruct(time, strTime);
      strTime.hour = 0;
      strTime.min = 0;
      strTime.sec = 0;
      return (StructToTime(strTime));
   }
   

   //+------------------------------------------------------------------+

   datetime correctDayEnd(datetime time) {
      MqlDateTime strTime;
      TimeToStruct(time, strTime);
      strTime.hour = 23;
      strTime.min = 59;
      strTime.sec = 59;
      return (StructToTime(strTime));
   }
      
   //+------------------------------------------------------------------+

   datetime addDays(datetime time, int days) {
      int oneDay = 60 * 60 * 24;
      
      return (time + (days * oneDay));
   }
   
   //+------------------------------------------------------------------+

   datetime setDayOfWeek(datetime time, int desiredDow) {
      int dow = convertToSQDOW(TimeDayOfWeek(time));
      desiredDow = convertToSQDOW(desiredDow);
      
      int diffInDays = desiredDow - dow;
      
      //Print("DiffInDays: ", diffInDays, ", dow: ", dow, ", desiredDow: ", desiredDow);
      
      return addDays(time, diffInDays);
   }  
   
   //+------------------------------------------------------------------+

   /**
    * converts from MT DOW format: 0 = Sunday, 1 = Monday ... 6 = Saturday
    * to SQ DOW format: 1 = Monday, 2 = Tuesday ... 7 = Sunday
   */
   int convertToSQDOW(int dow) {
      if(dow == 0) dow = 7;
      
      return(dow);
   } 
};

// create variable for class instance (required)
CSQTime* SQTime;

//+------------------------------------------------------------------+

bool sqEvaluateFuzzySignal(int conditionsCount, int minTrueConditions) {

	bool signalValue = false;
  int trueConditionsCount = 0;
   
  if(minTrueConditions <= 0) {
  	minTrueConditions = 1;
  }
	    
	for(int i=0; i<conditionsCount; i++) {
		bool value = cond[i];
				
		if(value) {
			trueConditionsCount++;
		}
				
		if(trueConditionsCount >= minTrueConditions) {
			signalValue = true;
			break;
		}
	}
			
	return(signalValue);
}

//+------------------------------------------------------------------+

string correctSymbol(string symbol){
  if(symbol == "NULL" || symbol == "Current" || symbol == "Same as main chart") {
      return Symbol();
  }
  else return symbol;
}

//+------------------------------------------------------------------+

double sqFixMarketPrice(double price, string symbol){             
   symbol = correctSymbol(symbol);
   double tickSize = MarketInfo(symbol, MODE_TICKSIZE);      
   int digits = (int) MarketInfo(symbol, MODE_DIGITS);
   double finalPrice = tickSize * MathRound(NormalizeDouble(price, digits) / tickSize);
   return NormalizeDouble(finalPrice, digits);
}

//+------------------------------------------------------------------+

bool sqIsUptrend(string symbol, int timeframe, int method) {
   if(method == 0) {
      return (iClose(symbol, timeframe, 1) > sqMA(symbol, timeframe, 200, 0, MODE_SMA, PRICE_CLOSE, 1));      
	 }
	 return(false);	
}

//+------------------------------------------------------------------+

bool sqIsDowntrend(string symbol, int timeframe, int method) {
   if(method == 0) {
      return (iClose(symbol, timeframe, 1) < sqMA(symbol, timeframe, 200, 0, MODE_SMA, PRICE_CLOSE, 1));      
	 }
	 return(false);	
}


double roundValue(double value){
    return NormalizeDouble(value + 0.0000000001, 6);
}

//+------------------------------------------------------------------+

double sqBarRange(string symbol, int timeframe, int shift) {
  double range;
   if(symbol == "NULL" || symbol == "Current") {
      range = iHigh(NULL, timeframe, shift) - iLow(NULL, timeframe, shift);
   } else {
      range = iHigh(symbol, timeframe, shift) - iLow(symbol, timeframe, shift);
   }
   
   return roundValue(range);
}

//+------------------------------------------------------------------+

double sqHeikenAshi(string symbol, int timeframe, string mode, int shift) {
   if(symbol == "NULL" || symbol == "Current") {
      if(mode == "Open") {
         return(NormalizeDouble(iCustom(NULL, timeframe, "SqHeikenAshi", 0,0,0,0, 2, shift), 6));
      }
      if(mode == "Close") {
         return(NormalizeDouble(iCustom(NULL, timeframe, "SqHeikenAshi", 0,0,0,0, 3, shift), 6));
      }
      if(mode == "High") {
         return(NormalizeDouble(MathMax(iCustom( NULL, timeframe, "SqHeikenAshi", 0,0,0,0, 0, shift), iCustom( NULL, timeframe, "SqHeikenAshi", 0,0,0,0, 1, shift)), 6));
      }
      if(mode == "Low") {
         return(NormalizeDouble(MathMin(iCustom( NULL, timeframe, "SqHeikenAshi", 0,0,0,0, 0, shift), iCustom( NULL, timeframe, "SqHeikenAshi", 0,0,0,0, 1, shift)), 6));
      }

   } else {
      if(mode == "Open") {
         return(NormalizeDouble(iCustom( symbol, timeframe, "SqHeikenAshi", 0,0,0,0, 2, shift), 6));
      }
      if(mode == "Close") {
         return(NormalizeDouble(iCustom( symbol, timeframe, "SqHeikenAshi", 0,0,0,0, 3, shift), 6));
      }
      if(mode == "High") {
         return(NormalizeDouble(MathMax(iCustom( symbol, timeframe, "SqHeikenAshi", 0,0,0,0, 0, shift), iCustom( symbol, timeframe, "SqHeikenAshi", 0,0,0,0, 1, shift)), 6));
      }
      if(mode == "Low") {
         return(NormalizeDouble(MathMin(iCustom( symbol, timeframe, "SqHeikenAshi", 0,0,0,0, 0, shift), iCustom( symbol, timeframe, "SqHeikenAshi", 0,0,0,0, 1, shift)), 6));
      }
   }

   return(-1);
}

//+------------------------------------------------------------------+

double sqBiggestRange(string symbol, int timeframe, int period, int shift) {
   double maxnum = -100000000;
   double range;

   for(int i=shift; i<shift+period; i++) {
      if(symbol == "NULL" || symbol == "Current") {
         range = iHigh(NULL, timeframe, i) - iLow(NULL, timeframe, i);
      } else {
         range = iHigh(symbol, timeframe, i) - iLow(symbol, timeframe, i);
      }

      if(range > maxnum) {
         maxnum = range;
      }
   }

   return roundValue(maxnum);
}

//+------------------------------------------------------------------+

double sqSmallestRange(string symbol, int timeframe, int period, int shift) {
   double minnum = 100000000;
   double range;

   for(int i=shift; i<shift+period; i++) {
      if(symbol == "NULL" || symbol == "Current") {
         range = iHigh(NULL, timeframe, i) - iLow(NULL, timeframe, i);
      } else {
         range = iHigh(symbol, timeframe, i) - iLow(symbol, timeframe, i);
      }

      if(range < minnum) {
         minnum = range;
      }
   }

   return roundValue(minnum);
}

//+------------------------------------------------------------------+

double sqMA(string symbol, int timeframe, int ma_period, int ma_shift, int ma_method, int applied_price, int shift) {
   return roundValue(iMA(symbol, timeframe, ma_period, ma_shift, ma_method, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqTEMA(string symbol, int timeframe, int ma_period, int applied_price, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqTEMA", ma_period, applied_price, 0, shift));
}

//+------------------------------------------------------------------+

double sqIchimoku(string symbol, int timeframe, int tenkanPeriod, int kijunPeriod, int senkouPeriod, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqIchimoku", tenkanPeriod, kijunPeriod, senkouPeriod, line, shift));
}

//+------------------------------------------------------------------+

double sqAroon(string symbol, int timeframe, int period, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqAroon", period, false, false, line, shift));
}

//+------------------------------------------------------------------+

double sqBBWidthRatio(string symbol, int timeframe, int period, double deviation, int appliedPrice, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqBBWidthRatio", period, deviation, appliedPrice, 0, shift));
}

//+------------------------------------------------------------------+

double sqAvgVolume(string symbol, int timeframe, int period, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqAvgVolume", period, 1, shift));
}
            
//+------------------------------------------------------------------+

double sqFibo(string symbol, int timeframe, int fiboRange, double fiboLevel) {
   return roundValue(iCustom(symbol, timeframe, "SqFibo", fiboRange, fiboLevel, 0, 0, 0));
}

//+------------------------------------------------------------------+

double sqLinReg(string symbol, int timeframe, int period, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqLinReg", period, line, 0, shift));
}

//+------------------------------------------------------------------+

double sqPivots(string symbol, int timeframe, int startHour, int startMinute, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqPivots", startHour, startMinute, line, shift));
}

//+------------------------------------------------------------------+

double sqQQE(string symbol, int timeframe, int rsiPeriod, int sF, double wF, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqQQE", rsiPeriod, sF, wF, line, shift));
}

//+------------------------------------------------------------------+

double sqKeltnerChannel(string symbol, int timeframe, int period, double Deviation, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqKeltnerChannel", period, Deviation, line, shift));
}

//+------------------------------------------------------------------+

double sqMTKeltnerChannel(string symbol, int timeframe, int period, double Deviation, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqMTKeltnerChannel", period, Deviation, line, shift));
}

//+------------------------------------------------------------------+

double sqRSI(string symbol, int timeframe, int period, int applied_price, int shift) {
   return roundValue(iRSI(symbol, timeframe, period, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqCCI(string symbol, int timeframe, int period, int applied_price, int shift) {
   return roundValue(iCCI(symbol, timeframe, period, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqAC(string symbol, int timeframe, int shift) {
   return roundValue(iAC(symbol, timeframe, shift));
}

//+------------------------------------------------------------------+

double sqADX(string symbol, int timeframe, int period, int line, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqADX", period, line, shift));
}

//+------------------------------------------------------------------+

double sqATR(string symbol, int timeframe, int period, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqATR", period, 0, shift));
}

//+------------------------------------------------------------------+

double sqAO(string symbol, int timeframe, int shift) {
   return roundValue(iAO(symbol, timeframe, shift));
}

//+------------------------------------------------------------------+

double sqBearsPower(string symbol, int timeframe, int period, int applied_price, int shift) {
   return roundValue(iBearsPower(symbol, timeframe, period, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqBullsPower(string symbol, int timeframe, int period, int applied_price, int shift) {
   return roundValue(iBullsPower(symbol, timeframe, period, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqDeMarker(string symbol, int timeframe, int period, int shift) {
   return roundValue(iDeMarker(symbol, timeframe, period, shift));
}

//+------------------------------------------------------------------+

double sqMACD(string symbol, int timeframe, int fast_ema_period, int slow_ema_period, int signal_period, int applied_price, int mode, int shift) {
   return roundValue(iMACD(symbol, timeframe, fast_ema_period, slow_ema_period, signal_period, applied_price, mode, shift));
} 

//+------------------------------------------------------------------+

double sqMomentum(string symbol, int timeframe, int period, int applied_price, int shift) {
   return roundValue(iMomentum(symbol, timeframe, period, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqStochastic(string symbol, int timeframe, int Kperiod, int Dperiod, int slowing, int method, int price_field, int mode, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqStochastic", Kperiod, Dperiod, slowing, method, price_field, mode, shift));
} 

//+------------------------------------------------------------------+

double sqOsMA(string symbol, int timeframe, int fast_ema_period, int slow_ema_period, int signal_period, int applied_price, int shift) {
   return roundValue(iOsMA(symbol, timeframe, fast_ema_period, slow_ema_period, signal_period, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqBands(string symbol, int timeframe, int period, double deviation, int bands_shift, int applied_price, int mode, int shift) {
   return roundValue(iBands(symbol, timeframe, period, deviation, bands_shift, applied_price, mode+1, shift));
}
    
//+------------------------------------------------------------------+

double sqBBRange(string symbol, int timeframe, int period, double deviation, int applied_price, int shift) {
   return roundValue(iBands(symbol, timeframe, period, deviation, 0, applied_price, 1, shift) - iBands(symbol, timeframe, period, deviation, 0, applied_price, 2, shift));
}

//+------------------------------------------------------------------+

double sqSAR(string symbol, int timeframe, double step, double maximum, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqParabolicSAR", step, maximum, 0, shift));
   //return roundValue(iSAR(symbol, timeframe, step, maximum, shift));
}

//+------------------------------------------------------------------+

double sqStdDev(string symbol, int timeframe, int ma_period, int ma_shift, int ma_method, int applied_price, int shift) {
   return roundValue(iStdDev(symbol, timeframe, ma_period, ma_shift, ma_method, applied_price, shift));
}

//+------------------------------------------------------------------+

double sqHighestInRange(string symbol, int timeframe, string timeFrom, string timeTo, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqHighestInRange", timeFrom, timeTo, 0, shift));
}
     
//+------------------------------------------------------------------+

double sqLowestInRange(string symbol, int timeframe, string timeFrom, string timeTo, int shift) {
   return roundValue(iCustom(symbol, timeframe, "SqLowestInRange", timeFrom, timeTo, 0, shift));
}

//+------------------------------------------------------------------+

bool sqIchimokuChikouSpanCross(int bullishOrBearish, string symbol, int timeframe, int tenkanPeriod, int kijunPeriod, int senkouPeriod, int shift, int signalStrength) {
   double chikouSpan1 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 4, shift+1);
	double chikouSpan0 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 4, shift);
	double c = iClose(symbol, timeframe, shift);
	double kumoTop = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 2, shift);
	double kumoBottom = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 3, shift);
	if(kumoBottom > kumoTop) {
		double temp = kumoBottom;
		kumoBottom = kumoTop;
		kumoTop = temp;
	}
		
	bool signal;
	
	if(bullishOrBearish == -1) {
	   // bearish;
	   signal = (chikouSpan1 >= c) && (chikouSpan0 < c) && (chikouSpan1 > chikouSpan0);
		
		if(signalStrength == 2) {
			// for strong signal the cross should happen below kumo cloud
			signal = signal && (c < kumoBottom);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (c < kumoTop);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;;
		}
		
		return signal;
		
	} else if(bullishOrBearish == 1) { 
	   // bullish
	   signal = (chikouSpan1 <= c) && (chikouSpan0 > c) && (chikouSpan1 < chikouSpan0);
	   
	   if(signalStrength == 2) {
			// for strong signal the cross should happen above kumo cloud
			signal = signal && (c > kumoTop);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (c > kumoBottom);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;
		}

		return signal;
		
	} else {
			return false;
	}
}


//+------------------------------------------------------------------+

bool sqIchimokuSenkouSpanCross(int bullishOrBearish, string symbol, int timeframe, int tenkanPeriod, int kijunPeriod, int senkouPeriod, int shift, int signalStrength) {
	double senkouSpanA1 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 2, shift+1);
	double senkouSpanA0 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 2, shift);
	double senkouSpanB1 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 3, shift+1);
	double senkouSpanB0 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 3, shift);
	double c = iClose(symbol, timeframe, shift);
			
	double kumoTop = MathMax(senkouSpanA0,  senkouSpanB0); 
	double kumoBottom = MathMin(senkouSpanA0,  senkouSpanB0); 
		
	bool signal;
	
	if(bullishOrBearish == -1) {
	   // bearish;
	   signal = (senkouSpanA1 > senkouSpanB1) && (senkouSpanA0 < senkouSpanB0);
		
		if(signalStrength == 2) {
			// for strong signal the cross should happen below kumo cloud
			signal = signal && (c < kumoBottom);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (c < kumoTop);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;;
		}
		
		return signal;
		
	} else if(bullishOrBearish == 1) { 
	   // bullish
	   signal = (senkouSpanA1 < senkouSpanB1) && (senkouSpanA0 > senkouSpanB0);
	   
	   if(signalStrength == 2) {
			// for strong signal the cross should happen above kumo cloud
			signal = signal && (c > kumoTop);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (c > kumoBottom);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;
		}

		return signal;
		
	} else {
			return false;
	}
}

//+------------------------------------------------------------------+

bool sqIchimokuKijunSenCross(int bullishOrBearish, string symbol, int timeframe, int tenkanPeriod, int kijunPeriod, int senkouPeriod, int shift, int signalStrength) {
	double o = iOpen(symbol, timeframe, shift);
	double c = iClose(symbol, timeframe, shift);
	double kijun = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 1, shift);

	double kumoTop = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 2, shift);
	double kumoBottom = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 3, shift);
	if(kumoBottom > kumoTop) {
		double temp = kumoBottom;
		kumoBottom = kumoTop;
		kumoTop = temp;
	}
		
	bool signal;
	
	if(bullishOrBearish == -1) {
	   // bearish;
	   signal = (o > kijun) && c < kijun;
		
		if(signalStrength == 2) {
			// for strong signal the cross should happen below kumo cloud
			signal = signal && (kijun < kumoBottom);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (kijun < kumoTop);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;;
		}
		
		return signal;
		
	} else if(bullishOrBearish == 1) { 
	   // bullish
	   signal = (o < kijun) && c > kijun;
	   
		if(signalStrength == 2) {
			// for strong signal the cross should happen above kumo cloud
			signal = signal && (kijun > kumoTop);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (kijun > kumoBottom);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;
		}

		return signal;
		
	} else {
			return false;
	}
}

//+------------------------------------------------------------------+

bool sqIchimokuTenkanKijunCross(int bullishOrBearish, string symbol, int timeframe, int tenkanPeriod, int kijunPeriod, int senkouPeriod, int shift, int signalStrength) {
	double tenkan1 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 0, shift+1);
	double tenkan0 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 0, shift);

	double kijun1 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 1, shift+1);
	double kijun0 = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 1, shift);

	double kumoTop = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 2, shift);
	double kumoBottom = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 3, shift);
	if(kumoBottom > kumoTop) {
		double temp = kumoBottom;
		kumoBottom = kumoTop;
		kumoTop = temp;
	}
		
	bool signal;
	
	if(bullishOrBearish == -1) {
	   // bearish;
	   signal = (tenkan1 > kijun1) && tenkan0 < kijun0;
		
		if(signalStrength == 2) {
			// for strong signal the cross should happen below kumo cloud
			signal = signal && (tenkan0 < kumoBottom);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (tenkan0 < kumoTop);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;;
		}
		
		return signal;
		
	} else if(bullishOrBearish == 1) { 
	   // bullish
	   signal = (tenkan1 < kijun1) && tenkan0 > kijun0;
	   
		if(signalStrength == 2) {
			// for strong signal the cross should happen above kumo cloud
			signal = signal && (tenkan0 > kumoTop);
			
		} else if(signalStrength == 1) {
			// for neutral signal the cross should happen in kumo cloud
			signal = signal && (tenkan0 > kumoBottom);
			
		} else if(signalStrength == 0) {
			// do nothing, if there is cross signal is always at least weak
			
		} else {
			return false;
		}

		return signal;
		
	} else {
			return false;
	}
}

//+------------------------------------------------------------------+

bool sqIchimokuKumoBreakout(int bullishOrBearish, string symbol, int timeframe, int tenkanPeriod, int kijunPeriod, int senkouPeriod, int shift) {
	double o = iOpen(symbol, timeframe, shift);
	double c = iClose(symbol, timeframe, shift);

	double kumoTop = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 2, shift);
	double kumoBottom = sqIchimoku(symbol, timeframe, tenkanPeriod, kijunPeriod, senkouPeriod, 3, shift);
	if(kumoBottom > kumoTop) {
		double temp = kumoBottom;
		kumoBottom = kumoTop;
		kumoTop = temp;
	}
		
	bool signal;
	
	if(bullishOrBearish == -1) {
	   // bearish;
	   signal = (o > kumoBottom) && c < kumoBottom;
		
		return signal;
		
	} else if(bullishOrBearish == 1) { 
	   // bullish
	   signal = (o < kumoTop) && c > kumoTop;
		
		return signal;
		
	} else {
			return false;
	}
}


//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
// ExitMethods includes
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+


// Move Stop Loss to Break Even
void sqManageSL2BE(int ticket) {

   double val = sqGetGlobalVariable(ticket, "MoveSL2BE");
   if(val == 0) {
      return;
   }

   if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
      return;
   }

   double moveSLAtValue = sqGetValueByIdentification( val );

   if(moveSLAtValue > 0) {
      double newSL = 0;
      int error;

      int valueType = sqGetGlobalVariable(ticket, "MoveSL2BEType");
      int orderType = OrderType();
      int digits = MarketInfo(OrderSymbol(), MODE_DIGITS);

      if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
         if(valueType == SLPTTYPE_RANGE) {
            moveSLAtValue = Bid - moveSLAtValue;
         }
      } else {
         if(valueType == SLPTTYPE_RANGE) {
            moveSLAtValue = Ask + moveSLAtValue;
         }
      }

      moveSLAtValue = NormalizeDouble(moveSLAtValue, digits);
      double addPips = NormalizeDouble(sqGetValueByIdentification(sqGetGlobalVariable(ticket, "SL2BEAddPips")), digits);
      
      double currentSL = OrderStopLoss();

      if(orderType == OP_BUY) {
         newSL = NormalizeDouble(OrderOpenPrice() + addPips, digits);

         if ((OrderOpenPrice() <= moveSLAtValue || sqDoublesAreEqual(OrderOpenPrice(), moveSLAtValue)) && (currentSL == 0 || currentSL < newSL) && !sqDoublesAreEqual(currentSL, newSL)) {
            Verbose("Moving SL 2 BE for order with ticket: ", OrderTicket(), " to :", newSL);
            if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit())) {
               error = GetLastError();
               Verbose("Failed, error: ", error, " - ", ErrorDescription(error),", Ask: ", Ask, ", Bid: ", Bid, " Current SL: ",  currentSL);
            }
         }

      } else { // orderType == OP_SELL
         newSL = NormalizeDouble(OrderOpenPrice() - addPips, digits);

         if ((OrderOpenPrice() >= moveSLAtValue || sqDoublesAreEqual(OrderOpenPrice(), moveSLAtValue)) && (currentSL == 0 || currentSL > newSL) && !sqDoublesAreEqual(currentSL, newSL)) {
            Verbose("Moving SL 2 BE for order with ticket: ", OrderTicket(), "  to :", newSL);
            if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit())) {
               error = GetLastError();
               Verbose("Failed, error: ", error, " - ", ErrorDescription(error),", Ask: ", Ask, ", Bid: ", Bid, " Current SL: ",  currentSL);
            }
         }
      }
   }
}
// Trailing Stop
void sqManageTrailingStop(int ticket) {

   double val = sqGetGlobalVariable(ticket, "TrailingStop");
   if(val == 0) {
      return;
   }

   if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
      return;
   }
   
   double tsValue = sqGetValueByIdentification( val );
   
   if(tsValue > 0) {
      double plValue;
      int error;

      int valueType = sqGetGlobalVariable(ticket, "TrailingStopType");
      int orderType = OrderType();
      int digits = MarketInfo(OrderSymbol(), MODE_DIGITS);

      if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
         if(valueType == SLPTTYPE_RANGE) {
            tsValue = Bid - tsValue;
         }
      } else {
         if(valueType == SLPTTYPE_RANGE) {
            tsValue = Ask + tsValue;
         }
      }

      tsValue = NormalizeDouble(tsValue, digits);

      double tsActivation = NormalizeDouble(sqGetValueByIdentification( sqGetGlobalVariable(ticket, "TrailingActivation") ), digits);
      double currentSL = OrderStopLoss();

      if(orderType == OP_BUY) {
         plValue = NormalizeDouble(Bid - OrderOpenPrice(), digits);

         if ((plValue >= tsActivation || sqDoublesAreEqual(plValue, tsActivation)) && (currentSL == 0 || currentSL < tsValue) && !sqDoublesAreEqual(currentSL, tsValue)) {
            Verbose("Moving trailing stop for order with ticket: ", OrderTicket(), " to :", tsValue);
            if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), tsValue, OrderTakeProfit())) {
               error = GetLastError();
               Verbose("Failed, error: ", error, " - ", ErrorDescription(error),", Ask: ", Ask, ", Bid: ", Bid, " Current SL: ",  currentSL);
            }
         }
      } else { // orderType == OP_SELL
         plValue = NormalizeDouble(OrderOpenPrice() - Ask, digits);

         if ((plValue >= tsActivation || sqDoublesAreEqual(plValue, tsActivation)) && (currentSL == 0 || currentSL > tsValue) && !sqDoublesAreEqual(currentSL, tsValue)) {
            Verbose("Moving trailing stop for order with ticket: ", OrderTicket(), " to :", tsValue);
            if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), tsValue, OrderTakeProfit())) {
               error = GetLastError();
               Verbose("Failed, error: ", error, " - ", ErrorDescription(error),", Ask: ", Ask, ", Bid: ", Bid, " Current SL: ",  currentSL);
            }
         }
      }
   }
}
void sqManageExitAfterXBars(int ticket) {
   int exitBars = sqGetGlobalVariable(ticket, "ExitAfterBars");
   if(exitBars > 0) {
      if (sqGetOpenBarsForOrder(ticket, exitBars+10) >= exitBars) {
         Verbose("Exit After ", exitBars, "bars - closing order with ticket: ", OrderTicket());
         sqClosePositionAtMarket(OrderLots());
      }
   }
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
// Trading Options includes
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

class CTradingOption {
public:
   virtual bool onBarUpdate() = 0;
};

//+------------------------------------------------------------------+

class CExitAtEndOfDay : public CTradingOption {
   private:
	   datetime dailyEODExitTime;
	   datetime EODTime;
	   bool closedThisDay;
   
   public:
      CExitAtEndOfDay() {
         dailyEODExitTime = D'1970.01.01';
         EODTime = D'1970.01.01';
         closedThisDay = false;
      }

      //+----------------------------------------------+

      virtual bool onBarUpdate() {
        if(!ExitAtEndOfDay) {
		      return(true);
	      }                   
                             
        onTick();

        if(!sqIsBarOpen()) {
           return(true);
        }
        
	      datetime currentTime = TimeCurrent();

	      if(currentTime > EODTime) {
	         //it is a new day
		      initTimesForCurrentDay(currentTime);
	      }

	      if(currentTime >= dailyEODExitTime) {
		      // returning false means there will be no more processing on this tick
	        // this is what we want because we don't want to be trading after close of all positions
		      return(false); 
	      }

	      return(true);
      }     
        
	   //------------------------------------------------------------------------

     virtual void onTick() {
        if(!ExitAtEndOfDay) {
		      return;
	      }
        
        datetime currentTime = TimeCurrent();
        datetime currentTimeDayStart = SQTime.correctDayStart(currentTime);    
				datetime currentTimeDayEnd = SQTime.correctDayEnd(currentTime);
        
        if(!closedThisDay && currentTime >= dailyEODExitTime) {
          // we should close all positions at midnight, so close them at the first tick of a new day
		      for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
            if (OrderSelect(cc, SELECT_BY_POS)) {
      
               if(OrderSymbol() != Symbol() || !checkMagicNumber(OrderMagicNumber())) continue;
               
               bool isLiveOrder = OrderType() == OP_BUY || OrderType() == OP_SELL;
               
               //Close all orders at the end of a day. When there is a data gap, on the first tick of new day close all pending orders and orders filled before current day start
               if(currentTimeDayEnd == EODTime || !isLiveOrder || OrderOpenTime() < currentTimeDayStart) {
                  if(isLiveOrder){
                     sqClosePositionAtMarket(OrderLots());
                  }
                  else {
                     sqDeletePendingOrder(OrderTicket());
                  }
               }
            }
         }
          
			    closedThisDay = true;
	      }
          
     }

      //+----------------------------------------------+

      void initTimesForCurrentDay(datetime currentTime) {
	      // set end time of the current day (so that we now when new day starts)
	      EODTime = SQTime.correctDayEnd(currentTime);

	      // set time of EOD
	      if(EODExitTime == "00:00" || EODExitTime == "0:00"){
	         dailyEODExitTime = EODTime;
	      }
	      else {
	         dailyEODExitTime = SQTime.setHHMM(currentTime, EODExitTime);
	      }
	      
			  closedThisDay = false;
      }
};

// create variable for class instance (required)
CExitAtEndOfDay* objExitAtEndOfDay;


class CExitOnFriday : public CTradingOption {
   private:
      datetime thisFridayExitTime; 
      datetime thisSundayBeginTime;
      datetime EOFDayTime;
      bool closedThisWeek;
       
   public:
      CExitOnFriday() {
         thisFridayExitTime = D'1970.01.01';    
         thisSundayBeginTime = D'1970.01.01';
         closedThisWeek = false;
      }

      //+----------------------------------------------+

      virtual bool onBarUpdate() {
         if(!ExitOnFriday) {
	  		    return true;
         }
         
         onTick();
         
         if(!sqIsBarOpen()) {
            return(true);
         }

         datetime currentTime = TimeCurrent();

         if(thisFridayExitTime < 100) {
            initFridayExitTime(currentTime, 0);
         }
         
         if(currentTime < thisFridayExitTime) {
            // trade normally
            return true;
         }
         
         if(currentTime < thisSundayBeginTime) {
    		   // do not allow opening new positions until sunday.
   			   // returning false means there will be no more processing on this tick.
   			   // this is what we want because we don't want to be trading after close of all positions
   			   return false;
    		}
         else {
            // new week starting
            initFridayExitTime(currentTime, DayOfWeek() == 0 ? 1 : 0); 

            return true;
         }
      }           
        
	   //------------------------------------------------------------------------

     virtual void onTick() {
        if(!ExitOnFriday) {
	  		    return;
        }
        
        datetime currentTime = TimeCurrent();      
        datetime currentTimeDayStart = SQTime.correctDayStart(currentTime);      
				datetime currentTimeDayEnd = SQTime.correctDayEnd(currentTime);

    		if(!closedThisWeek && currentTime >= thisFridayExitTime) {
   				 // time is over friday closing time, we should close the positions
   				 for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
              if (OrderSelect(cc, SELECT_BY_POS)) {
        
                 if(OrderSymbol() != Symbol() || !checkMagicNumber(OrderMagicNumber())) continue;
                 
                 bool isLiveOrder = OrderType() == OP_BUY || OrderType() == OP_SELL;
                 
                 //Close all orders at the end of Friday. When there is a data gap, on the first tick of new day close all pending orders and orders filled before current day start
                 if(currentTimeDayEnd == EOFDayTime || !isLiveOrder || OrderOpenTime() < currentTimeDayStart) {
                    if(isLiveOrder){
                        sqClosePositionAtMarket(OrderLots());
                    }
                    else {
                        sqDeletePendingOrder(OrderTicket());
                    }
                 }
              }
           }
           
   				 closedThisWeek = true;
    		} 
     }

      //+----------------------------------------------+

      void initFridayExitTime(datetime currentTime, int addDays) {
         if(addDays > 0) {
			    thisFridayExitTime = SQTime.addDays(currentTime, addDays);
	      } else {
			    thisFridayExitTime = currentTime;
	      }
	
	      // set time of EOD 
	      thisFridayExitTime = SQTime.setDayOfWeek(thisFridayExitTime, (FridayExitTime == "00:00" || FridayExitTime == "0:00") ? SATURDAY : FRIDAY);
	      thisFridayExitTime = SQTime.setHHMM(thisFridayExitTime, FridayExitTime);	
	     
        EOFDayTime = SQTime.correctDayEnd(thisFridayExitTime);
       
	      thisSundayBeginTime = SQTime.setDayOfWeek(currentTime, SUNDAY);
	      thisSundayBeginTime = SQTime.correctDayStart(thisSundayBeginTime);
        
        closedThisWeek = false;
      }
};

// create variable for class instance (required)
CExitOnFriday* objExitOnFriday;

class CLimitTimeRange : public CTradingOption {
   private:
      datetime EODTime;
      datetime dailySignalTimeRangeFrom;
      datetime dailySignalTimeRangeTo;
      bool closedThisDay;
      
   public:
      CLimitTimeRange() {
         EODTime = D'1970.01.01';
         closedThisDay = false;
      }

      //+----------------------------------------------+

      virtual bool onBarUpdate() {     
	    if(!LimitTimeRange) {
		    return true;
	    }
                                
        onTick();
           
        if(!sqIsBarOpen()) {
           return true;
        }
        
	    datetime currentTime = TimeCurrent();
	
	    if(currentTime > EODTime) {
		    // it is new day
		    initTimesForCurrentDay(currentTime);
	    }

	    if(currentTime < dailySignalTimeRangeFrom || currentTime >= dailySignalTimeRangeTo) {
		    // time is outside given range
		    // returning false means there will be no more processing on this tick
		   	// this is what we want because we don't want to be trading outside of this time range
		    
		    return false; 
	    }
	
	    return true;
	 }
        
     //------------------------------------------------------------------------

     virtual void onTick() {
        if(!LimitTimeRange) {
		    return;
	    }
        
        datetime currentTime = TimeCurrent();
        if(!closedThisDay && ExitAtEndOfRange && currentTime >= dailySignalTimeRangeTo) {
		   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
              if (OrderSelect(cc, SELECT_BY_POS)) {
        
                 if(OrderSymbol() != Symbol() || !checkMagicNumber(OrderMagicNumber())) continue;
                 
                 bool isLiveOrder = OrderType() == OP_BUY || OrderType() == OP_SELL;
               
                 if(isLiveOrder){
                     sqClosePositionAtMarket(OrderLots());
                 }
                 else {
                     sqDeletePendingOrder(OrderTicket());
                 }
              }
           }
           
		   closedThisDay = true;
	    }
     }

    //------------------------------------------------------------------------

    void initTimesForCurrentDay(datetime currentTime) {
	   // set end time of the current day (so that we now when new day starts)
	   EODTime = SQTime.correctDayEnd(currentTime);

   	   // set time of range open 
   	   dailySignalTimeRangeFrom = SQTime.setHHMM(currentTime, SignalTimeRangeFrom);
	
	   dailySignalTimeRangeTo = SQTime.setHHMM(currentTime, SignalTimeRangeTo);    
	   closedThisDay = false;
    }
};


// create variable for class instance (required)
CLimitTimeRange* objLimitTimeRange;



class CMaxTradesPerDay : public CTradingOption {
   private:
      int lastHistoryPositionChecked;
      datetime openTimeToday;
      datetime EODTime;
      bool reachedLimitToday;
      
   public:
      CMaxTradesPerDay() {
         EODTime = D'1970.01.01';
         lastHistoryPositionChecked = 0;
      }

      //+----------------------------------------------+

      virtual bool onBarUpdate() {
         if(MaxTradesPerDay <= 0) {
            return true;
         }
		
         datetime currentTime = TimeCurrent();

         if(currentTime > EODTime) {
            // it is new day
            initTimeForCurrentDay(currentTime);
         }		
		
		   if(reachedLimitToday) {
		      return false;
		   }
		   
         if(getNumberOfTradesToday() >= MaxTradesPerDay) {
            reachedLimitToday = true;
            return(false);
         }
		
         return true;
      }

      //------------------------------------------------------------------------

      void initTimeForCurrentDay(datetime currentTime) {
			// set end time of the current day (so that we now when new day starts)
         EODTime = SQTime.correctDayEnd(currentTime);

         openTimeToday = SQTime.correctDayStart(currentTime);
         
         reachedLimitToday = false;
      }
      
      //------------------------------------------------------------------------

		int getNumberOfTradesToday() {
			int todayTradesCount = 0;

			// count closed trades that started today
			int startAt = lastHistoryPositionChecked -10;
			if(startAt < 0) {
				startAt = 0;
			}

       for(int i=startAt;i<OrdersHistoryTotal();i++) {
          if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==true && OrderSymbol() == Symbol() && checkMagicNumber(OrderMagicNumber())) {
             lastHistoryPositionChecked = i;

             if(OrderOpenTime() >= openTimeToday) {
                todayTradesCount++;
             }
          }
       }

       for(i=0; i<OrdersTotal(); i++) {
          if (OrderSelect(i,SELECT_BY_POS)==true && OrderSymbol() == Symbol() && checkMagicNumber(OrderMagicNumber())) {

             if(OrderOpenTime() >= openTimeToday) {
                todayTradesCount++;
             }
          }
       }
   
			return todayTradesCount;
		}      
};

// create variable for class instance (required)
CMaxTradesPerDay* objMaxTradesPerDay;


class CMinMaxSLPT : public CTradingOption {
   private:
      
   public:
      CMinMaxSLPT() {
      }

      //+----------------------------------------------+

      virtual bool onBarUpdate() {
         return true;
      }
};

// create variable for class instance (required)
CMinMaxSLPT* objMinMaxSLPT;


double sqMMFixedAmount(string symbol, int orderType, double price, double sl, double RiskedMoney, int decimals, double LotsIfNoMM, double MaximumLots) {
   Verbose("Computing Money Management for order -  Risk fixed % of account balance");
   
   if(symbol == "NULL" || symbol == "Current") {
      symbol = _Symbol;
   }
   
   double openPrice = price > 0 ? price : (orderType == OP_BUY ? sqGetAsk(symbol) : sqGetBid(symbol));
   double LotSize=0;

   if(RiskedMoney <= 0 ) {
      Verbose("Computing Money Management - Incorrect RiskedMoney value, it must be above 0");
      return(0);
   }
   
   double PointValue = MarketInfo(symbol, MODE_TICKVALUE) / MarketInfo(symbol, MODE_TICKSIZE);   
   double Smallest_Lot = MarketInfo(symbol, MODE_MINLOT);
   double Largest_Lot = MarketInfo(symbol, MODE_MAXLOT);    
   double LotStep = MarketInfo(symbol, MODE_LOTSTEP);
		
   //Maximum drawdown of this order if we buy 1 lot 
   double oneLotSLDrawdown = PointValue * MathAbs(openPrice - sl);
		
   if(oneLotSLDrawdown > 0) {
	  LotSize = roundDown(RiskedMoney / oneLotSLDrawdown, decimals);
   }
   else {
	  LotSize = 0;
   }

   //--- MAXLOT and MINLOT management

   Verbose("Computing Money Management - Smallest_Lot: ", Smallest_Lot, ", Largest_Lot: ", Largest_Lot,", Computed LotSize: ", LotSize);
   Verbose("Money to risk: ", RiskedMoney, ", Max Trade drawdown: ", oneLotSLDrawdown, ", Point value: ", PointValue);

   if(LotSize <= 0) {
      Verbose("Calculated LotSize is <= 0. Using LotsIfNoMM value: ", LotsIfNoMM, ")");
			LotSize = LotsIfNoMM;
	 }

   if (LotSize < Smallest_Lot) {
      Verbose("LotSize is too small. Minimal allowed lot size: ", Smallest_Lot);
      LotSize = 0;
   }
   else if (LotSize > Largest_Lot) {
      Verbose("LotSize is too big. LotSize set to maximal allowed market value: ", Largest_Lot);
      LotSize = Largest_Lot;
   }

   if(LotSize > MaximumLots) {
      Verbose("LotSize is too big. LotSize set to maximal allowed value (MaximumLots): ", MaximumLots);
      LotSize = MaximumLots;
   }

   //--------------------------------------------

   return (LotSize);
}

//=============================================================================
//                              OrderReliable.mqh
//
//         Copyright ? 2006, Derk Wehler     (derkwehler@gmail.com)
//
//  This file is simply LibOrderReliable as a header file instead of a library
//
//  In order to read this code most clearly in the Metaeditor, it is advised
//  that you set your tab settings to 4 (instead of the default 3): 
//  Tools->Options->General Tab, set Tab Size to 4, uncheck "Insert spaces"
//
// ***************************************************************************
// OrderReliable library MIT license
// 
// Copyright (c) 2006 Derk Wehler
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// LICENSE LIMITATION
// This MIT license is limited only for use of OrderReliable within 
// strategies generated by StrategyQuant. Any method from this library 
// that will be used outside of this source code will be governed 
// by GPL license.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// ***************************************************************************

// ***************************************************************************
//
//  A library for MT4 expert advisors, intended to give more reliable
//  order handling.	This library only concerns the mechanics of sending
//  orders to the Metatrader server, dealing with transient connectivity
//  problems better than the standard order sending functions.  It is
//  essentially an error-checking wrapper around the existing transaction
//  functions. This library provides nothing to help actual trade strategies,
//  but ought to be valuable for nearly all expert advisors which trade 'live'.
//
//
//=============================================================================
//
//  Contents:
//
//		OrderSendReliable()
//			This is intended to be a drop-in replacement for OrderSend()
//			which, one hopes is more resistant to various forms of errors
//			prevalent with MetaTrader.
//
//		OrderSendReliable2Step()
//			This function is intended to be used when brokers do not allow
//			initial stoploss and take-profit settings as part of the initial
//			market order. After successfully playing an order, it will then
//			Call OrderModifyReliable() to update the SL and TP settings.
//
//		OrderModifyReliable()
//			A replacement for OrderModify with more error handling.
//
//		OrderCloseReliable()
//			A replacement for OrderClose with more error handling.
//
//		OrderCloseReliableMKT()
//			This function is intended for closing orders ASAP; the
//			principal difference is that in its internal retry-loop,
//			it uses the new "Bid" and "Ask" real-time variables as opposed
//			to the OrderCloseReliable() which uses only the price given upon
//			entry to the routine.  More likely to get the order closed if 
//          price moves, but more likely to "slip"
//
//		OrderDeleteReliable()
//			A replacement for OrderDelete with more error handling.
//
//===========================================================================
//                      CHANGE LOG BEGUN 28 March, 2014
//         Prior to this, Source OffSite was used to save changes
//      Start with revision 32, which is what SOS had as last change
//
//  v32, 28 Mar 14: 
//  Small bug fixes for Build 600 changes
//
//  v33, 25 Apr 16: 
//  Tiny adjustment made to GetOrderDetails() for non-forex pairs
//
//  v34, 21 Jun 16: 
//  Changed SleepRandomTime() to just sleep 200ms
//
//  v35, 20 Jul 16: (important)
//  Added MySymbolConst2Val(), MySymbolVal2String(), necessary for correct
//  functioning of GetOrderDetails()
//
//  v36, 23 Apr 19: (Mark Fric, SQ)
//  Added separate retry_attempts_bad_price variable that can configure repeat 
//  attempts for ERR_INVALID_PRICE and ERR_INVALID_STOPS errors
//
//===========================================================================


//=============================================================================
//							 OrderSendReliable()
//
//  This is intended to be a drop-in replacement for OrderSend() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//	RETURN VALUE:
//     Ticket number or -1 under some error conditions.  
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Automatic normalization of Digits
//
//     * Automatically makes sure that stop levels are more than
//       the minimum stop distance, as given by the server. If they
//       are too close, they are adjusted.
//
//     * Automatically converts stop orders to market orders
//       when the stop orders are rejected by the server for
//       being to close to market.  NOTE: This intentionally
//       applies only to OP_BUYSTOP and OP_SELLSTOP,
//       OP_BUYLIMIT and OP_SELLLIMIT are not converted to market
//       orders and so for prices which are too close to current
//       this function is likely to loop a few times and return
//       with the "invalid stops" error message.
//       Note, the commentary in previous versions erroneously said
//       that limit orders would be converted.  Note also
//       that entering a BUYSTOP or SELLSTOP new order is distinct
//       from setting a stoploss on an outstanding order; use
//       OrderModifyReliable() for that.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Matt Kennel, 2006-05-28
//
//=============================================================================
int OrderSendReliable(string symbol, int cmd, double volume, double price,
                      int slippage, double stoploss, double takeprofit,
                      string comment="", int magic=0, datetime expiration=0,
                      color arrow_color=CLR_NONE)
{
	OrderReliable_Fname = "OrderSendReliable";
	int ticket = -1;
  
  price = NormalizeDouble(price, Digits);
	takeprofit = NormalizeDouble(takeprofit, Digits);
	stoploss = NormalizeDouble(stoploss, Digits);
  
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss,
			                   takeprofit, comment, magic, expiration, arrow_color);
			return(ticket);
		}
	}
	// ========================================================================
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	double realPoint = MarketInfo(symbol, MODE_POINT);
	double adjPoint = realPoint;
	if (adjPoint == 0.00001  ||  adjPoint == 0.001)
		adjPoint *= 10;
	int digits;
	double point, M;
	double bid, ask;
	double sl, tp;
	double priceNow;
	double hasSlippedBy;
	
	GetOrderDetails(0, symbol, cmd, digits, point, sl, tp, bid, ask, false);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	
	OrderReliablePrint("Attempted " + OrderTypeToString(cmd) + " " + symbol + ": " + DoubleToStr(volume, 3) + " lots @" + 
	                   DoubleToStr(price, digits+1) + " sl:" + DoubleToStr(stoploss, digits+1) + " tp:" + DoubleToStr(takeprofit, digits+1));


	// Normalize all price / stoploss / takeprofit to the proper # of digits.
	price = NormalizeDouble(price, digits);
	stoploss = NormalizeDouble(stoploss, digits);
	takeprofit = NormalizeDouble(takeprofit, digits);

	// Check stop levels, adjust if necessary
	EnsureValidStops(symbol, cmd, price, stoploss, takeprofit);

	int cnt, cnt_bad_price;
	GetLastError(); // clear the global variable.
	int err = 0;
	bool exit_loop = false;
	bool limit_to_market = false;
	bool fixed_invalid_price = false;

	// Concatenate to comment if enabled
	double symSpr = MarketInfo(symbol, MODE_ASK) - MarketInfo(symbol, MODE_BID);
	if (AddSpreadToComment)
		comment = comment + " (Spr: " + DoubleToStr(symSpr / adjPoint, 1) + ")";
		
	// Limit/Stop order...............................................................
	if (cmd > OP_SELL)
	{
		cnt = 0;
		cnt_bad_price = 0;
		while (!exit_loop)
		{
			// = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : =
			// Calculating our own slippage internally should not need to be done for pending orders; see market orders below
			// = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : =

			OrderReliablePrint("About to call OrderSend(), comment = " + comment);
			ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss,
			                   takeprofit, comment, magic, expiration, arrow_color);
			err = GetLastError();

			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					break;

				// retryable errors
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_TRADE_CONTEXT_BUSY:
				case ERR_TRADE_TIMEOUT:
				case ERR_TRADE_DISABLED:
				case ERR_PRICE_CHANGED:
				case ERR_REQUOTE:
					cnt++;
					break;

				case ERR_INVALID_PRICE:
				case ERR_INVALID_STOPS:
					cnt++;
					cnt_bad_price++;
					break;

				case ERR_INVALID_TRADE_PARAMETERS:
				default:
					// an apparently serious error.
					exit_loop = true;
					break;

			}  // end switch

			if (cnt > retry_attempts)
				exit_loop = true;

			if (cnt_bad_price > retry_attempts_bad_price)
				exit_loop = true;


			if (exit_loop)
			{
				if (!limit_to_market)
				{
					if (err != ERR_NO_ERROR  &&  err != ERR_NO_RESULT)
						OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
					else if (cnt > retry_attempts)
						OrderReliablePrint("Retry attempts maxed at " + retry_attempts +"("+retry_attempts_bad_price+")");
				}
			}
			else
			{
				OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts+"("+retry_attempts_bad_price+")" + ": Retryable error: " + OrderReliableErrTxt(err));
				
				SleepRandomTime(sleep_time, sleep_maximum);
				RefreshRates();
			}
		}

		// We have now exited from loop.
		if (err == ERR_NO_ERROR  ||  err == ERR_NO_RESULT)
		{
			OrderReliablePrint("Ticket #" + ticket + ": Successful " + OrderTypeToString(cmd) + " order placed with comment = " + comment + ", details follow.");
			if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
				OrderReliablePrint("Could Not Select Ticket #" + ticket);
			sqOrderPrint();
			return(ticket); // SUCCESS!
		}
		if (!limit_to_market)
		{
			OrderReliablePrint("Failed to execute stop or limit order after " + retry_attempts + " retries");
			OrderReliablePrint("Failed trade: " + OrderTypeToString(cmd) + " " + DoubleToStr(volume, 2) + " lots  " + symbol +
			                   "@" + price + " tp@" + takeprofit + " sl@" + stoploss);
			OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
			
			OrderReliablePrint("");
			return(-1);
		}
	}  // end

	if (limit_to_market)
	{
		OrderReliablePrint("Going from stop/limit order to market order because market is too close.");
		cmd %= 2;
		if (cmd == OP_BUY)	price = ask;
		else 				price = bid;
	}

	// We now have a market order.
	err = GetLastError(); // so we clear the global variable.
	err = 0;
	ticket = -1;
	exit_loop = false;


	// Market order..........................................................
	if (cmd == OP_BUY  ||  cmd == OP_SELL)
	{
		cnt = 0;
		cnt_bad_price = 0;
		while (!exit_loop)
		{
			// = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : =
			// Get current price and calculate slippage
			RefreshRates();
			if (cmd == OP_BUY)
			{
				M = 1.0;
				priceNow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), MarketInfo(symbol, MODE_DIGITS));	// Open @ Ask
				hasSlippedBy = (priceNow - price) / point;	// (Adjusted Point)
			}
			else if (cmd == OP_SELL)
			{
				M = -1.0;
				priceNow = NormalizeDouble(MarketInfo(symbol, MODE_BID), MarketInfo(symbol, MODE_DIGITS));	// Open @ Bid
				hasSlippedBy = (price - priceNow) / point;	// (Adjusted Point)
			}

			// Check if slippage is more than caller's maximum allowed
			if (priceNow != price  &&  hasSlippedBy > slippage)
			{
				// Actual price has slipped against us more than user allowed
				// Log error message, sleep, and try again
				OrderReliablePrint("Actual Price (Ask for buy, Bid for sell) = " + DoubleToStr(priceNow, Digits+1) + "; Slippage from Requested Price = " + DoubleToStr(hasSlippedBy, 1) + " pips.  Retrying...");
				err = ERR_PRICE_CHANGED;
			}
			else
			{
				if (priceNow != price)
				{
					// If the price has slipped "acceptably" (either negative or within 
					// "Slippage" param), then we need to adjust the SL and TP accordingly
					if (stoploss != 0)		stoploss += M * hasSlippedBy;
					if (takeprofit != 0)	takeprofit += M * hasSlippedBy;
					OrderReliablePrint("Actual Price (Ask for buy, Bid for sell) = " + DoubleToStr(priceNow, Digits+1) + "; Requested Price = " + DoubleToStr(price, Digits) + "; Slippage from Requested Price = " + DoubleToStr(hasSlippedBy, 1) + " pips (\'positive slippage\').  Attempting order at market");
				}
				OrderReliablePrint("About to call OrderSend(), comment = " + comment);
				ticket = OrderSend(symbol, cmd, volume, priceNow, (slippage - hasSlippedBy), 
								   stoploss, takeprofit, comment, magic,	expiration, arrow_color);
				err = GetLastError();
			}
			// = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : =

			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					break;

				case ERR_INVALID_PRICE:
					if (cmd == OP_BUY)
						OrderReliablePrint("INVALID PRICE ERROR - Requested Price: " + DoubleToStr(price, Digits) + "; Ask = " + DoubleToStr(MarketInfo(symbol, MODE_ASK), Digits));
					else
						OrderReliablePrint("INVALID PRICE ERROR - Requested Price: " + DoubleToStr(price, Digits) + "; Bid = " + DoubleToStr(MarketInfo(symbol, MODE_BID), Digits));
					cnt++; // a retryable error
					cnt_bad_price++;
					break;
					
				case ERR_INVALID_STOPS:
					OrderReliablePrint("INVALID STOPS on attempted " + OrderTypeToString(cmd) + " : " + DoubleToStr(volume, 2) + " lots " + " @ " + DoubleToStr(price, Digits) + ", SL = " + DoubleToStr(stoploss, Digits) + ", TP = " + DoubleToStr(takeprofit, Digits));
					cnt++; // a retryable error
					cnt_bad_price++;
					break;
					
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_TRADE_CONTEXT_BUSY:
				case ERR_TRADE_TIMEOUT:
				case ERR_TRADE_DISABLED:
				case ERR_PRICE_CHANGED:
				case ERR_REQUOTE:
					cnt++; // a retryable error
					break;

				default:
					// an apparently serious, unretryable error.
					exit_loop = true;
					break;
			}  

			if (cnt > retry_attempts)
				exit_loop = true;

			if (cnt_bad_price > retry_attempts_bad_price)
				exit_loop = true;

			if (exit_loop)
			{
				if (err != ERR_NO_ERROR  &&  err != ERR_NO_RESULT)
					OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
				if (cnt > retry_attempts)
					OrderReliablePrint("Retry attempts maxed at " + retry_attempts +"("+retry_attempts_bad_price+")");
			}
			else
			{
				OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts +"("+retry_attempts_bad_price+")" + ": Retryable error: " + OrderReliableErrTxt(err));
				
				SleepRandomTime(sleep_time, sleep_maximum);
				RefreshRates();
			}
		}

		// We have now exited from loop; if successful, return ticket #
		if (err == ERR_NO_ERROR  ||  err == ERR_NO_RESULT)
		{
			OrderReliablePrint("Ticket #" + ticket + ": Successful " + OrderTypeToString(cmd) + " order placed with comment = " + comment + ", details follow.");
			if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
				OrderReliablePrint("Could Not Select Ticket #" + ticket);
			sqOrderPrint();
			return(ticket); // SUCCESS!
		}
		
		// If not successful, log and return -1
		OrderReliablePrint("Failed to execute OP_BUY/OP_SELL, after " + retry_attempts + " retries");
		OrderReliablePrint("Failed trade: " + OrderTypeToString(cmd) + " " + DoubleToStr(volume, 2) + " lots  " + symbol +
		                   "@" + price + " tp@" + takeprofit + " sl@" + stoploss);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	return(-1);
}


//=============================================================================
//							 OrderSendReliable2Step()
//
//  Some brokers don't allow the SL and TP settings as part of the initial
//  market order (Water House Capital).  Therefore, this routine will first
//  place the market order with no stop-loss and take-profit but later
//  update the order accordingly
//
//	RETURN VALUE:
//     Same as OrderSendReliable; the ticket number
//
//  NOTES:
//     Order will not be updated if an error continues during
//     OrderSendReliableMKT.  No additional information will be logged
//     since OrderSendReliableMKT would have already logged the error
//     condition
//
//  ORIGINAL AUTHOR AND DATE:
//     Jack Tomlinson, 2007-05-29
//
//=============================================================================
int OrderSendReliable2Step(string symbol, int cmd, double volume, double price,
                           int slippage, double stoploss, double takeprofit,
                           string comment="", int magic=0, datetime expiration=0,
                           color arrow_color=CLR_NONE)
{
	OrderReliable_Fname = "OrderSendReliable2Step";
	int ticket = -1;
	double slipped = 0;
  
  price = NormalizeDouble(price, Digits);
	takeprofit = NormalizeDouble(takeprofit, Digits);
	stoploss = NormalizeDouble(stoploss, Digits);
    
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			ticket = OrderSend(symbol, cmd, volume, price, slippage, 0, 0, 
							   comment, magic, 0, arrow_color);

			if (!OrderModify(ticket, price, stoploss, takeprofit, expiration, arrow_color))
				OrderReliablePrint("Order Modify of Ticket #" + ticket + " FAILED");
			
			return(ticket);
		}
	}
	// ========================================================================
	
	
	OrderReliablePrint("Doing OrderSendReliable, followed by OrderModifyReliable:");

	ticket = OrderSendReliable(symbol, cmd, volume, price, slippage,
								0, 0, comment, magic, expiration, arrow_color);

	if (stoploss != 0 || takeprofit != 0)
	{
		if (ticket >= 0)
		{
			double theOpenPrice = price;
			if (OrderSelect(ticket, SELECT_BY_TICKET))
			{
				slipped = OrderOpenPrice() - price;
				theOpenPrice = OrderOpenPrice();
			}
			else
				OrderReliablePrint("Failed to select ticket #" + ticket + " after successful 2step placement; cannot recalculate SL & TP");
			if (slipped > 0)
			{
				OrderReliablePrint("2step order slipped by: " + DoubleToStr(slipped, Digits) + "; SL & TP modified by same amount");
				if (takeprofit != 0)	takeprofit += slipped;
				if (stoploss != 0)		stoploss += slipped;
			}
			OrderModifyReliable(ticket, theOpenPrice, stoploss, takeprofit, expiration, arrow_color);
		}
	}
	else
		OrderReliablePrint("Skipping OrderModifyReliable because no SL or TP specified.");

	return(ticket);
}


//=============================================================================
//							 OrderModifyReliable()
//
//  This is intended to be a drop-in replacement for OrderModify() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//
//  ORIGINAL AUTHOR AND DATE:
//     Matt Kennel, 2006-05-28
//
//=============================================================================
bool OrderModifyReliable(int ticket, double price, double stoploss,
                         double takeprofit, datetime expiration,
                         color arrow_color=CLR_NONE)
{
	OrderReliable_Fname = "OrderModifyReliable";
	bool result = false;
	bool non_retryable_error = false;

	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderModify(ticket, price, stoploss,
			                     takeprofit, expiration, arrow_color);
			return(result);
		}
	}
	// ========================================================================
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		
	
	OrderReliablePrint("Attempted modify of #" + ticket + " price:" + DoubleToStr(price, digits+1) +
	                   " sl:" + DoubleToStr(stoploss, digits+1) + " tp:" + DoubleToStr(takeprofit, digits+1) + 
	                   " exp:" + TimeToStr(expiration));

	// Below, we call "EnsureValidStops".  If the order we are modifying 
	// is a pending order, then we should use the price passed in.  But 
	// if it's an open order, the price passed in is irrelevant; we need 
	// to use the appropriate bid or ask, so get those...
	double prc = price;
	if (type == OP_BUY)			prc = bid;
	else if (type == OP_SELL)	prc = ask;

	// With the requisite info, we can do error checking on SL & TP
	prc = NormalizeDouble(prc, digits);
	price = NormalizeDouble(price, digits);
	stoploss = NormalizeDouble(stoploss, digits);
	takeprofit = NormalizeDouble(takeprofit, digits);
	
	// If SL/TP are not changing then send in zeroes to EnsureValidStops(),
	// so that it does not bother to try to change them
	double newSL = stoploss;
	double newTP = takeprofit;
	if (stoploss == sl)		newSL = 0;
	if (takeprofit == tp)	newTP = 0;
	EnsureValidStops(symbol, type, prc, newSL, newTP, false);
	if (stoploss != sl)		stoploss = newSL;
	if (takeprofit != tp)	takeprofit = newTP;


	int cnt = 0;
	int cnt_bad_price = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;

	while (!exit_loop)
	{
		result = OrderModify(ticket, price, stoploss,
		                     takeprofit, expiration, arrow_color);
		err = GetLastError();

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderClose() returned false; exiting");
					break;

				case ERR_NO_RESULT:
					// Modification to same value as before
					// See below for reported result
					exit_loop = true;
					break;

				// Shouldn't be any reason stops are invalid (and yet I've seen it); try again
				case ERR_INVALID_PRICE:
				case ERR_INVALID_STOPS:	
					OrderReliablePrint("OrderModifyReliable, ERR_INVALID_STOPS, Broker\'s Min Stop Level (in pips) = " + DoubleToStr(MarketInfo(symbol, MODE_STOPLEVEL) * Point / AdjPoint(symbol), 1));
//					EnsureValidStops(symbol, price, stoploss, takeprofit);
               cnt_bad_price++;
               
				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for modify this is a retryable error, I hope.
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_TOO_MANY_REQUESTS:
				case ERR_TRADE_CONTEXT_BUSY:
				case ERR_TRADE_DISABLED:
					cnt++; 	// a retryable error
					break;

				case ERR_TRADE_MODIFY_DENIED:
					// This one may be important; have to Ensure Valid Stops AND valid price (for pends)
					break;
				
				case ERR_PRICE_CHANGED:
				case ERR_REQUOTE:
					RefreshRates();
					continue; 	// we can apparently retry immediately according to MT docs.

				default:
					// an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (cnt_bad_price > retry_attempts_bad_price)
			exit_loop = true;

		if (!exit_loop)
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts+"("+retry_attempts_bad_price+")" + ": Retryable error: " + OrderReliableErrTxt(err));
			
			SleepRandomTime(sleep_time, sleep_maximum);
			RefreshRates();
		}
		else
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: "  + OrderReliableErrTxt(err));
		}
	}

	// we have now exited from loop.
	if (err == ERR_NO_RESULT)
	{
		OrderReliablePrint("Server reported modify order did not actually change parameters.");
		OrderReliablePrint("Redundant modification: " + ticket + " " + symbol +
		                   "@" + price + " tp@" + takeprofit + " sl@" + stoploss);
		OrderReliablePrint("Suggest modifying code logic to avoid.");
	}
	
	if (result)
	{
		OrderReliablePrint("Ticket #" + ticket + ": Modification successful, updated trade details follow.");
		if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
			OrderReliablePrint("Could Not Select Ticket #" + ticket);
		sqOrderPrint();
	}
	else
	{
		OrderReliablePrint("Failed to execute modify after " + retry_attempts + " retries");
		OrderReliablePrint("Failed modification: "  + ticket + " " + symbol +
	                   	"@" + price + " tp@" + takeprofit + " sl@" + stoploss);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	
	return(result);
}


//=============================================================================
//                            OrderCloseReliable()
//
//  This is intended to be a drop-in replacement for OrderClose() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Derk Wehler, 2006-07-19
//
//=============================================================================
bool OrderCloseReliable(int ticket, double volume, double price,
						int slippage, color arrow_color=CLR_NONE)
{
	OrderReliable_Fname = "OrderCloseReliable";
	bool result = false;
	bool non_retryable_error = false;
	
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderClose(ticket, volume, price, slippage, arrow_color);
			return(result);
		}
	}
	// ========================================================================
	

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	
	
	OrderReliablePrint("Attempted close of #" + ticket + " initial price:" + DoubleToStr(price, digits+1) +
	                   " lots:" + DoubleToStr(volume, 3) + " slippage:" + slippage);


	if (type != OP_BUY && type != OP_SELL)
	{
		OrderReliablePrint("Error: Trying to close ticket #" + ticket + ", which is " + OrderTypeToString(type) + ", not OP_BUY or OP_SELL");
		return(false);
	}


	int cnt = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;
	double priceNow;
	double hasSlippedBy;

	while (!exit_loop)
	{
		// = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : =
		// Get current price and calculate slippage
		RefreshRates();
		if (type == OP_BUY)
		{
			priceNow = NormalizeDouble(MarketInfo(symbol, MODE_BID), MarketInfo(symbol, MODE_DIGITS));	// Close @ Bid
			hasSlippedBy = (price - priceNow) / point;	// (Adjusted Point)
		}
		else if (type == OP_SELL)
		{
			priceNow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), MarketInfo(symbol, MODE_DIGITS));	// Close @ Ask
			hasSlippedBy = (priceNow - price) / point;	// (Adjusted Point)
		}

		// Check if slippage is more than caller's maximum allowed
		if (priceNow != price  &&  hasSlippedBy > slippage)
		{
			// Actual price has slipped against us more than user allowed
			// Log error message, sleep, and try again
			OrderReliablePrint("Actual Price (Bid for buy, Ask for sell) Value = " + DoubleToStr(priceNow, Digits) + "; Slippage from Requested Price = " + DoubleToStr(hasSlippedBy, 1) + " pips.  Retrying...");
			result = false;
			err = ERR_PRICE_CHANGED;
		}
		else
		{
			result = OrderClose(ticket, volume, priceNow, (slippage - hasSlippedBy), arrow_color);
			err = GetLastError();
		}
		// = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : =

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderClose() returned false; exiting");
					OrderReliablePrint("If order did not actually close, error code is apparently wrong");
					break;

				case ERR_NO_RESULT:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_RESULT received, but OrderClose() returned false; exiting");
					OrderReliablePrint("If order did not actually close, error code is apparently wrong");
					break;

				case ERR_INVALID_PRICE:
					OrderReliablePrint("ERR_INVALID_PRICE received, but should not occur since we are refreshing rates");
					cnt++; 	// a retryable error
					break;

				case ERR_PRICE_CHANGED:
				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for close this is a retryable error, I hope.
				case ERR_TRADE_DISABLED:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_REQUOTE:
				case ERR_TOO_MANY_REQUESTS:	
				case ERR_TRADE_CONTEXT_BUSY:
					cnt++; 	// a retryable error
					break;

				default:
					// Any other error is an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (exit_loop)
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
		}
		else
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
			
			SleepRandomTime(sleep_time, sleep_maximum);
		}
	}

	// We have now exited from loop
	if (result  ||  err == ERR_NO_RESULT  ||  err == ERR_NO_ERROR)
	{
		if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
			OrderReliablePrint("Successful close of Ticket #" + ticket + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
		else if (OrderCloseTime() > 0)	// Then it closed ok
			OrderReliablePrint("Successful close of Ticket #" + ticket + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
		else
		{
			OrderReliablePrint("Close result reported success (or failure, but w/ERR_NO_ERROR); yet order remains!  Must re-try close from EA logic!");
			OrderReliablePrint("Close Failed: Ticket #" + ticket + ", Price: " +
		                   		price + ", Slippage: " + slippage);
			OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
			result = false;
		}
	}
	else
	{
		OrderReliablePrint("Failed to execute close after " + (cnt-1) + " retries");
		OrderReliablePrint("Failed close: Ticket #" + ticket + " @ Price: " + priceNow + 
	                   	   " (Requested Price: " + price + "), Slippage: " + slippage);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	
	
	return(result);
}


//=============================================================================
//                           OrderCloseReliableMKT()
//
//	This function is intended for closing orders ASAP; the principal 
//  difference is that in its internal retry-loop, it uses the new "Bid" 
//  and "Ask" real-time variables as opposed to the OrderCloseReliable(), 
//  which uses only the price given upon entry to the routine.  More likely 
//  to get the order closed if price moves, but more likely to "slip"
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Derk Wehler, 2009-04-03
//
//=============================================================================
bool OrderCloseReliableMKT(int ticket, double volume, double price,
						   int slippage, color arrow_color=CLR_NONE)
{
	OrderReliable_Fname = "OrderCloseReliableMKT";
	bool result = false;
	bool non_retryable_error = false;
	
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderClose(ticket, volume, price, slippage, arrow_color);
			return(result);
		}
	}
	// ========================================================================
	

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	
	
	OrderReliablePrint("Attempted close of #" + ticket + " initial price:" + DoubleToStr(price, digits+1) +
	                   " lots:" + DoubleToStr(price, 3) + " slippage:" + slippage);


	if (type != OP_BUY && type != OP_SELL)
	{
		OrderReliablePrint("Error: Trying to close ticket #" + ticket + ", which is " + OrderTypeToString(type) + ", not OP_BUY or OP_SELL");
		return(false);
	}


	int cnt = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;
	double pnow;
	int slippagenow;

	while (!exit_loop)
	{
		if (type == OP_BUY)
		{
			pnow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
			if (pnow > price)
			{
				// Do not allow slippage to go negative; will cause error
				slippagenow = MathMax(0, slippage - (pnow - price) / point);
			}
		}
		else if (type == OP_SELL)
		{
			pnow = NormalizeDouble(MarketInfo(symbol, MODE_BID), MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
			if (pnow < price)
			{
				// Do not allow slippage to go negative; will cause error
				slippagenow = MathMax(0, slippage - (price - pnow) / point);
			}
		}

		result = OrderClose(ticket, volume, pnow, slippagenow, arrow_color);
		err = GetLastError();

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderClose() returned false; exiting");
					break;

				case ERR_NO_RESULT:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_RESULT received, but OrderClose() returned false; exiting");
					break;

				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for close this is a retryable error, I hope.
				case ERR_TRADE_DISABLED:
				case ERR_PRICE_CHANGED:
				case ERR_INVALID_PRICE:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_REQUOTE:
				case ERR_TOO_MANY_REQUESTS:	
				case ERR_TRADE_CONTEXT_BUSY:
					cnt++; 	// a retryable error
					break;

				default:
					// Any other error is an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (!exit_loop)
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
			
			SleepRandomTime(sleep_time, sleep_maximum);
		}

		if (exit_loop)
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
		}
	}

	// we have now exited from loop.
	if (result)
	{
		if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
			OrderReliablePrint("Successful close of Ticket #" + ticket + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
		else if (OrderCloseTime() > 0)	// Then it closed ok
			OrderReliablePrint("Successful close of Ticket #" + ticket + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
		else
		{
			OrderReliablePrint("Close result reported success, but order remains!  Must re-try close from EA logic!");
			OrderReliablePrint("Close Failed: Ticket #" + ticket + ", Price: " +
		                   		price + ", Slippage: " + slippage);
			OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
			result = false;
		}
	}
	else
	{
		OrderReliablePrint("Failed to execute close after " + retry_attempts + " retries");
		OrderReliablePrint("Failed close: Ticket #" + ticket + " @ Price: " +
	                   		pnow + " (Initial Price: " + price + "), Slippage: " + 
	                   		slippagenow + " (Initial Slippage: " + slippage + ")");
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	
	
	return(result);
}


//=============================================================================
//                            OrderDeleteReliable()
//
//  This is intended to be a drop-in replacement for OrderDelete() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Derk Wehler, 2006-12-21
//
//=============================================================================
bool OrderDeleteReliable(int ticket, color clr=CLR_NONE)
{
	OrderReliable_Fname = "OrderDeleteReliable";
	bool result = false;
	bool non_retryable_error = false;

	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderDelete(ticket, clr);
			return(result);
		}
	}
	// ========================================================================
	
	
	
	OrderReliablePrint("Attempted deletion of pending order, #" + ticket);
	

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		
	if (type == OP_BUY || type == OP_SELL)
	{
		OrderReliablePrint("error: Trying to close ticket #" + ticket +
		                   ", which is " + OrderTypeToString(type) +
		                   ", not OP_BUYSTOP, OP_SELLSTOP, OP_BUYLIMIT, or OP_SELLLIMIT");
		return(false);
	}


	int cnt = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;

	while (!exit_loop)
	{
		result = OrderDelete(ticket, clr);
		err = GetLastError();

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderDelete() returned false; exiting");
					break;

				case ERR_NO_RESULT:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_RESULT received, but OrderDelete() returned false; exiting");
					break;

				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for delete this is a retryable error, I hope.
				case ERR_TRADE_DISABLED:
				case ERR_OFF_QUOTES:
				case ERR_PRICE_CHANGED:
				case ERR_BROKER_BUSY:
				case ERR_REQUOTE:
				case ERR_TOO_MANY_REQUESTS:
				case ERR_TRADE_CONTEXT_BUSY:
					cnt++; 	// a retryable error
					break;

				default:	// Any other error is an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (!exit_loop)
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
			SleepRandomTime(sleep_time, sleep_maximum);
		}
		else
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
		}
	}

	// we have now exited from loop.
	if (result)
	{
		OrderReliablePrint("Successful deletion of Ticket #" + ticket);
		
		return(true); // SUCCESS!
	}
	else
	{
		OrderReliablePrint("Failed to execute delete after " + retry_attempts + " retries");
		OrderReliablePrint("Failed deletion: Ticket #" + ticket);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	
	
	return(result);
}



//=============================================================================
//=============================================================================
//								Utility Functions
//=============================================================================
//=============================================================================

string OrderReliableErrTxt(int err)
{
	return (err + "  ::  " + ErrorDescription(err));
}


// Defaut level is 3
// Use level = 1 to Print all but "Retry" messages
// Use level = 0 to Print nothing
void OrderReliableSetErrorLevel(int level)
{
	ErrorLevel = level;
}


void OrderReliablePrint(string s)
{
	// Print to log prepended with stuff;
	if (ErrorLevel >= 99 || (!(IsTesting() || IsOptimization())))
	{
		if (ErrorLevel > 0)
			Print(OrderReliable_Fname + " " + OrderReliableVersion + ":     " + s);
	}
}


void sqOrderPrint() {
  Print("Ticket: "+OrderTicket()+" - "+
  "OpenTime: "+OrderOpenTime()+","+
  "Type: "+OrderType()+","+
  "Size: "+OrderLots()+","+
  "Symbol: "+OrderSymbol()+","+
  "OpenPrice: "+OrderOpenPrice()+","+
  "SL: "+OrderStopLoss()+","+
  "PT: "+OrderTakeProfit());

  Print("Ticket: "+OrderTicket()+" (continue) - "+
  "CloseTime: "+OrderCloseTime()+","+
  "ClosePrice: "+OrderClosePrice()+","+
  "Commission: "+OrderCommission()+","+
  "Swap: "+OrderSwap()+","+
  "Profit: "+OrderProfit()+","+
  "Comment: "+OrderComment()+","+
  "MagicNumber: "+OrderMagicNumber());
}

string OrderTypeToString(int type)
{
	if (type == OP_BUY) 		return("BUY");
	if (type == OP_SELL) 		return("SELL");
	if (type == OP_BUYSTOP) 	return("BUY STOP");
	if (type == OP_SELLSTOP)	return("SELL STOP");
	if (type == OP_BUYLIMIT) 	return("BUY LIMIT");
	if (type == OP_SELLLIMIT)	return("SELL LIMIT");
	return("None (" + type + ")");
}


//=============================================================================
//                        EnsureValidStops()
//
//  Most MQ4 brokers have a minimum stop distance, which is the number of 
//  pips from price where a pending order can be placed or where a SL & TP 
//  can be placed.  THe purpose of this function is to detect when the 
//  requested SL or TP is too close, and to move it out automatically, so 
//  that we do not get ERR_INVALID_STOPS errors.
//
//  FUNCTION COMPLETELY OVERHAULED:
//     Derk Wehler, 2008-11-08
//
//=============================================================================
void EnsureValidStops(string symbol, int cmd, double price, double& sl, double& tp, bool isNewOrder=true)
{
	string prevName = OrderReliable_Fname;
	OrderReliable_Fname = "EnsureValidStops";
	
	double point = MarketInfo(symbol, MODE_POINT);
	
	// We only use point for StopLevel, and StopLevel is reported as 10 times
	// what you expect on a 5-digit broker, so leave it as is.
	//if (point == 0.001  ||  point == 0.00001)
	//	point *= 10;
		
	double 	orig_sl = sl;
	double 	orig_tp = tp;
	double 	new_sl, new_tp;
	int 	min_stop_level = MarketInfo(symbol, MODE_STOPLEVEL);
	double 	servers_min_stop = min_stop_level * point;
	double 	spread = MarketInfo(symbol, MODE_ASK) - MarketInfo(symbol, MODE_BID);
	//Print("        EnsureValidStops: Symbol = " + symbol + ",  servers_min_stop = " + servers_min_stop); 

	// Skip if no S/L (zero)
	if (sl != 0)
	{
		if (cmd % 2 == 0)	// we are long
		{
			// for pending orders, sl/tp can bracket price by servers_min_stop
			new_sl = price - servers_min_stop;
			//Print("        EnsureValidStops: new_sl [", new_sl, "] = price [", price, "] - servers_min_stop [", servers_min_stop, "]"); 
			
			// for market order, sl/tp must bracket bid/ask
			if (cmd == OP_BUY  &&  isNewOrder)
			{
				new_sl -= spread;	
				//Print("        EnsureValidStops: Minus spread [", spread, "]"); 
			}
			sl = MathMin(sl, new_sl);
		}
		else	// we are short
		{
			new_sl = price + servers_min_stop;	// we are short
			//Print("        EnsureValidStops: new_sl [", new_sl, "] = price [", price, "] + servers_min_stop [", servers_min_stop, "]"); 
			
			// for market order, sl/tp must bracket bid/ask
			if (cmd == OP_SELL  &&  isNewOrder)
			{
				new_sl += spread;	
				//Print("        EnsureValidStops: Plus spread [", spread, "]"); 
			}

			sl = MathMax(sl, new_sl);
		}
		sl = NormalizeDouble(sl, MarketInfo(symbol, MODE_DIGITS));
	}


	// Skip if no T/P (zero)
	if (tp != 0)
	{
		// check if we have to adjust the stop
		if (MathAbs(price - tp) <= servers_min_stop)
		{
			if (cmd % 2 == 0)	// we are long
			{
				new_tp = price + servers_min_stop;	// we are long
				tp = MathMax(tp, new_tp);
			}
			else	// we are short
			{
				new_tp = price - servers_min_stop;	// we are short
				tp = MathMin(tp, new_tp);
			}
			tp = NormalizeDouble(tp, MarketInfo(symbol, MODE_DIGITS));
		}
	}
	
	// notify if changed
	if (sl != orig_sl)
		OrderReliablePrint("SL was too close to brokers min distance (" + min_stop_level + "); moved SL to: " + sl);
	if (tp != orig_tp)
		OrderReliablePrint("TP was too close to brokers min distance (" + min_stop_level + "); moved TP to: " + tp);
		
	OrderReliable_Fname = prevName;
}


//=============================================================================
//                            EnsureValidPendPrice()
//
//  This function is called if OrderSendReliable gets an ERR_INVALID_PRICE 
//  or ERR_INVALID_STOPS error when attempting to place a pending order. 
//  We assume these are signs that the brokers minumum stop distance, which 
//  is what is used for pending distances as well, is too small and the price 
//  is too close to the pending's requested price.  Therefore we want to do 
//  one of two things: 
//
//  If UseLimitToMarket is enabled, then see if the actual and requested 
//  prices are close enough to be within the requested slippage, and if so 
//  return true to indicate a swap to a market order.
//
//  Otherwise, we move the requested price far enough from current price to 
//  (hopefully) place the pending order, and return false (price, sl & tp  
//  are all I/O params).  If this does not work, and the the same error is 
//  received, and the function is called again, it attempts to move the 
//  entry price (and sl & tp) out one more pip at a time.
//
//  RETURN VALUE:
//     True if calling function should convert this to market order, 
//     otherwise False
//
//  ORIGINAL AUTHOR AND DATE:
//     Derk Wehler, 2011-05-17
//
//=============================================================================
bool EnsureValidPendPrice(int err, bool& fixed, string symbol, int cmd, double& price, 
						  double& stoploss, double& takeprofit, int slippage, double point, int digits)
{
	OrderReliable_Fname = "EnsureValidPendPrice";

	double 	servers_min_stop = MarketInfo(symbol, MODE_STOPLEVEL) * Point;
	double 	old_price, priceNow, hasSlippedBy;
	
	// Assume buy pendings relate to Ask, and sell pendings relate to Bid
	if (cmd % 2 == OP_BUY)
		priceNow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), MarketInfo(symbol, MODE_DIGITS));
	else if (cmd % 2 == OP_SELL)
		priceNow = NormalizeDouble(MarketInfo(symbol, MODE_BID), MarketInfo(symbol, MODE_DIGITS));

	// If we are too close to put in a limit/stop order so go to market.
	if (MathAbs(priceNow - price) <= servers_min_stop)
	{
		if (UseLimitToMarket)
		{
			hasSlippedBy = MathAbs(priceNow - price) / point;	// (Adjusted Point)

			// Check if slippage is more than caller's maximum allowed
			if (priceNow != price  &&  hasSlippedBy > slippage)
			{
				// Actual price is too far from requested price to do market order, 
				// and too close to place pending order (because of broker's minimum 
				// stop distance).  Therefore, report the problem and try again...
				OrderReliablePrint("Actual price (Ask for buy, Bid for sell) = " + DoubleToStr(priceNow, Digits) + ", and requested pend price = " + DoubleToStr(priceNow, Digits) + "; slippage between the two = ?" + DoubleToStr(hasSlippedBy, 1) + " pips, which is larger than user specified.  Cannot Convert to Market Order.");
			}
			else
			{
				// Price has moved close enough (within slippage) to requested 
				// pending price that we can go ahead and enter a market position
				return(true);
			}
		}
		else 
		{
			if (fixed)
			{
				if (cmd == OP_BUYSTOP  ||  cmd == OP_SELLLIMIT)
				{
					price += point;
					if (stoploss > 0)   stoploss += point;
					if (takeprofit > 0) takeprofit += point;
					OrderReliablePrint("Pending " + OrderTypeToString(cmd) + " Order still has \'" + ErrorDescription(err) + "\', adding 1 pip; new price = " + DoubleToStr(price, Digits));
					if (stoploss > 0  ||  takeprofit > 0)
						OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
				}
				else if (cmd == OP_BUYLIMIT  ||  cmd == OP_SELLSTOP)
				{
					price -= point;
					if (stoploss > 0)   stoploss -= point;
					if (takeprofit > 0) takeprofit -= point;
					OrderReliablePrint("Pending " + OrderTypeToString(cmd) + " Order still has \'" + ErrorDescription(err) + "\', subtracting 1 pip; new price = " + DoubleToStr(price, digits));
					if (stoploss > 0  ||  takeprofit > 0)
						OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
				}
			}
			else
			{
				if (cmd == OP_BUYLIMIT)
				{
					old_price = price;
					price = priceNow - servers_min_stop;
					if (stoploss > 0)   stoploss += (price - old_price);
					if (takeprofit > 0) takeprofit += (price - old_price);
					OrderReliablePrint("Pending " + OrderTypeToString(cmd) + " has \'" + ErrorDescription(err) + "\'; new price = " + DoubleToStr(price, digits));
					if (stoploss > 0  ||  takeprofit > 0)
						OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
				}
				else if (cmd == OP_BUYSTOP)
				{
					old_price = price;
					price = priceNow + servers_min_stop;
					if (stoploss > 0)   stoploss += (price - old_price);
					if (takeprofit > 0) takeprofit += (price - old_price);
					OrderReliablePrint("Pending " + OrderTypeToString(cmd) + " has \'" + ErrorDescription(err) + "\'; new price = " + DoubleToStr(price, digits));
					if (stoploss > 0  ||  takeprofit > 0)
						OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
				}
				else if (cmd == OP_SELLSTOP)
				{
					old_price = price;
					price = priceNow - servers_min_stop;
					if (stoploss > 0)   stoploss -= (old_price - price);
					if (takeprofit > 0) takeprofit -= (old_price - price);
					OrderReliablePrint("Pending SellStop has \'" + ErrorDescription(err) + "\'; new price = " + DoubleToStr(price, digits));
					if (stoploss > 0  ||  takeprofit > 0)
						OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
				}
				else if (cmd == OP_SELLLIMIT)
				{
					old_price = price;
					price = priceNow + servers_min_stop;
					if (stoploss > 0)   stoploss -= (old_price - price);
					if (takeprofit > 0) takeprofit -= (old_price - price);
					OrderReliablePrint("Pending SellLimit has \'" + ErrorDescription(err) + "\'; new price = " + DoubleToStr(price, digits));
					if (stoploss > 0  ||  takeprofit > 0)
						OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
				}
				fixed = true;
			}
			EnsureValidStops(symbol, cmd, price, stoploss, takeprofit);
		}
	}
	return(false);
}


//=============================================================================
//                              SleepRandomTime()
//
//  This sleeps a random amount of time defined by an exponential
//  probability distribution. The mean time, in Seconds is given
//  in 'mean_time'.
//
//  This is the back-off strategy used by Ethernet.  This will
//  quantize in fiftieths of seconds, so don't call this with a too
//  small a number.  This returns immediately if we are backtesting
//  and does not sleep.
//
//=============================================================================
void SleepRandomTime(double mean_time, double max_time)
{
	// No need for pauses on tester
	if (IsTesting())
		return;
/*
	// 19Jun16 : Noticed for a long time that when an order fails, 
	// it fails all 10 tries.  So try a different tack here and just 
	// sleep a set time per try.
	Sleep(200);
	return;
*/

	double tenths = MathCeil(mean_time / 0.1);
	if (tenths <= 0)
		return;

	int maxtenths = MathRound(max_time / 0.1);		// 250
	double p = 1.0 - 1.0 / tenths;					// 0.975

	// Always sleep at least some minimum
	Sleep(20);

	// Now loop through and sleep 1/10th second a max of 
	// (10 * mean_time) times.  But break out "randomly".
	for (int i = 0; i < maxtenths; i++)
	{
		if (MathRand() > p*32768)
			break;

		// MathRand() returns in 0..32767
		Sleep(20);
	}
}


//=============================================================================
//  Adjusted Point funtion
//=============================================================================
double AdjPoint(string sym="")
{
	if (sym == "")
		sym = Symbol();
	double ticksize = MarketInfo(sym, MODE_TICKSIZE);
	if (ticksize == 0.00001  ||  ticksize == 0.001)
		ticksize *= 10;
	return(ticksize);
}


//=============================================================================
//                                LimitToMarket()
//
//  Setting to toggle what OrderSendReliable does with Stop or Limit orders
//  that are requested to be placed too close to the current price.  
//
//  When set True, it will turn any such conundrum from a stop/limit order 
//  into a simple market order
//
//  When set False, the library will alter the price of the Stop/Limit order\
//  just far enough to be able to place the order as a pending order.
//
//=============================================================================
void LimitToMarket(bool limit2market)
{
	UseLimitToMarket = limit2market;
}


//=============================================================================
//                        OrderReliableUseForTesting()
//
//  Setting to toggle whether this OrderReliable library is used in testing 
//  and optimization.  By default, it is set to false, and will thus just pass 
//  orders straight through.
//
//  When set true, it will use the full functions as normally all the time,
//  including testing / optimization.
//
//=============================================================================
void OrderReliableUseForTesting(bool use)
{
	UseForTesting = use;
}


//=============================================================================
//                      OrderReliableAddSpreadToComment()
//
//  Setting to toggle whether this to add the current spread to the trade 
//  commment, so that user can monitor variable spread situations on a 
//  per trade basis.
//
//=============================================================================
void OrderReliableAddSpreadToComment(bool use)
{
	AddSpreadToComment = use;
}


//=============================================================================
//                              GetOrderDetails()
//
//  For some OrderReliable functions (such as Modify), we need to know some
//  things about the order (such as direction and symbol).  To do this, we 
//  need to select the order.  However, the caller may already have an order 
//  selected so we need to be responsible and put it back when done.
//
//  Return false if there is a problem, true otherwise.
//
//=============================================================================
bool GetOrderDetails(int ticket, string& symb, int& type, int& digits, 
					 double& point, double& sl, double& tp, double& bid, 
					 double& ask, bool exists=true)
{
	// If this is existing order, select it and get symbol and type
	if (exists)
	{
		int lastTicket = OrderTicket();
		if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
		{
			OrderReliablePrint("OrderSelect() error: " + ErrorDescription(GetLastError()));
			return(false);
		}
		symb = OrderSymbol();
		type = OrderType();
		tp = OrderTakeProfit();
		sl = OrderStopLoss();
		
		// Select back the prior ticket num in case caller was using it.
		if (lastTicket >= 0)
		{
			if (!OrderSelect(lastTicket, SELECT_BY_TICKET, MODE_TRADES))
				OrderReliablePrint("Could Not Select Ticket #" + lastTicket);
		}
	}
	
	// Get bid, ask, point & digits
	bid = NormalizeDouble(MarketInfo(symb, MODE_BID), MarketInfo(symb, MODE_DIGITS));
	ask = NormalizeDouble(MarketInfo(symb, MODE_ASK), MarketInfo(symb, MODE_DIGITS));
	point = MarketInfo(symb, MODE_POINT);
	if (point == 0.001  ||  point == 0.00001)
		point *= 10;
		
	digits = MarketInfo(symb, MODE_DIGITS);
	
	// If this is a forex pair (i.e. symbol length == 6), then digits should not be zero
	if (digits == 0  &&  StringLen(MySymbolVal2String(MySymbolConst2Val(Symbol()))) == 6)
	{
		string prevName = OrderReliable_Fname;
		OrderReliable_Fname = "GetDigits";
		OrderReliablePrint("error: MarketInfo(symbol (" + symb + "), MODE_DIGITS) == 0");
		OrderReliable_Fname = prevName;
		return(false);
	}
	else if (exists)
	{
		tp = NormalizeDouble(tp, digits);
		sl = NormalizeDouble(sl, digits);
		bid = NormalizeDouble(bid, digits);
		ask = NormalizeDouble(ask, digits);
	}
	
	return(true);
}

//=============================================================================
// 20Jul16: Not sure what BS this is; all this ised to work, but not sure how.  
// DerksUtils includes OrderReliable, but not vice versa...  So copied these 
// functions from DerksUtils and changed the names.  Also, abive, where used, 
// it WAS: StringLen(MySymbolVal2String(Symbol())) == 6), which was wrong.
//=============================================================================
int MySymbolConst2Val(string symbol) 
{
	// Handle problem of trailing chars on mini accounts.
	string mySymbol = StringSubstr(symbol,0,6); 
	
	if (mySymbol == "AUDCAD") 	return(1);
	if (mySymbol == "AUDJPY") 	return(2);
	if (mySymbol == "AUDNZD") 	return(3);
	if (mySymbol == "AUDUSD") 	return(4);
	if (mySymbol == "CADJPY") 	return(5);
	if (mySymbol == "CHFJPY") 	return(6);
	if (mySymbol == "EURAUD") 	return(7);
	if (mySymbol == "EURCAD") 	return(8);
	if (mySymbol == "EURCHF") 	return(9);
	if (mySymbol == "EURGBP") 	return(10);
	if (mySymbol == "EURJPY") 	return(11);
	if (mySymbol == "EURUSD") 	return(12);
	if (mySymbol == "GBPCHF") 	return(13);
	if (mySymbol == "GBPJPY") 	return(14);
	if (mySymbol == "GBPUSD") 	return(15);
	if (mySymbol == "NZDJPY") 	return(16);
	if (mySymbol == "NZDUSD") 	return(17);
	if (mySymbol == "USDCAD") 	return(18);
	if (mySymbol == "USDCHF") 	return(19);
	if (mySymbol == "USDJPY")	return(20);
	
	// These symbols were added 26Sep10
	if (mySymbol == "AUDCHF") 	return(22);
	if (mySymbol == "AUDDKK") 	return(23);
	if (mySymbol == "AUDNOK") 	return(24);
	if (mySymbol == "AUDSEK") 	return(25);
	if (mySymbol == "CADCHF") 	return(26);
	if (mySymbol == "CHFNOK") 	return(27);
	if (mySymbol == "EURDKK")	return(28);
	if (mySymbol == "EURNZD") 	return(29);
	if (mySymbol == "EURPLN") 	return(30);
	if (mySymbol == "EURSEK")	return(31);
	if (mySymbol == "EURSGD") 	return(32);
	if (mySymbol == "EURZAR")	return(33);
	if (mySymbol == "GBPAUD") 	return(34);
	if (mySymbol == "GBPCAD") 	return(35);
	if (mySymbol == "GBPNOK") 	return(36);
	if (mySymbol == "GBPNZD") 	return(37);
	if (mySymbol == "GBPSGD") 	return(38);
	if (mySymbol == "NOKJPY") 	return(39);
	if (mySymbol == "NZDCAD") 	return(40);
	if (mySymbol == "NZDCHF") 	return(41);
	if (mySymbol == "NZDGBP") 	return(42);
	if (mySymbol == "SEKJPY") 	return(43);
	if (mySymbol == "USDAED")	return(44);
	if (mySymbol == "USDBHD")	return(45);
	if (mySymbol == "USDDKK")	return(46);
	if (mySymbol == "USDEGP")	return(47);
	if (mySymbol == "USDHKD")	return(48);
	if (mySymbol == "USDJOD")	return(49);
	if (mySymbol == "USDKWD")	return(50);
	if (mySymbol == "USDMXN")	return(51);
	if (mySymbol == "USDNOK")	return(52);
	if (mySymbol == "USDPLN")	return(53);
	if (mySymbol == "USDQAR")	return(54);
	if (mySymbol == "USDSAR")	return(55);
	if (mySymbol == "USDSEK")	return(56);
	if (mySymbol == "USDSGD")	return(57);
	if (mySymbol == "USDTHB")	return(58);
	if (mySymbol == "USDZAR")	return(59);
	if (mySymbol == "XAGUSD")	return(60);
	if (mySymbol == "XAUUSD")	return(61);
	
	// Originally, this was "other"; kept 
	// the same for backward compatability
	return(21);
}


string MySymbolVal2String(int val) 
{
	if (val == 1) 	return("AUDCAD");
	if (val == 2) 	return("AUDJPY");
	if (val == 3) 	return("AUDNZD");
	if (val == 4) 	return("AUDUSD");
	if (val == 5) 	return("CADJPY");
	if (val == 6) 	return("CHFJPY");
	if (val == 7) 	return("EURAUD");
	if (val == 8) 	return("EURCAD");
	if (val == 9) 	return("EURCHF");
	if (val == 10) 	return("EURGBP");
	if (val == 11) 	return("EURJPY");
	if (val == 12) 	return("EURUSD");
	if (val == 13) 	return("GBPCHF");
	if (val == 14) 	return("GBPJPY");
	if (val == 15) 	return("GBPUSD");
	if (val == 16) 	return("NZDJPY");
	if (val == 17) 	return("NZDUSD");
	if (val == 18) 	return("USDCAD");
	if (val == 19) 	return("USDCHF");
	if (val == 20)	return("USDJPY");
	
	// These symbols were added 26Sep10
	if (val == 22)	return("AUDCHF");
	if (val == 23)	return("AUDDKK");
	if (val == 24)	return("AUDNOK");
	if (val == 25)	return("AUDSEK");
	if (val == 26)	return("CADCHF");
	if (val == 27)	return("CHFNOK");
	if (val == 28)	return("EURDKK");
	if (val == 29)	return("EURNZD");
	if (val == 30)	return("EURPLN");
	if (val == 31)	return("EURSEK");
	if (val == 32)	return("EURSGD");
	if (val == 33)	return("EURZAR");
	if (val == 34)	return("GBPAUD");
	if (val == 35)	return("GBPCAD");
	if (val == 36)	return("GBPNOK");
	if (val == 37)	return("GBPNZD");
	if (val == 38)	return("GBPSGD");
	if (val == 39)	return("NOKJPY");
	if (val == 40)	return("NZDCAD");
	if (val == 41)	return("NZDCHF");
	if (val == 42)	return("NZDGBP");
	if (val == 43)	return("SEKJPY");
	if (val == 44)	return("USDAED");
	if (val == 45)	return("USDBHD");
	if (val == 46)	return("USDDKK");
	if (val == 47)	return("USDEGP");
	if (val == 48)	return("USDHKD");
	if (val == 49)	return("USDJOD");
	if (val == 50)	return("USDKWD");
	if (val == 51)	return("USDMXN");
	if (val == 52)	return("USDNOK");
	if (val == 53)	return("USDPLN");
	if (val == 54)	return("USDQAR");
	if (val == 55)	return("USDSAR");
	if (val == 56)	return("USDSEK");
	if (val == 57)	return("USDSGD");
	if (val == 58)	return("USDTHB");
	if (val == 59)	return("USDZAR");
	if (val == 60)	return("XAGUSD");
	if (val == 61)	return("XAUUSD");
	
	return("Unrecognized Pair");
}





//+------------------------------------------------------------------+
//+ Custom functions
//+
//+ Here you can define your own custom functions that can be used
//+ in Algo Wizard.
//+ The functions can perform some action (for example draw on chart
//+ or manipulate with orders) or they can return value that
//+ can be used in comparison.
//+
//+ Note! All the functions below must be in valid MQL code!
//+ Contents of this file will be appended to your EA code.
//+
//+------------------------------------------------------------------+

double exampleFunction(double value) {
   return(2 * value);
}


 