try to convert this EA using 2calgo.net

Created at 17 Nov 2020, 18:54
How’s your experience with the cTrader Platform?
Your feedback is crucial to cTrader's development. Please take a few seconds to share your opinion and help us improve your trading experience. Thanks!
notzen's avatar

notzen

Joined 30.01.2017

try to convert this EA using 2calgo.net
17 Nov 2020, 18:54


Hello,

 

I recently try algowizard.io and it let me generate some code from strategies I have create free of charge, unfortunately it looks that support most trading programs except ctrader,

i try to use 2calgo to convert the metatrader4 code but got an error.

i past the full code here and pseudocode, anyone that would convert it (its a ichimoku bot) and share with the community is well came.

 

//+------------------------------------------------------------------+
//| ichibuono EA
//|
//| Generated by StrategyQuant 3.9.132 at 11/17/2020 17:39
//|
//| 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 = "ichibuono";

extern int MagicNumber = 11111;
bool LongEntrySignal = false;
bool ShortEntrySignal = false;
bool LongExitSignal = false;
bool ShortExitSignal = false;
extern int IchimokuTnkKjnCrsBshTnkPrd = 9;
extern int IchimokuTnkKjnCrsBshKjnPrd = 26;
extern int IchimokuTnkKjnCrsBshSnkPrd = 52;
extern int IchimokuKjnSenCrsBshTnkPrd = 9;
extern int IchimokuKjnSenCrsBshKjnPrd = 26;
extern int IchimokuKjnSenCrsBshSnkPrd = 52;
extern int IchimokuSnkSpnCrsBshTnkPrd = 9;
extern int IchimokuSnkSpnCrsBshKjnPrd = 26;
extern int IchimokuSnkSpnCrsBshSnkPrd = 52;
extern double TrailingStopCoef = 5;
extern double TrailingActCef = 5;

//+------------------------------------------------------------------+
// Money Management variables
//+------------------------------------------------------------------+
extern string smm = "----------- Money Management - Risk Fixed % Of Balance -----------";
extern double mmRiskPercent = 3.0;
extern int mmDecimals = 1;
extern double mmLotsIfNoMM = 1.0;
extern double mmMaxLots = 5.0;


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

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 smtpd = "----------- Max Trades Per Day -----------";
extern int MaxTradesPerDay = 0;
extern string smmslpt = "----------- Min/Max SL/PT -----------";
extern int MinimumSL = 0;   //Minimum SL in pips
extern int MinimumPT = 0;   //Minimum PT in pips
extern int MaximumSL = 0;   //Maximum SL in pips
extern int MaximumPT = 0;   //Maximum PT in pips


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

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

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


int     ErrorLevel             = 3;

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

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

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

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

