Jump to content

Dellan

Community Member
  • Posts

    3
  • Joined

  • Last visited

Posts posted by Dellan

  1. Deal all,

    I'm new to forex and EA, trying to execute the EA to run a backtesting, but it show error unable to load file, need help regarding this issue.

    Due to unable upload EA file, hence I paste the code bellow (is an EA i create by using AlgoWizard "Simple").

    //+------------------------------------------------------------------+
    //| New strategy EA
    //|
    //| Generated by StrategyQuant 3.9.132 at 12/30/2022 14:11
    //|  
    //| Backtested on EURUSD / D1, 2021.12.28 - 2022.12.28 
    //|
    //| 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"
    #property strict

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

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

    //+------------------------------------------------------------------+
    // -- Variables
    //+------------------------------------------------------------------+

    extern string CustomComment = "New strategy";
    extern int MagicNumber = 11111;
    bool LongEntrySignal = false;
    bool ShortEntrySignal = false;
    bool LongExitSignal = false;
    bool ShortExitSignal = false;
    extern int MABarOpensPeriod = 10;

    //+------------------------------------------------------------------+
    // Money Management variables
    //+------------------------------------------------------------------+

    extern string smm = "----------- Money Management - Fixed size -----------";
    extern double mmLots = 0.1;

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

    extern string sdtw = "----------- Dont Trade On Weekends -----------";
    extern bool DontTradeOnWeekends = false;
    extern string FridayCloseTime = "23:00";
    extern string SundayOpenTime = "23:00";
    extern string seod = "----------- Exit At End Of Day -----------";
    extern bool ExitAtEndOfDay = false;
    extern string EODExitTime = "23:55";

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

    extern string sltr = "----------- Limit Time Range -----------";
    extern bool LimitTimeRange = false;
    extern string SignalTimeRangeFrom = "08:00";
    extern string SignalTimeRangeTo = "16:00";
    extern bool ExitAtEndOfRange = false;
    extern int OrderTypeToExit = 0;

    extern string smmmdfmp = "----------- Max distance from market price -----------";
    extern bool LimitMaxDistanceFromMarketPrice = false;   //Limit max distance
    extern double MaxDistanceFromMarketPct = 6;   //Max distance %

    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               = 100; // 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)  

    //Some brokers have problems with updating position counts. Set this timeout to non-zero value if you experience this.
    //For example EnterReverseAtMarket doesn't work well for Admiral Markets, because OrdersTotal() returns 1 even after the order has been closed.
    uint orderSelectTimeout = 0;         //in ms

    double sqMinDistance = 0.0;

    //+------------------------------------------------------------------+
    // 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 = true;

    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;
    bool strategyUsesATM = false;

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

    void OnTick() {

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

       //------------------------
       // Rule: Trading signals
       //------------------------
    if (sqIsBarOpen()) {
         // init signals only on bar open 
         LongEntrySignal = (sqGetValue("NULL",0, PRICE_OPEN, 0) > sqMA(NULL,0, MABarOpensPeriod, 0, 1, PRICE_CLOSE, 1));

         ShortEntrySignal = (sqGetValue("NULL",0, PRICE_OPEN, 0) < sqMA(NULL,0, MABarOpensPeriod, 0, 1, PRICE_CLOSE, 1));

         LongExitSignal = (sqGetValue("NULL",0, PRICE_OPEN, 0) < sqMA(NULL,0, MABarOpensPeriod, 0, 1, PRICE_CLOSE, 1));

         ShortExitSignal = (sqGetValue("NULL",0, PRICE_OPEN, 0) > sqMA(NULL,0, MABarOpensPeriod, 0, 1, PRICE_CLOSE, 1));

       } 


                    
       //------------------------
       // Rule: Long entry
       //------------------------
       if (sqIsBarOpen()
          && ((LongEntrySignal
          && (!(ShortEntrySignal)))
          && (!(LongExitSignal))))
       {
          // Action #1
             _ticket = sqOpenOrder(OP_BUY, "Current", mmLots, 0, MagicNumber, "", 0, false, 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

          }


      }

                    
       //------------------------
       // Rule: Short entry
       //------------------------
       if (sqIsBarOpen()
          && ((ShortEntrySignal
          && (!(LongEntrySignal)))
          && (!(ShortExitSignal))))
       {
          // Action #1
             _ticket = sqOpenOrder(OP_SELL, "Current", mmLots, 0, MagicNumber, "", 0, false, 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

          }


      }

                    
       //------------------------
       // Rule: Long exit
       //------------------------
       if (sqIsBarOpen()
          && (LongExitSignal
          && 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
          && 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");

       if(!checkComments()){
          return(INIT_FAILED);
       }

       gPointCoef = calculatePointCoef(Symbol());

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

       if(sqDisplayInfoPanel) {
         sqInitInfoPanel();
       }

       SQTime = new CSQTime();

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

       double minDistanceMT = NormalizeDouble(MarketInfo(Symbol(), MODE_STOPLEVEL) * MarketInfo(Symbol(), MODE_POINT), Digits);
       double minDistanceSQ = NormalizeDouble(sqMinDistance * sqGetPointCoef(Symbol()), Digits);
       
       if(minDistanceSQ < minDistanceMT){
          VerboseLog("--------------------------------------------------------");
          VerboseLog("Warning! Min distance of this symbol is greater than min distance set in SQ! The backtest results may differ");
          VerboseLog("MT min distance: ", DoubleToStr(minDistanceMT), ", SQ min distance: ", DoubleToStr(minDistanceSQ));
          VerboseLog("--------------------------------------------------------");
       }

       //Show all subchart symbols in market watch

       return(0);
    }

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

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

    bool checkComments(){
       if(StringLen(CustomComment) > 30){
          Alert("Strategy initialization error - Comment CustomComment is longer than 30 characters");
          return false;
       }
       return true;
    }

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

    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: ", IntegerToString(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)){
             string variableNameParts[];
             int parts = StringSplit(variableName, '_', variableNameParts);
             
             if(parts != 3 || variableNameParts[0] != getVariablePrefix()) continue;
             
             int ticketNo = StrToInteger(variableNameParts[1]);
             
             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: ", IntegerToString(GetLastError()));
                }
             }
          }
       }
       
       VerboseLog(IntegerToString(deletedCount), " variables cleared");
    }

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

    bool sqHandleTradingOptions() {
       bool continueWithBarUpdate = true;

       if(!objDontTradeOnWeekends.onBarUpdate()) continueWithBarUpdate = false;
       if(!objExitAtEndOfDay.onBarUpdate()) continueWithBarUpdate = false;
       if(!objExitOnFriday.onBarUpdate()) continueWithBarUpdate = false;
       if(!objLimitTimeRange.onBarUpdate()) continueWithBarUpdate = false;
       if(!objMaxDistanceFromMarket.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) {

       return(0);
    }


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

    double sqGetValueByIdentification(string idHash) {

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

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

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

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

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

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

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

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

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

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

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

       if(idHash == (string) 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 || isMarketClosed(OrderSymbol())) {
                   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;
      
               long 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 (int) 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: ", IntegerToString(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);
    }

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

    string getVariablePrefix(){
       return IsTesting() ? "SQX(Test)" : "SQX";
    }

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

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

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

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

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

    int sqStringHash(string str){
       int i, h=0, 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: ", IntegerToString(magicNumber), ", symbol: ", symbol, ", direction: ", IntegerToString(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 ----------------");
    }

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

    void sqClosePendingOrder(int magicNumber, string symbol, int direction, string comment) {
       Verbose("Closing pending order with Magic Number: ", IntegerToString(magicNumber), ", symbol: ", symbol, ", direction: ", IntegerToString(direction), ", comment: ", comment);

       if(!sqSelectOrder(magicNumber, symbol, direction, comment, false, false, true)) {
          Verbose("Order cannot be found");
       } else {
          sqDeletePendingOrder(OrderTicket());
       }

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

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

    void sqCloseAllPendingOrders(int magicNumber, string symbol, int direction, string comment) {
       Verbose("Closing pending orders with Magic Number: ", IntegerToString(magicNumber), ", symbol: ", symbol, ", direction: ", IntegerToString(direction), ", comment: ", comment);

       while(sqSelectOrder(magicNumber, symbol, direction, comment, false, false, true)) {
          sqDeletePendingOrder(OrderTicket());
       }

       Verbose("Closing pending orders finished ----------------");
    }

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

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

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

       int numberOfBars = 0;
       int limit = MathMin(expBarsPeriod + 10, Bars);

       for(int i=0; i<limit; 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(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
        }
        
        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(OrderSymbol() != correctSymbol(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;
       
       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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: ", IntegerToString(ticket),", PL: ", DoubleToString(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;

       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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: ", IntegerToString(ticket),", PL: ", DoubleToString(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;

       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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(OrderSymbol() != correctSymbol(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;

       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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(OrderSymbol() != correctSymbol(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;

       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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(OrderSymbol() != correctSymbol(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));
    }

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

    double 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(OrderSymbol() != correctSymbol(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);
    }

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

    double 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(OrderSymbol() != correctSymbol(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) {
       int count = 0;

       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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(OrderSymbol() != correctSymbol(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;
          int limit = MathMin(10000, Bars);

          for(int i=0; i<limit; 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;
          int limit = MathMin(10000, Bars);

          for(int i=0; i<limit; 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(OrderSymbol() != correctSymbol(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) {
       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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(OrderSymbol() != correctSymbol(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) {
       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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(OrderSymbol() != correctSymbol(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: ", IntegerToString(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(correctSymbol(symbol),MODE_ASK), Digits));
       }
    }

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

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

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

    bool sqDeleteOrder(int ticket){
       if(!OrderSelect(ticket, SELECT_BY_TICKET)){
          Verbose("Warning! Cannot delete order - order with ticket ", IntegerToString(ticket), " can't be selected");
          return false;
       }

       if(sqIsPendingOrder(OrderType())){
          return OrderDeleteReliable(ticket);
       }
       else {
          return OrderCloseReliableMKT(ticket, OrderLots(), sqGetDirectionFromOrderType(OrderType()) == 1 ? Bid : Ask, correctSlippage(0, OrderSymbol()));
       }
    }

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

    bool sqDeletePendingOrder(int ticket) {
       Verbose(" Deleting pending order, ticket: " + IntegerToString(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, const bool isExitLevel = false) {
       if(size <= 0) return (0);

       string correctedSymbol = correctSymbol(symbol);

       if(isMarketClosed(correctedSymbol)){
          return 0;
       }

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

       price = (orderType == OP_BUY || orderType == OP_SELL) ? marketPrice : price;               
       price = sqFixMarketPrice(price, correctedSymbol);

          if(LimitMaxDistanceFromMarketPrice && (orderType != OP_BUY && orderType != OP_SELL)){
          double pctDistance = NormalizeDouble(MathAbs(price-marketPrice) / marketPrice * 100, 2);
          if(pctDistance > NormalizeDouble(MaxDistanceFromMarketPct, 2)){
             Verbose("Order skipped - too far from market price. Open price: ", DoubleToStr(price), ", Market price: ", DoubleToStr(marketPrice), ", Max distance: ", DoubleToStr(MaxDistanceFromMarketPct), "%"); 
             return(0);  
          }
       }
       
       openingOrdersAllowed = openingOrdersAllowed && sqHandleTradingOptions();
       
       if(!openingOrdersAllowed) return(0);                                                        

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

       // check if live order exists
       if(!isExitLevel && sqSelectOrder(magicNumber, correctedSymbol, 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(!isExitLevel){
          while(sqSelectOrder(magicNumber, correctedSymbol, direction, comment, false, false, true)) {
             if(replaceExisting) {
             
                if(!strategyUsesATM && ModifyInsteadOfReplacing && size == OrderLots()) {
                   // 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, correctedSymbol, price, marketPrice)){
          return 0;
       }

       //check free margin
       if(AccountFreeMarginCheck(correctedSymbol, isLongOrder(orderType) ? OP_BUY : OP_SELL, size) <= 0 || GetLastError()==134) {
          Alert("Balance too low for trading!");
          return(0);
        }

       string commentToUse = "";
       if(comment != ""){
          commentToUse = comment;
       }
       else {
          commentToUse = CustomComment;
       }

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

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

         return(ticket);
    }

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

    bool isLongOrder(int orderType){
       return orderType == OP_BUY || orderType == OP_BUYLIMIT || orderType == OP_BUYSTOP;
    }

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

    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("Based on its logic, the strategy tried to place order a market order at incorrect price. Market price: ", DoubleToString(marketPrice), ", order price: ", DoubleToString(price), " (this is NOT an error)");
             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 minDistanceSQ = sqMinDistance * sqGetPointCoef(symbol);
       
       if(minDistanceSQ > minDistance){
          minDistance = minDistanceSQ;
       }
       
       double priceLevel;
       
       if(orderType == OP_BUYLIMIT || orderType == OP_SELLSTOP){
          priceLevel = marketPrice - minDistance;
          
          if(price <= priceLevel){
             return true;
          }
          else {
             Verbose("Based on its logic, the strategy tried to place ", name, " at incorrect price. Market price: ", DoubleToString(marketPrice), ", max. price allowed: ", DoubleToString(priceLevel), ", ", name, " price: ", DoubleToString(price), " (this is NOT an error)");
             return false;
          }
       }
       else if(orderType == OP_BUYSTOP || orderType == OP_SELLLIMIT){
          priceLevel = marketPrice + minDistance;
          
          if(price >= priceLevel){
             return true;
          }
          else {
             Verbose("Based on its logic, the strategy tried to place ", name, " at incorrect price. Market price: ", DoubleToString(marketPrice), ", min. price allowed: ", DoubleToString(priceLevel), ", ", name," price: ", DoubleToString(price), " (this is NOT an error)");
             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: ",IntegerToString(ticket));
          return;
       }
       
       if(sl == OrderOpenPrice()) {
          Verbose("SL is the same as order price, cannot set it, so we'll delete the order!");
          if(!sqDeleteOrder(ticket)) {
             Verbose("Warning! Cannot delete order and SL/PT was not set! Error: ", IntegerToString(GetLastError()));
          }

          return;
       }

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

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

    bool sqOrderModifySL(int ticket,double stopLoss, int type) {
       if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
          Verbose("Cannot select order with ticket: ",IntegerToString(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, OrderSymbol()), OrderTakeProfit()));
    }

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

    bool sqOrderModifyPT(int ticket,double profitTarget, int type) {
       if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
          Verbose("Cannot select order with ticket: ",IntegerToString(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, OrderSymbol())));
    }

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

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

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

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

       int orderType = OrderType();

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

       bool result;
       double closestPossibleSL, closestPossiblePT;

       double point = MarketInfo(OrderSymbol(), MODE_POINT);
       double minStopLevel = MarketInfo(OrderSymbol(),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: ", DoubleToString(stopLoss), " > ", DoubleToString(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: ", DoubleToString(stopLoss), " < ", DoubleToString(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: ", DoubleToString(profitTarget), " < ", DoubleToString(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: ", DoubleToString(profitTarget), " > ", DoubleToString(closestPossiblePT));
                return(false);
             }
          }
       }
          
       result = OrderModifyReliable(ticket, price, sqFixMarketPrice(stopLoss, OrderSymbol()), sqFixMarketPrice(profitTarget, OrderSymbol()), 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 = (int) 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 = (int) 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);
    }

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

    int sleepPeriod = 500; // 0.5 s
    int 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", "New strategy", 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: "+DoubleToStr(sqGetTotalProfits(0, 100))+"/"+DoubleToStr(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;

       if(orderSelectTimeout > 0){
           Sleep(orderSelectTimeout);
       }
       
       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);
    }


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

    double 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) {
       string correctedSymbol = correctSymbol(symbol);
       double pointCoef = sqGetPointCoef(symbol);

       if(valueType == 1) {
          // convert from pips to real points
          value = sqConvertToRealPips(correctedSymbol, value);
       }
       
       if(price == 0) {
          // price can be zero for market order
          if(orderType == OP_BUY) {
             price = sqGetAsk(correctedSymbol);
          } else {
             price = sqGetBid(correctedSymbol);
          }
       }
       
       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 * pointCoef);
                slptValue = MathMin(slptValue, minSLPTValue);
                
             } else {
             
                minSLPTValue = price - (SLorPT * MinimumSL * pointCoef);
                slptValue = MathMax(slptValue, minSLPTValue);
             }
       
          }
          
          if(MaximumSL <= 0) {
             maxSLPTValue = slptValue;
          } else {
             if(orderType == OP_BUY || orderType == OP_BUYSTOP || orderType == OP_BUYLIMIT) {
                maxSLPTValue = price + (SLorPT * MaximumSL * pointCoef);
                slptValue = MathMax(slptValue, maxSLPTValue);

             } else {
                maxSLPTValue = price - (SLorPT * MaximumSL * pointCoef);
                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 * pointCoef);
                slptValue = MathMax(slptValue, minSLPTValue);
                
             } else {
                minSLPTValue = price - (SLorPT * MinimumPT * pointCoef);
                slptValue = MathMin(slptValue, minSLPTValue);
             }

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

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

             } else {
             
                maxSLPTValue = price - (SLorPT * MaximumPT * pointCoef);
                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){
       string correctedSymbol = correctSymbol(symbol);
       
       if(UseSQTickSize) {
          double ticksize = sqGetMarketTickSize(correctedSymbol);
          if(ticksize >= 0){
             return ticksize;
          }
       }
          
       int realDigits = (int) MarketInfo(correctedSymbol, 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 = 0;
       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 = 0;
       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]-100*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]+100*Point); //draw an down arrow
       ObjectSet(name, OBJPROP_STYLE, STYLE_SOLID);
       ObjectSet(name, OBJPROP_ARROWCODE, SYMBOL_ARROWDOWN);
       ObjectSet(name, OBJPROP_COLOR, Red);
    }

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

    void sqDrawVerticalLine(int shift) {
       string name = StringConcatenate("VerticalLine", MathRand());

       ObjectCreate(name, OBJ_VLINE, 0, Time[shift], 0);                       //draw a vertical line
       ObjectSet(name,OBJPROP_COLOR, Red);
       ObjectSet(name,OBJPROP_WIDTH, 1);
       ObjectSet(name,OBJPROP_STYLE, STYLE_DOT);
    }

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

    double sqDaily(string symbol, int tf, string mode, int shift) {
       return sqGetOHLC(symbol, PERIOD_D1, mode, shift);
    }                

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

    double sqWeekly(string symbol, int tf, string mode, int shift) {
       return sqGetOHLC(symbol, PERIOD_W1, mode, shift);
    }

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

    double sqMonthly(string symbol, int tf, string mode, int shift) {
       return sqGetOHLC(symbol, PERIOD_MN1, mode, shift);
    }
      
    //+------------------------------------------------------------------+

    double sqGetOHLC(string symbol, int tf, string mode, int shift){
       if(symbol == "NULL" || symbol == "Current") {
          if(mode == "Open") {
             return(iOpen(NULL, tf, shift));
          }
          if(mode == "Close") {
             return(iClose(NULL, tf, shift));
          }
          if(mode == "High") {
             return(iHigh(NULL, tf, shift));
          }
          if(mode == "Low") {
             return(iLow(NULL, tf, shift));
          }
       } 
       else {
          if(mode == "Open") {
             return(iOpen(symbol, tf, shift));
          }
          if(mode == "Close") {
             return(iClose(symbol, tf, shift));
          }
          if(mode == "High") {
             return(iHigh(symbol, tf, shift));
          }
          if(mode == "Low") {
             return(iLow(symbol, tf, 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[1]);
          return (hour * 100) + minute;
       }
       else {
          Print("Incorrect time value format. Value: '" + time + "'");
          return 0;
       }
    }

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

    int sqGetDate(int day, int month, int year) {
       return (year - 1900) * 10000 + month * 100 + day;
    }

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

    int sqGetBarDate(datetime dt) {
       return (TimeYear(dt) - 1900) * 10000 + TimeMonth(dt) * 100 + TimeDay(dt);
    }

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

    double sqGetTime(int hour, int minute, int second) {
       return 100 * hour + minute + 0.01 * second;
    }

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

    int getSQTime(datetime time){
       int minutesToday = (int) ((ulong) 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 = 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 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 && LW > BL90_M2 && CL >= CL_MPV && O != C && LW_D3 <= UW && LW_D4 <= UW)  {
                  return(true);
          }
          if(LW_D3 > UW && LW > BL90_M2 && CL >= CL_MPV && O != C && LW_D4 <= UW)  {
              return(true);
          }
          if(LW_D4 > UW && LW > 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 && C < O1 && 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));
       }
    }   


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

    double sqTrueRange(string symbol, int timeframe, int shift) {

       double close1 = sqClose(symbol, timeframe, shift+1);
       double high = sqHigh(symbol, timeframe, shift+1);
       double low = sqLow(symbol, timeframe, shift+1);

       double TrueHigh, TrueLow;

        if(close1 > high) {
            TrueHigh = close1;
        } else {
            TrueHigh = high;
        }

        if(close1 < low) {
            TrueLow = close1;
        } else {
            TrueLow = low;
        }

        double TrueRange = TrueHigh - TrueLow;
        
        return (TrueRange);
    }   

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

    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(OrderSymbol() != correctSymbol(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 getDateInMs(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 setDayOfMonth(datetime time, int day) {
            MqlDateTime strTime;
            TimeToStruct(time, strTime);
            strTime.day = day;
            return (StructToTime(strTime));
       }
       
       //+------------------------------------------------------------------+

       int getMonth(datetime time) {
          return TimeMonth(time);
       }
       
       //+------------------------------------------------------------------+

       int getDaysInMonth(datetime time) {
          MqlDateTime strTime;
          TimeToStruct(time, strTime);
          if(strTime.mon==2) {
            return  28+isLeapYear(strTime.year);
          }
          
          return 31-((strTime.mon-1)%7)%2;
       }
       
       //+------------------------------------------------------------------+

        bool isLeapYear(const int _year){
           if(_year%4 == 0){
              if(_year%400 == 0)return true;
              if(_year%100 > 0)return true;
           }
           return false;
        }
       
       //+------------------------------------------------------------------+

       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);  
       
       if(tickSize == 0){
          return price;
       }
           
       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);    
    }


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

    int sqGetMonthLastTradingDay(string symbol, int timeframe, bool includeWeekends) {
        datetime barTime = iTime(symbol, timeframe, 0);
        datetime lastTradingDate = SQTime.setDayOfMonth(barTime, SQTime.getDaysInMonth(barTime));

        if(!includeWeekends) {
            if(TimeDayOfWeek(lastTradingDate) == 6) {
                lastTradingDate = SQTime.addDays(lastTradingDate, -1);
            
            } else if(TimeDayOfWeek(lastTradingDate) == 0) {
                lastTradingDate = SQTime.addDays(lastTradingDate, -2);
            }
        }

        return TimeDay(lastTradingDate);
    }

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

    datetime monthFirstTradingDay = 0;

    bool sqIsMonthFirstTradingDay(string symbol, int timeframe, bool includeWeekends) {
        datetime date = SQTime.getDateInMs(iTime(symbol, timeframe, 0));
        
        if(monthFirstTradingDay == 0) {
            monthFirstTradingDay = date;
        }

        if(SQTime.getMonth(monthFirstTradingDay)!=SQTime.getMonth(date)) {
            if(!includeWeekends) {
                if(TimeDayOfWeek(date) != 6 && TimeDayOfWeek(date) != 0) {
                    monthFirstTradingDay = date;
                }
            } else {
                monthFirstTradingDay = date;
            }
        }

        return monthFirstTradingDay==date;
    }

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

    bool isMarketClosed(string symbol){
       return !IsTesting() && !IsOptimization() && MarketInfo(symbol, MODE_TRADEALLOWED) == 0;
    }

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

    double fixLotSize(string symbol, double size){
       string correctedSymbol = correctSymbol(symbol);
       double Smallest_Lot = MarketInfo(correctedSymbol, MODE_MINLOT);
       double Largest_Lot = MarketInfo(correctedSymbol, MODE_MAXLOT);    
       double LotStep = MarketInfo(correctedSymbol, MODE_LOTSTEP);

       double mod = MathMod(size, LotStep);
       double finalSize = size;
       
       if(MathAbs(mod - LotStep) > 0.000001){
          finalSize -= mod;
       } 

       if(finalSize < Smallest_Lot){
          Verbose("Calculated lot size (", DoubleToStr(finalSize), ") is lower than minimum possible (", DoubleToStr(Smallest_Lot), "). Using minimum lot size...");
          return Smallest_Lot;
       }
       else if(finalSize > Largest_Lot){
          Verbose("Calculated lot size (", DoubleToStr(finalSize), ") is larger than maximum possible (", DoubleToStr(Largest_Lot), "). Using maximum lot size...");
          return Largest_Lot;
       }
       else {
          return finalSize;
       }
    }

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

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

    int sqFixRanges(int value, int min, int max, int defaultVal) {
       if(value < min || value > max) {
          return (defaultVal);
       }
       
       return value;
    }

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

    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) {
        
       ma_method = sqFixRanges(ma_method, 0, 3, 0);
       applied_price = sqFixRanges(applied_price, 0, 6, 0);

       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) {
       fiboRange = sqFixRanges(fiboRange, 1, 7, 1);

       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) {
       price_field = sqFixRanges(price_field, 0, 1, 0);
       method = sqFixRanges(method, 0, 3, 0);
       
       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));
    }

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

    double sqFractal(string symbol, int timeframe, int fractal, int bufferIndex, int shift) {
       return roundValue(iCustom(symbol, timeframe, "SqFractal", fractal, bufferIndex, 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;
        }
        
        signalStrength = sqFixRanges(signalStrength, 0, 2, 1);
            
        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;
        
        signalStrength = sqFixRanges(signalStrength, 0, 2, 1);
        
        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;
        
        signalStrength = sqFixRanges(signalStrength, 0, 2, 1);
        
        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;
        
        signalStrength = sqFixRanges(signalStrength, 0, 2, 1);
        
        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((string) val);

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

          int valueType = (int) sqGetGlobalVariable(ticket, "MoveSL2BEType");
          int orderType = OrderType();
          int digits = (int) 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((string) 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: ", IntegerToString(OrderTicket()), " to :", DoubleToString(newSL));
                if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit())) {
                   error = GetLastError();
                   Verbose("Failed, error: ", IntegerToString(error), " - ", ErrorDescription(error),", Ask: ", DoubleToString(Ask), ", Bid: ", DoubleToString(Bid), " Current SL: ",  DoubleToString(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: ", IntegerToString(OrderTicket()), "  to :", DoubleToString(newSL));
                if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit())) {
                   error = GetLastError();
                   Verbose("Failed, error: ", IntegerToString(error), " - ", ErrorDescription(error),", Ask: ", DoubleToString(Ask), ", Bid: ", DoubleToString(Bid), " Current SL: ", DoubleToString(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((string) val);
       
       if(tsValue > 0) {
          double plValue;
          int error;

          int valueType = (int) sqGetGlobalVariable(ticket, "TrailingStopType");
          int orderType = OrderType();
          int digits = (int) 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((string) 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: ", IntegerToString(OrderTicket()), " to :", DoubleToString(tsValue));
                if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), tsValue, OrderTakeProfit())) {
                   error = GetLastError();
                   Verbose("Failed, error: ", IntegerToString(error), " - ", ErrorDescription(error),", Ask: ", DoubleToString(Ask), ", Bid: ", DoubleToString(Bid), " Current SL: ",  DoubleToString(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: ", IntegerToString(OrderTicket()), " to :", DoubleToString(tsValue));
                if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), tsValue, OrderTakeProfit())) {
                   error = GetLastError();
                   Verbose("Failed, error: ", IntegerToString(error), " - ", ErrorDescription(error),", Ask: ", DoubleToString(Ask), ", Bid: ", DoubleToString(Bid), " Current SL: ",  DoubleToString(currentSL));
                }
             }
          }
       }
    }
    void sqManageExitAfterXBars(int ticket) {
       int exitBars = (int) sqGetGlobalVariable(ticket, "ExitAfterBars");
       if(exitBars > 0) {
          if (sqGetOpenBarsForOrder(ticket, exitBars+10) >= exitBars) {
             Verbose("Exit After ", IntegerToString(exitBars), "bars - closing order with ticket: ", IntegerToString(OrderTicket()));
             sqClosePositionAtMarket(OrderLots());
          }
       }
    }
    //+------------------------------------------------------------------+
    //+------------------------------------------------------------------+
    // Trading Options includes
    //+------------------------------------------------------------------+
    //+------------------------------------------------------------------+

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

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

    class CDontTradeOnWeekends: public CTradingOption {
       private:
          datetime thisFridayCloseTime; 
          datetime thisSundayOpenTime;
           
       public:
          CDontTradeOnWeekends() {
             thisFridayCloseTime = D'1970.01.01';    
             thisSundayOpenTime = D'1970.01.01';
          }

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

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

             datetime currentTime = TimeCurrent();

             if(thisFridayCloseTime < 100) {
                initTimes(currentTime, 0);
             }
             
             if(currentTime < thisFridayCloseTime) {
                // trade normally
                return true;
             }
             
             if(currentTime < thisSundayOpenTime) {
                   // 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
                initTimes(currentTime, DayOfWeek() == 0 ? 1 : 0); 

                return true;
             }
          }           

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

          void initTimes(datetime currentTime, int addDays) {
             if(addDays > 0) {
                    thisFridayCloseTime = SQTime.addDays(currentTime, addDays);
              } else {
                    thisFridayCloseTime = currentTime;
              }
        
              // set time of EOD 
              thisFridayCloseTime = SQTime.setDayOfWeek(thisFridayCloseTime, (FridayCloseTime == "00:00" || FridayCloseTime == "0:00") ? SATURDAY : FRIDAY);
              thisFridayCloseTime = SQTime.setHHMM(thisFridayCloseTime, FridayCloseTime);    
             
              thisSundayOpenTime = SQTime.setDayOfWeek(currentTime, SUNDAY);
             thisSundayOpenTime = SQTime.setHHMM(thisSundayOpenTime, SundayOpenTime);    
          }
    };

    // create variable for class instance (required)
    CDontTradeOnWeekends* objDontTradeOnWeekends;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(!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(!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 dailySignalTimeRangeFrom;
          datetime dailySignalTimeRangeTo;
          bool closedThisDay;
          
       public:
          CLimitTimeRange() {
             closedThisDay = false;
          }

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

          virtual bool onBarUpdate() {     
            if(!LimitTimeRange) {
                return true;
            }
                                    
            onTick();
               
            if(!sqIsBarOpen()) {
               return true;
            }
            
            datetime currentTime = TimeCurrent();
        
            if(currentTime > dailySignalTimeRangeTo) {
                // 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(!checkMagicNumber(OrderMagicNumber())) continue;
                     
                     bool isLiveOrder = OrderType() == OP_BUY || OrderType() == OP_SELL;
                   
                     if(isLiveOrder){
                        if(OrderTypeToExit != 2) { // not pending only
                           sqClosePositionAtMarket(OrderLots());
                        }
                     }
                     else {
                        if(OrderTypeToExit != 1) { // not live only
                           sqDeletePendingOrder(OrderTicket());
                        }
                     }
                  }
               }
               
               closedThisDay = true;
            }
         }

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

        void initTimesForCurrentDay(datetime currentTime) {
           // set time of range open 
           dailySignalTimeRangeFrom = SQTime.setHHMM(currentTime, SignalTimeRangeFrom);
        
           dailySignalTimeRangeTo = SQTime.setHHMM(currentTime, SignalTimeRangeTo);    

          int timeFrom = getHHMM(SignalTimeRangeFrom);
          int timeTo = getHHMM(SignalTimeRangeTo);

          if(timeFrom >= timeTo){
             if(getSQTime(currentTime) < timeTo){
                dailySignalTimeRangeFrom = SQTime.addDays(dailySignalTimeRangeFrom, -1);
             }
             else {
                dailySignalTimeRangeTo = SQTime.addDays(dailySignalTimeRangeTo, 1);
             }
          }
          else {
             if(currentTime > dailySignalTimeRangeTo) {
                    dailySignalTimeRangeFrom = SQTime.addDays(dailySignalTimeRangeFrom, 1);
                    dailySignalTimeRangeTo = SQTime.addDays(dailySignalTimeRangeTo, 1);
                }
          }

           closedThisDay = false;
        }
    };


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

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

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

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

    // create variable for class instance (required)
    CMaxDistanceFromMarket* objMaxDistanceFromMarket;


    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;
                int i = 0;

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

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

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

             for(i=0; i<OrdersTotal(); i++) {
                if (OrderSelect(i,SELECT_BY_POS)==true && 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;
    // Money Management - Fixed Size used

    //=============================================================================
    //                              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
    //
    //    14.10.2020 (Tomas Brynda) - added ERR_MARKET_CLOSED on user requested
    //    "important when using Daily strategies 
    //    during roll over market is closed for several seconds"
    //
    //===========================================================================


    //=============================================================================
    //                             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 = 0;
        double bid, ask;
        double sl, tp;
        double priceNow = 0;
        double hasSlippedBy = 0;
        
        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:
                    case ERR_MARKET_CLOSED:
                        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 " + IntegerToString(retry_attempts) +"("+IntegerToString(retry_attempts_bad_price)+")");
                    }
                }
                else
                {
                    OrderReliablePrint("Result of attempt " + IntegerToString(cnt) + " of " + IntegerToString(retry_attempts)+"("+IntegerToString(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 #" + IntegerToString(ticket) + ": Successful " + OrderTypeToString(cmd) + " order placed with comment = " + comment + ", details follow.");
                if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
                    OrderReliablePrint("Could Not Select Ticket #" + IntegerToString(ticket));
                sqOrderPrint();
                return(ticket); // SUCCESS!
            }
            if (!limit_to_market)
            {
                OrderReliablePrint("Failed to execute stop or limit order after " + IntegerToString(retry_attempts) + " retries");
                OrderReliablePrint("Failed trade: " + OrderTypeToString(cmd) + " " + DoubleToStr(volume, 2) + " lots  " + symbol +
                                   "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(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), (int) 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), (int) 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, (int)(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:
                    case ERR_MARKET_CLOSED:
                        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 " + IntegerToString(retry_attempts) +"("+IntegerToString(retry_attempts_bad_price)+")");
                }
                else
                {
                    OrderReliablePrint("Result of attempt " + IntegerToString(cnt) + " of " + IntegerToString(retry_attempts) +"("+IntegerToString(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 #" + IntegerToString(ticket) + ": Successful " + OrderTypeToString(cmd) + " order placed with comment = " + comment + ", details follow.");
                if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
                    OrderReliablePrint("Could Not Select Ticket #" + IntegerToString(ticket));
                sqOrderPrint();
                return(ticket); // SUCCESS!
            }
            
            // If not successful, log and return -1
            OrderReliablePrint("Failed to execute OP_BUY/OP_SELL, after " + IntegerToString(retry_attempts) + " retries");
            OrderReliablePrint("Failed trade: " + OrderTypeToString(cmd) + " " + DoubleToStr(volume, 2) + " lots  " + symbol +
                               "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(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 #" + IntegerToString(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 #" + IntegerToString(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 #" + IntegerToString(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:
                    case ERR_MARKET_CLOSED:
                        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 " + IntegerToString(cnt) + " of " + IntegerToString(retry_attempts)+"("+IntegerToString(retry_attempts_bad_price)+")" + ": Retryable error: " + OrderReliableErrTxt(err));
                
                SleepRandomTime(sleep_time, sleep_maximum);
                RefreshRates();
            }
            else
            {
                if (cnt > retry_attempts)
                    OrderReliablePrint("Retry attempts maxed at " + IntegerToString(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: " + IntegerToString(ticket) + " " + symbol +
                               "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(stoploss));
            OrderReliablePrint("Suggest modifying code logic to avoid.");
        }
        
        if (result)
        {
            OrderReliablePrint("Ticket #" + IntegerToString(ticket) + ": Modification successful, updated trade details follow.");
            if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
                OrderReliablePrint("Could Not Select Ticket #" + IntegerToString(ticket));
            sqOrderPrint();
        }
        else
        {
            OrderReliablePrint("Failed to execute modify after " + IntegerToString(retry_attempts) + " retries");
            OrderReliablePrint("Failed modification: "  + IntegerToString(ticket) + " " + symbol +
                               "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(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 #" + IntegerToString(ticket) + " initial price:" + DoubleToStr(price, digits+1) +
                           " lots:" + DoubleToStr(volume, 3) + " slippage:" + IntegerToString(slippage));


        if (type != OP_BUY && type != OP_SELL)
        {
            OrderReliablePrint("Error: Trying to close ticket #" + IntegerToString(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 = 0;
        double hasSlippedBy = 0;

        while (!exit_loop)
        {
            // = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : =
            // Get current price and calculate slippage
            RefreshRates();
            if (type == OP_BUY)
            {
                priceNow = NormalizeDouble(MarketInfo(symbol, MODE_BID), (int) MarketInfo(symbol, MODE_DIGITS));    // Close @ Bid
                hasSlippedBy = (price - priceNow) / point;    // (Adjusted Point)
            }
            else if (type == OP_SELL)
            {
                priceNow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), (int) 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, (int)(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:
                    case ERR_MARKET_CLOSED:
                        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 " + IntegerToString(retry_attempts));
                else if (non_retryable_error)
                    OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
            }
            else
            {
                OrderReliablePrint("Result of attempt " + IntegerToString(cnt) + " of " + IntegerToString(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 #" + IntegerToString(ticket) + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
            else if (OrderCloseTime() > 0)    // Then it closed ok
                OrderReliablePrint("Successful close of Ticket #" + IntegerToString(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 #" + IntegerToString(ticket) + ", Price: " +
                                       DoubleToString(price) + ", Slippage: " + DoubleToString(slippage));
                OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
                result = false;
            }
        }
        else
        {
            OrderReliablePrint("Failed to execute close after " + IntegerToString(cnt-1) + " retries");
            OrderReliablePrint("Failed close: Ticket #" + IntegerToString(ticket) + " @ Price: " + DoubleToString(priceNow) + 
                                  " (Requested Price: " + DoubleToString(price) + "), Slippage: " + IntegerToString(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 #" + IntegerToString(ticket) + " initial price:" + DoubleToStr(price, digits+1) +
                           " lots:" + DoubleToStr(price, 3) + " slippage:" + IntegerToString(slippage));


        if (type != OP_BUY && type != OP_SELL)
        {
            OrderReliablePrint("Error: Trying to close ticket #" + IntegerToString(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 = 0;
        int slippagenow = 0;

        while (!exit_loop)
        {
            if (type == OP_BUY)
            {
                pnow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), (int) MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
                if (pnow > price)
                {
                    // Do not allow slippage to go negative; will cause error
                    slippagenow = (int) MathMax(0, slippage - (pnow - price) / point);
                }
            }
            else if (type == OP_SELL)
            {
                pnow = NormalizeDouble(MarketInfo(symbol, MODE_BID), (int) MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
                if (pnow < price)
                {
                    // Do not allow slippage to go negative; will cause error
                    slippagenow = (int) 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:
                    case ERR_MARKET_CLOSED:
                        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 " + IntegerToString(cnt) + " of " + IntegerToString(retry_attempts)+ ": Retryable error: " + OrderReliableErrTxt(err));
                
                SleepRandomTime(sleep_time, sleep_maximum);
            }

            if (exit_loop)
            {
                if (cnt > retry_attempts)
                    OrderReliablePrint("Retry attempts maxed at " + IntegerToString(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 #" + IntegerToString(ticket) + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
            else if (OrderCloseTime() > 0)    // Then it closed ok
                OrderReliablePrint("Successful close of Ticket #" + IntegerToString(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 #" + IntegerToString(ticket) + ", Price: " +
                                       DoubleToString(price) + ", Slippage: " + IntegerToString(slippage));
                OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
                result = false;
            }
        }
        else
        {
            OrderReliablePrint("Failed to execute close after " + IntegerToString(retry_attempts) + " retries");
            OrderReliablePrint("Failed close: Ticket #" + IntegerToString(ticket) + " @ Price: " +
                                   DoubleToString(pnow) + " (Initial Price: " + DoubleToString(price) + "), Slippage: " + 
                                   IntegerToString(slippagenow) + " (Initial Slippage: " + IntegerToString(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, #" + IntegerToString(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 #" + IntegerToString(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:
                    case ERR_MARKET_CLOSED:
                        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 " + IntegerToString(cnt) + " of " + IntegerToString(retry_attempts) + ": Retryable error: " + OrderReliableErrTxt(err));
                SleepRandomTime(sleep_time, sleep_maximum);
            }
            else
            {
                if (cnt > retry_attempts)
                    OrderReliablePrint("Retry attempts maxed at " + IntegerToString(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 #" + IntegerToString(ticket));
            
            return(true); // SUCCESS!
        }
        else
        {
            OrderReliablePrint("Failed to execute delete after " + IntegerToString(retry_attempts) + " retries");
            OrderReliablePrint("Failed deletion: Ticket #" + IntegerToString(ticket));
            OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
        }
        
        
        return(result);
    }

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

    string OrderReliableErrTxt(int err)
    {
        return (IntegerToString(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: "+IntegerToString(OrderTicket())+" - "+
      "OpenTime: "+TimeToString(OrderOpenTime(), TIME_DATE | TIME_MINUTES | TIME_SECONDS)+","+
      "Type: "+IntegerToString(OrderType())+","+
      "Size: "+DoubleToString(OrderLots())+","+
      "Symbol: "+OrderSymbol()+","+
      "OpenPrice: "+DoubleToString(OrderOpenPrice())+","+
      "SL: "+DoubleToString(OrderStopLoss())+","+
      "PT: "+DoubleToString(OrderTakeProfit()));

      Print("Ticket: "+IntegerToString(OrderTicket())+" (continue) - "+
      "CloseTime: "+TimeToString(OrderCloseTime(), TIME_DATE | TIME_MINUTES | TIME_SECONDS)+","+
      "ClosePrice: "+DoubleToString(OrderClosePrice())+","+
      "Commission: "+DoubleToString(OrderCommission())+","+
      "Swap: "+DoubleToString(OrderSwap())+","+
      "Profit: "+DoubleToString(OrderProfit())+","+
      "Comment: "+OrderComment()+","+
      "MagicNumber: "+IntegerToString(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 (" + IntegerToString(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 = (int) 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, (int) 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, (int) MarketInfo(symbol, MODE_DIGITS));
            }
        }
        
        // notify if changed
        if (sl != orig_sl)
            OrderReliablePrint("SL was too close to brokers min distance (" + IntegerToString(min_stop_level) + "); moved SL to: " + DoubleToString(sl));
        if (tp != orig_tp)
            OrderReliablePrint("TP was too close to brokers min distance (" + IntegerToString(min_stop_level) + "); moved TP to: " + DoubleToString(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 = 0, hasSlippedBy;
        
        // Assume buy pendings relate to Ask, and sell pendings relate to Bid
        if (cmd % 2 == OP_BUY)
            priceNow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), (int) MarketInfo(symbol, MODE_DIGITS));
        else if (cmd % 2 == OP_SELL)
            priceNow = NormalizeDouble(MarketInfo(symbol, MODE_BID), (int) 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 = (int) 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 #" + IntegerToString(lastTicket));
            }
        }
        
        // Get bid, ask, point & digits
        bid = NormalizeDouble(MarketInfo(symb, MODE_BID), (int) MarketInfo(symb, MODE_DIGITS));
        ask = NormalizeDouble(MarketInfo(symb, MODE_ASK), (int) MarketInfo(symb, MODE_DIGITS));
        point = MarketInfo(symb, MODE_POINT);
        if (point == 0.001  ||  point == 0.00001)
            point *= 10;
            
        digits = (int) 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 == 😎     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");
    }


    //+----------------------------- Include from /MetaTrader4/CustomFunctions/GreaterCount.mq4 -------------------------------------+


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


    bool sqIsGreaterCount(string indicatorIdentificationLeft,string indicatorIdentificationRight, int bars,bool NotStrict,int shift) {

           bool atLeastOnce = false;

        for(int i=0; i<bars; i++) { /// bars != Bars in mq4

            double leftIndicator = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentificationLeft,shift+i), 5); /// precision = 5. It returns more acccurate backtest synchronisation
            double rightIndicator = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentificationRight,shift+i), 5);
            
            if(leftIndicator<rightIndicator){

                return (false);
            }
            if(leftIndicator==rightIndicator && NotStrict == false){

                return (false);
            }
            if(leftIndicator>rightIndicator){

                atLeastOnce = true;
            }
        }
        return(atLeastOnce);
    }

     
    //+----------------------------- Include from /MetaTrader4/CustomFunctions/LowerCount.mq4 -------------------------------------+


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

    bool sqIsLowerCount(string indicatorIdentificationLeft,string indicatorIdentificationRight, int bars,bool NotStrict,int shift) {

           bool atLeastOnce = false;

        for(int i=0; i<bars; i++) { /// bars != Bars in mq4

            double leftIndicator = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentificationLeft,shift+i), 5); /// precision = 5. It returns more acccurate backtest synchronisation
            double rightIndicator = NormalizeDouble(sqGetIndicatorByIdentification(indicatorIdentificationRight,shift+i), 5);
            
            if(leftIndicator>rightIndicator){

                return (false);
            }
            if(leftIndicator==rightIndicator && NotStrict == false){

                return (false);
            }
            if(leftIndicator<rightIndicator){

                atLeastOnce = true;
            }
        }
        return(atLeastOnce);
    }


     
    //+----------------------------- Include from /MetaTrader4/CustomFunctions/SessionOHLC.mq4 -------------------------------------+

    double SessionOpen(string symbol, int tf, int startHours, int startMinutes, int daysAgo){
       return getSessionPrice(symbol, tf, 1, startHours, startMinutes, startHours, startMinutes, daysAgo);
    }

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

    double SessionHigh(string symbol, int tf, int startHours, int startMinutes, int endHours, int endMinutes, int daysAgo){
       return getSessionPrice(symbol, tf, 2, startHours, startMinutes, endHours, endMinutes, daysAgo);
    }

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

    double SessionLow(string symbol, int tf, int startHours, int startMinutes, int endHours, int endMinutes, int daysAgo){
       return getSessionPrice(symbol, tf, 3, startHours, startMinutes, endHours, endMinutes, daysAgo);
    }

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

    double SessionClose(string symbol, int tf, int endHours, int endMinutes, int daysAgo){
       return getSessionPrice(symbol, tf, 4, endHours, endMinutes, endHours, endMinutes, daysAgo);
    }

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

    double getSessionPrice(string symbol, int tf, int type, int startHours, int startMinutes, int endHours, int endMinutes, int daysAgo){
       string correctedSymbol = correctSymbol(symbol);
       return iCustom(correctedSymbol, tf, "SqSessionOHLC", type, startHours, startMinutes, endHours, endMinutes, daysAgo, 0, 0);
    }
    //+----------------------------- Include from /MetaTrader4/CustomFunctions/SRPercRankAboveLevelXBars.mqh -------------------------------------+


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

    bool sqSRPercRankAboveLevelXBars(int mode,int lenght,int ATRPeriod,double Level, int bars,int shift) {

        bool atLeastOnce = false;

        for(int i=0; i<bars; i++) {  /// bars != Bars in mq4

            if(NormalizeDouble(iCustom(NULL,0,"SqSRPercentRank",mode,lenght,ATRPeriod,0,shift+i),5)<Level){
                
                return (false);
            }
            if(NormalizeDouble(iCustom(NULL,0,"SqSRPercentRank",mode,lenght,ATRPeriod,0,shift+i),5)>Level){
                
                atLeastOnce = true;
            }
        }
        return(atLeastOnce);
    }
    //+----------------------------- Include from /MetaTrader4/CustomFunctions/SRPercRankBelowLevelXBars.mqh -------------------------------------+

    bool sqSRPercRankBelowLevelXBars(int mode,int lenght,int ATRPeriod,double Level, int bars,int shift) {
        bool atLeastOnce = false;

        for(int i=0; i<bars; i++) {  /// bars != Bars in mq4

            if(NormalizeDouble(iCustom(NULL,0,"SqSRPercentRank",mode,lenght,ATRPeriod,0,shift+i),5)>Level){
                
                return (false);
            }
            if(NormalizeDouble(iCustom(NULL,0,"SqSRPercentRank",mode,lenght,ATRPeriod,0,shift+i),5)<Level){
                
                atLeastOnce = true;
            }
        }
        return(atLeastOnce);
    }
    //+----------------------------- Include from /MetaTrader4/CustomFunctions/CustomFunctions.mq4 -------------------------------------+

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

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

     

    Thousand thanks in advance :)

    Screenshot 2022-12-30 151219.png

    • Like 1
×
×
  • Create New...
us