void OnTick() {

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

   //------------------------
   // Rule: Trading signals
   //------------------------
if (sqIsBarOpen()) {
     // init signals only on bar open 
     LongEntrySignal = sqIchimokuTenkanKijunCross(1, NULL,0, IchimokuTnkKjnCrsBshTnkPrd, IchimokuTnkKjnCrsBshKjnPrd, IchimokuTnkKjnCrsBshSnkPrd, 1, 2);

     LongExitSignal = (sqIchimokuKijunSenCross(-1, NULL,0, IchimokuKjnSenCrsBshTnkPrd, IchimokuKjnSenCrsBshKjnPrd, IchimokuKjnSenCrsBshSnkPrd, 1, 1)
      && sqIchimokuSenkouSpanCross(-1, NULL,0, IchimokuSnkSpnCrsBshTnkPrd, IchimokuSnkSpnCrsBshKjnPrd, IchimokuSnkSpnCrsBshSnkPrd, 1, 0));

     ShortEntrySignal = sqIchimokuTenkanKijunCross(-1, NULL,0, IchimokuTnkKjnCrsBshTnkPrd, IchimokuTnkKjnCrsBshKjnPrd, IchimokuTnkKjnCrsBshSnkPrd, 1, 2);

     ShortExitSignal = (sqIchimokuKijunSenCross(1, NULL,0, IchimokuKjnSenCrsBshTnkPrd, IchimokuKjnSenCrsBshKjnPrd, IchimokuKjnSenCrsBshSnkPrd, 1, 1)
      && sqIchimokuSenkouSpanCross(1, NULL,0, IchimokuSnkSpnCrsBshTnkPrd, IchimokuSnkSpnCrsBshKjnPrd, IchimokuSnkSpnCrsBshSnkPrd, 1, 0));

   } 


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

         // TrailingStop initialization
         sqSetGlobalVariable(_ticket, "TrailingStop", sqStringHash("TrailingStopCoef * sqATR(NULL,0,20,1)"));
         sqSetGlobalVariable(_ticket, "TrailingStopType", SLPTTYPE_RANGE);
         sqSetGlobalVariable(_ticket, "TrailingActivation", sqStringHash("TrailingActCef * sqATR(NULL,0,20,1)"));
      }

  }

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

         // TrailingStop initialization
         sqSetGlobalVariable(_ticket, "TrailingStop", sqStringHash("TrailingStopCoef * sqATR(NULL,0,20,1)"));
         sqSetGlobalVariable(_ticket, "TrailingStopType", SLPTTYPE_RANGE);
         sqSetGlobalVariable(_ticket, "TrailingActivation", sqStringHash("TrailingActCef * sqATR(NULL,0,20,1)"));
      }

  }

                
   //------------------------
   // 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");

   gPointCoef = calculatePointCoef(Symbol());

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

   if(sqDisplayInfoPanel) {
     sqInitInfoPanel();
   }

   SQTime = new CSQTime();

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

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

   return(0);
}

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

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

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

   if(!EventSetTimer(period)){
      VerboseLog("Cannot set timer. Error code: ", 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(!objExitAtEndOfDay.onBarUpdate()) continueWithBarUpdate = false;
   if(!objExitOnFriday.onBarUpdate()) continueWithBarUpdate = false;
   if(!objLimitTimeRange.onBarUpdate()) continueWithBarUpdate = false;
   if(!objMaxTradesPerDay.onBarUpdate()) continueWithBarUpdate = false;
   if(!objMinMaxSLPT.onBarUpdate()) continueWithBarUpdate = false;

   return(continueWithBarUpdate);
}   

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

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

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

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

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

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

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

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

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

double sqGetIndicatorByIdentification(string idHash, int shift) {

   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("TrailingStopCoef * sqATR(NULL,0,20,1)")) {
      return (TrailingStopCoef * sqATR(NULL,0,20,1));
   }

   if(idHash == (string) sqStringHash("TrailingActCef * sqATR(NULL,0,20,1)")) {
      return (TrailingActCef * sqATR(NULL,0,20,1));
   }

   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("TrailingStopCoef * sqATR(NULL,0,20,1)")) {
      return (TrailingStopCoef * sqATR(NULL,0,20,1));
   }

   if(idHash == (string) sqStringHash("TrailingActCef * sqATR(NULL,0,20,1)")) {
      return (TrailingActCef * sqATR(NULL,0,20,1));
   }

   return(0);
}

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

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

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

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

            sqManageOrderExpiration(ticket);
         }

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

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

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

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

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

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

        if(OpenBarDelay > 0) {
           // set bar to open after X minutes from real open
           processBarOpen = false;
  
           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, k;
   for (i=0; i<StringLen(str); i++){
      k = StringGetChar(str, i);
      h = (h << 5) + h + k;
   }
   return(h);
}

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

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

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

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

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

void sqClosePendingOrder(int magicNumber, string symbol, int direction, string comment) {
   Verbose("Closing pending order with Magic Number: ", magicNumber, ", symbol: ", symbol, ", direction: ", 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: ", magicNumber, ", symbol: ", symbol, ", direction: ", 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 = 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));
}

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

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

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

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

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

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

         if(symbol != "Any") {
            if(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);
}

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

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

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

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

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

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

         if(symbol != "Any") {
            if(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) {
   double 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) {
   if(size <= 0) return (0);

   string correctedSymbol = correctSymbol(symbol);

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

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

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

   // check if live order exists
   if(sqSelectOrder(magicNumber, 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(sqSelectOrder(magicNumber, correctedSymbol, direction, comment, false, false, true)) {
      if(replaceExisting) {
      
         if(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;
   }
   
   string commentToUse = "";
   if(comment != ""){
      commentToUse = comment;
   }
   else {
      commentToUse = CustomComment;
      StringReplace(commentToUse, "Optimization", "Opt.");     //shorten the name of optimized strategies
   }
   commentToUse = StringSubstr(commentToUse, 0, 30);           //limit the length to 30 characters

   int ticket = OrderSendReliable(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);
}

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

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: ", marketPrice, ", order price: ", 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: ", marketPrice, ", max. price allowed: ", priceLevel, ", ", name, " price: ", 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: ", marketPrice, ", min. price allowed: ", priceLevel, ", ", name," price: ", 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 = 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: ", stopLoss, " > ", closestPossibleSL);
            return(false);
         }

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

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

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

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

         if(profitTarget > closestPossiblePT) {
            Verbose("Cannot modify PT, it cannot be closer than minimum allowed: ", profitTarget, " > ", closestPossiblePT);
            return(false);
         }
      }
   }
      
   result = OrderModifyReliable(ticket, price, sqFixMarketPrice(stopLoss, 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 = MarketInfo(symbol, MODE_DIGITS);

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

   return(price);
}


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

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

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

   return(price);
}

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

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

   return(true);
}

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

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

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

   Sleep(sleepPeriod);

   int periods = maxSleepPeriod / sleepPeriod;

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

      Sleep(sleepPeriod);
   }
}

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

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

   return("Unknown");
}

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

void sqInitInfoPanel() {
      ObjectCreate("line1", OBJ_LABEL, 0, 0, 0);
      ObjectSet("line1", OBJPROP_CORNER, sqLabelCorner);
      ObjectSet("line1", OBJPROP_YDISTANCE, sqOffsetVertical + 0 );
      ObjectSet("line1", OBJPROP_XDISTANCE, sqOffsetHorizontal);
      ObjectSetText("line1", "ichibuono", 9, "Tahoma", sqLabelColor);

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

   return(pl);
}

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

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

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

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

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

/**
* valueType: 1 - pips, 2 - real pips (ATR range), 3 - price level
*/
double sqGetSLPTLevel(double SLorPT, string symbol, int orderType, double price, int valueType, double value) {
   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;
   double val;

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

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

   return(index);
}

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

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

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

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

   return(minnum);
}

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

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

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

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

   return(index);
}

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

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

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

   return roundValue(val);
}

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

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

   ObjectCreate(name, OBJ_ARROW, 0, Time[shift], Low[shift]-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[0]);
      return (hour * 100) + minute;
   }
   else {
      Print("Incorrect time value format. Value: '" + time + "'");
      return 0;
   }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

   return(false);
}

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

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

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

   return(false);
}

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

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

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

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

   return(false);
}

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

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

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

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

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

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

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

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


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

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

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

int sqGetMonthFirstTradingDay(string symbol, int timeframe, bool includeWeekends) {
    datetime barTime = iTime(symbol, timeframe, 0);
    datetime firstTradingDate = SQTime.setDayOfMonth(barTime, 1);

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

    return TimeDay(firstTradingDate);
}

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

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

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

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

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

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

         if ((OrderOpenPrice() <= moveSLAtValue || sqDoublesAreEqual(OrderOpenPrice(), moveSLAtValue)) && (currentSL == 0 || currentSL < newSL) && !sqDoublesAreEqual(currentSL, newSL)) {
            Verbose("Moving SL 2 BE for order with ticket: ", OrderTicket(), " to :", newSL);
            if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit())) {
               error = GetLastError();
               Verbose("Failed, error: ", error, " - ", ErrorDescription(error),", Ask: ", 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: ", OrderTicket(), "  to :", newSL);
            if(!sqOrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit())) {
               error = GetLastError();
               Verbose("Failed, error: ", 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( val );
   
   if(tsValue > 0) {
      double plValue;
      int error;

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

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

      tsValue = NormalizeDouble(tsValue, digits);

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

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

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

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

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

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

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

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

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

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

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

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

     virtual void onTick() {
        if(!ExitAtEndOfDay) {
              return;
          }
        
        datetime currentTime = TimeCurrent();
        datetime currentTimeDayStart = SQTime.correctDayStart(currentTime);    
                datetime currentTimeDayEnd = SQTime.correctDayEnd(currentTime);
        
        if(!closedThisDay && currentTime >= dailyEODExitTime) {
          // we should close all positions at midnight, so close them at the first tick of a new day
              for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
            if (OrderSelect(cc, SELECT_BY_POS)) {
      
               if(!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 EODTime;
      datetime dailySignalTimeRangeFrom;
      datetime dailySignalTimeRangeTo;
      bool closedThisDay;
      
   public:
      CLimitTimeRange() {
         EODTime = D'1970.01.01';
         closedThisDay = false;
      }

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

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

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

     virtual void onTick() {
        if(!LimitTimeRange) {
            return;
        }
        
        datetime currentTime = TimeCurrent();
        if(!closedThisDay && ExitAtEndOfRange && currentTime >= dailySignalTimeRangeTo) {
           for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
              if (OrderSelect(cc, SELECT_BY_POS)) {
        
                 if(!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 end time of the current day (so that we now when new day starts)
       EODTime = SQTime.correctDayEnd(currentTime);

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


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

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

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

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

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

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

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

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

        int getNumberOfTradesToday() {
            int todayTradesCount = 0;
            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;


double sqMMRiskFixedBalancePct(string symbol, int orderType, double price, double sl, double RiskInPercent, int Decimals, double LotsIfNoMM, double MaximumLots) {
   Verbose("Computing Money Management for order -  Risk fixed % of account balance");
   
   symbol = correctSymbol(symbol);
   
   double openPrice = price > 0 ? price : (orderType == OP_BUY ? sqGetAsk(symbol) : sqGetBid(symbol));
   double LotSize=0;

   if(RiskInPercent < 0 ) {
      Verbose("Computing Money Management - Incorrect RiskInPercent size, it must be above 0");
      return(0);
   }
   
   double PointValue = MarketInfo(symbol, MODE_TICKVALUE) / MarketInfo(symbol, MODE_TICKSIZE);    
   double Smallest_Lot = MarketInfo(symbol, MODE_MINLOT);
   double Largest_Lot = MarketInfo(symbol, MODE_MAXLOT);    
   double LotStep = MarketInfo(symbol, MODE_LOTSTEP);

   //Maximum amount of money to risk 
   double moneyToRisk = AccountBalance() * RiskInPercent / 100;
                                                                                                                                                               
   //Maximum drawdown of this order if we buy 1 lot 
   double oneLotSLDrawdown = PointValue * MathAbs(openPrice - sl);
        
   if(oneLotSLDrawdown > 0) {
      LotSize = roundDown(moneyToRisk / oneLotSLDrawdown, Decimals);
   }
   else {
      LotSize = 0;
   }

   //--- MAXLOT and MINLOT management

   Verbose("Computing Money Management - Smallest_Lot: ", Smallest_Lot, ", Largest_Lot: ", Largest_Lot,", Computed LotSize: ", LotSize);
   Verbose("Max money to risk: ", moneyToRisk, ", SL:", sl, ", One lot drawdown: ", oneLotSLDrawdown, ", Point value: ", PointValue);

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

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

   if (LotSize < Smallest_Lot) {
      Verbose("Calculated LotSize is too small. Minimal allowed lot size from the broker is: ", Smallest_Lot, ". Please, increase your risk or set fixed LotSize.");
      LotSize = 0;
   }
   else if (LotSize > Largest_Lot) {
      Verbose("LotSize is too big. LotSize set to maximal allowed market value: ", Largest_Lot);
      LotSize = Largest_Lot;
   }

   return (LotSize);
}

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

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


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


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

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

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

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

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

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

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

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

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

            }  // end switch

            if (cnt > retry_attempts)
                exit_loop = true;

            if (cnt_bad_price > retry_attempts_bad_price)
                exit_loop = true;


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

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

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

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


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

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

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

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

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

            if (cnt > retry_attempts)
                exit_loop = true;

            if (cnt_bad_price > retry_attempts_bad_price)
                exit_loop = true;

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

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


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

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

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

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

    return(ticket);
}


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

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

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

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


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

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

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

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

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

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

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

            }  // end switch
        }

        if (cnt > retry_attempts)
            exit_loop = true;

        if (cnt_bad_price > retry_attempts_bad_price)
            exit_loop = true;

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

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


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

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

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


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


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

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

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

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

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

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

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

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

            }  // end switch
        }

        if (cnt > retry_attempts)
            exit_loop = true;

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

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


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

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

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


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


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

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

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

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

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

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

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

            }  // end switch
        }

        if (cnt > retry_attempts)
            exit_loop = true;

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

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

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


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

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

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


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

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

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

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

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

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

            }  // end switch
        }

        if (cnt > retry_attempts)
            exit_loop = true;

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

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

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

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


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


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


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

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

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


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

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

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


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


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

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

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

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


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

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

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

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

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

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


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


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


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


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


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

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


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


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

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

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

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

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

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

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

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

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

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

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

double getSessionPrice(int type, int startHours, int startMinutes, int endHours, int endMinutes, int daysShift){
   int totalEndDaysShift = daysShift;
   int totalStartDaysShift = daysShift;
   
   datetime currentTime = Time[0];
   
   int currentHHMM = TimeHour(currentTime) * 100 + TimeMinute(currentTime);
   int startHHMM = startHours * 100 + startMinutes;
   int endHHMM = endHours * 100 + endMinutes;
   
   if(type == 1){
      if(currentHHMM < startHHMM){
         totalEndDaysShift++;
         totalStartDaysShift++;
      }
   }
   else if(type == 4){
      if(currentHHMM >= startHHMM){
         totalEndDaysShift--;
         totalStartDaysShift--;
      }
   }
   else {
      if(startHHMM < endHHMM){
         if(currentHHMM < startHHMM){
            totalEndDaysShift++;
            totalStartDaysShift++;
         }
      }
      else {
         if(currentHHMM < startHHMM){
            totalStartDaysShift++;
         }
         else {
            totalEndDaysShift--;
         }
      }
   }
   
   int totalBars = Bars(NULL, 0);
   int currentIndex = 0;
   int lastDayOfYear = TimeDayOfYear(currentTime);
   int daysAgo = 0;
   
   double highest = -1000000000;
   double lowest = 1000000000;
   
   while(currentIndex < totalBars){
      datetime time = Time[currentIndex];
      int hhmm = TimeHour(time) * 100 + TimeMinute(time);
      int curDayOfYear = TimeDayOfYear(time);
      
      if(curDayOfYear != lastDayOfYear) {
            lastDayOfYear = curDayOfYear;
            daysAgo++;
        }
        
        if(type == 2 || type == 3){
           if(totalStartDaysShift == totalEndDaysShift){
              if(daysAgo == totalEndDaysShift && hhmm < endHHMM && hhmm >= startHHMM) {
                 if(type == 2){
                    highest = MathMax(highest, High[currentIndex]);
                 }
                 else {
                    lowest = MathMin(lowest, Low[currentIndex]);
                 }
              }
           }
           else {
              if((daysAgo == totalEndDaysShift && hhmm < endHHMM) || (daysAgo == totalStartDaysShift && hhmm >= startHHMM)) {
                 if(type == 2){
                    highest = MathMax(highest, High[currentIndex]);
                 }
                 else {
                    lowest = MathMin(lowest, Low[currentIndex]);
                 }
              }
           }
        }
        
        if(daysAgo > totalStartDaysShift || (daysAgo == totalStartDaysShift && ((type < 4 && hhmm <= startHHMM) || (type == 4 && hhmm < endHHMM)))) {
           switch(type){
              case 1: 
                 return Open[currentIndex];
              case 2: return highest != -1000000000 ? highest : 0;
              case 3: return lowest != 1000000000 ? lowest : 0;
              case 4:
                 return Close[currentIndex];
              default: return 0;
           }
        }
        
        currentIndex++;
   }
   
   switch(type){
      case 2: return highest != -1000000000 ? highest : 0;
      case 3: return lowest != 1000000000 ? lowest : 0;
      default: return 0;
   }
}
 

 

PSEUDO CODE

//--------------------------------------------------------------------
// Pseudo Source Code of ichibuono
//
//   Generated by StrategyQuant 3.9.132 for MetaTrader
//   at 11/17/2020 17:39
//--------------------------------------------------------------------

//--------------------------------------------------------------------
//  Strategy Parameters
//--------------------------------------------------------------------
int MagicNumber = 11111;
int IchimokuTnkKjnCrsBshTnkPrd = 9;
int IchimokuTnkKjnCrsBshKjnPrd = 26;
int IchimokuTnkKjnCrsBshSnkPrd = 52;
int IchimokuKjnSenCrsBshTnkPrd = 9;
int IchimokuKjnSenCrsBshKjnPrd = 26;
int IchimokuKjnSenCrsBshSnkPrd = 52;
int IchimokuSnkSpnCrsBshTnkPrd = 9;
int IchimokuSnkSpnCrsBshKjnPrd = 26;
int IchimokuSnkSpnCrsBshSnkPrd = 52;
double TrailingStopCoef = 5;
double TrailingActCef = 5;

Main chart = Current Symbol / Current TF;

//--------------------------------------------------------------------
// Trading options logic
//--------------------------------------------------------------------
Exit at End Of Day = false (2355);
Exit On Friday = false (2300);
LimitSignalsTimeRange = false (From: 0800 to: 1600, Exit at End Of Range = false);
MaxTradesPerDay = 0;
Min SL: 0, Max SL: 0, Min PT: 0, Max PT: 0; // in ticks/pips, 0 means unlimited


//--------------------------------------------------------------------
// Trading rule: Trading signals (On Bar Open)
//--------------------------------------------------------------------                   
LongEntrySignal = Ichimoku(Main chart,IchimokuTnkKjnCrsBshTnkPrd, IchimokuTnkKjnCrsBshKjnPrd, IchimokuTnkKjnCrsBshSnkPrd) TenkanSen crosses KijunSen bullish;

LongExitSignal = (Ichimoku(Main chart,IchimokuKjnSenCrsBshTnkPrd, IchimokuKjnSenCrsBshKjnPrd, IchimokuKjnSenCrsBshSnkPrd) price crosses KijunSen bearish
   and Ichimoku(Main chart,IchimokuSnkSpnCrsBshTnkPrd, IchimokuSnkSpnCrsBshKjnPrd, IchimokuSnkSpnCrsBshSnkPrd) Senkou Span cross bearish);

ShortEntrySignal = Ichimoku(Main chart,IchimokuTnkKjnCrsBshTnkPrd, IchimokuTnkKjnCrsBshKjnPrd, IchimokuTnkKjnCrsBshSnkPrd) TenkanSen crosses KijunSen bearish;

ShortExitSignal = (Ichimoku(Main chart,IchimokuKjnSenCrsBshTnkPrd, IchimokuKjnSenCrsBshKjnPrd, IchimokuKjnSenCrsBshSnkPrd) price crosses KijunSen bullish
   and Ichimoku(Main chart,IchimokuSnkSpnCrsBshTnkPrd, IchimokuSnkSpnCrsBshKjnPrd, IchimokuSnkSpnCrsBshSnkPrd) Senkou Span cross bullish);

//--------------------------------------------------------------------
// Trading rule: Long entry (On Bar Open)
//--------------------------------------------------------------------                   
if ((LongEntrySignal
   and Not ShortEntrySignal)
   and Not LongExitSignal)
{
    // Action #1
    Open Long order at Market;
        Duplicate trades: allowed; 

        Trailing Stop = TrailingStopCoef * ATR(20), TS Activation at TrailingActCef * ATR(20);

}


//--------------------------------------------------------------------
// Trading rule: Short entry (On Bar Open)
//--------------------------------------------------------------------                   
if ((ShortEntrySignal
   and Not LongEntrySignal)
   and Not ShortExitSignal)
{
    // Action #1
    Open Short order at Market;
        Duplicate trades: allowed; 

        Trailing Stop = TrailingStopCoef * ATR(20), TS Activation at TrailingActCef * ATR(20);

}


//--------------------------------------------------------------------
// Trading rule: Long exit (On Bar Open)
//--------------------------------------------------------------------                   
if (LongExitSignal
   and (MarketPosition("Any", MagicNumber, "") is Long))
{
    // Action #1
    Close Full position for Symbol = Any and Magic Number = MagicNumber;

}


//--------------------------------------------------------------------
// Trading rule: Short exit (On Bar Open)
//--------------------------------------------------------------------                   
if (ShortExitSignal
   and (MarketPosition("Any", MagicNumber, "") is Short))
{
    // Action #1
    Close Full position for Symbol = Any and Magic Number = MagicNumber;

}


 

 


@notzen