//+------------------------------------------------------------------------------+//
//)   ____  _  _  ____  ____  ____  ____  __  __    __      ___  _____  __  __   (//
//)  ( ___)( \/ )(  _ \(  _ \( ___)( ___)(  \/  )  /__\    / __)(  _  )(  \/  )  (//
//)   )__)  )  (  )(_) ))   / )__)  )__)  )    (  /(__)\  ( (__  )(_)(  )    (   (//
//)  (__)  (_/\_)(____/(_)\_)(____)(____)(_/\/\_)(__)(__)()\___)(_____)(_/\/\_)  (//
//)   http://fxdreema.com                              Copyright 2017, fxDreema  (//
//+------------------------------------------------------------------------------+//
#property copyright ""
#property link      "https://fxdreema.com"
//#property strict

/************************************************************************************************************************/
// +------------------------------------------------------------------------------------------------------------------+ //
// |                       INPUT PARAMETERS, GLOBAL VARIABLES, CONSTANTS, IMPORTS and INCLUDES                        | //
// |                      System and Custom variables and other definitions used in the project                       | //
// +------------------------------------------------------------------------------------------------------------------+ //
/************************************************************************************************************************/

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// System constants (project settings) //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
//--
#define PROJECT_ID "mt4-6800"
//--
// Point Format Rules
#define POINT_FORMAT_RULES "0.001=0.01,0.00001=0.0001,0.000001=0.0001"
//--
// Events On/Off
#define ENABLE_EVENT_TICK 1 // enable "Tick" event
#define ENABLE_EVENT_TRADE 0 // enable "Trade" event
#define ENABLE_EVENT_TIMER 0 // enable "Timer" event
//--
// Virtual Stops
#define VIRTUAL_STOPS_ENABLED 0 // enable virtual stops
#define VIRTUAL_STOPS_TIMEOUT 0 // virtual stops timeout
#define USE_EMERGENCY_STOPS "no" // "yes" to use emergency (hard stops) when virtual stops are in use. "always" to use EMERGENCY_STOPS_ADD as emergency stops when there is no virtual stop.
#define EMERGENCY_STOPS_REL 0 // use 0 to disable hard stops when virtual stops are enabled. Use a value >=0 to automatically set hard stops with virtual. Example: if 2 is used, then hard stops will be 2 times bigger than virtual ones.
#define EMERGENCY_STOPS_ADD 0 // add pips to relative size of emergency stops (hard stops)
//--
// Settings for events
#define ON_TRADE_REALTIME 0 //
#define ON_TIMER_PERIOD 60 // Timer event period (in seconds)

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// System constants (predefined constants) //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
//--
#define TLOBJPROP_TIME1 801
#define OBJPROP_TL_PRICE_BY_SHIFT 802
#define OBJPROP_TL_SHIFT_BY_PRICE 803
#define OBJPROP_FIBOVALUE 804
#define OBJPROP_FIBOPRICEVALUE 805
#define OBJPROP_BARSHIFT1 807
#define OBJPROP_BARSHIFT2 808
#define OBJPROP_BARSHIFT3 809
#define SEL_CURRENT 0
#define SEL_INITIAL 1

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// Imports, Constants, Variables //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//



//--
// Constants (Input Parameters)
input int lookBack = 10;input int atrPeriod = 14;input int MagicStart = 6800; // Magic Number, kind of...
class c
{
	public:
	static int lookBack;
	static int atrPeriod;
	static int MagicStart;
};
int c::lookBack;
int c::atrPeriod;
int c::MagicStart;


//--
// Variables (Global Variables)
class v
{
	public:
	static double atrNew;
	static double atrMax;
	static double atrMin;
	static int incr;
};
double v::atrNew;
double v::atrMax;
double v::atrMin;
int v::incr;




//VVVVVVVVVVVVVVVVVVVVVVVVV//
// System global variables //
//^^^^^^^^^^^^^^^^^^^^^^^^^//
//--
int FXD_CURRENT_FUNCTION_ID = 0;
double FXD_MILS_INIT_END    = 0;
int FXD_TICKS_FROM_START    = 0;
int FXD_MORE_SHIFT          = 0;
bool FXD_DRAW_SPREAD_INFO   = false;
bool FXD_FIRST_TICK_PASSED  = false;
bool FXD_BREAK              = false;
bool FXD_CONTINUE           = false;
bool FXD_CHART_IS_OFFLINE   = false;
bool FXD_ONTIMER_TAKEN      = false;
bool FXD_ONTIMER_TAKEN_IN_MILLISECONDS = false;
double FXD_ONTIMER_TAKEN_TIME = 0;
bool USE_VIRTUAL_STOPS = VIRTUAL_STOPS_ENABLED;
string FXD_CURRENT_SYMBOL   = "";
int FXD_BLOCKS_COUNT        = 9;
datetime FXD_TICKSKIP_UNTIL = 0;

//- for use in OnChart() event
struct fxd_onchart
{
int id;
long lparam;
double dparam;
string sparam;
};
fxd_onchart FXD_ONCHART;

//--
// Variables for On/Off
bool disabled[9];

/************************************************************************************************************************/
// +------------------------------------------------------------------------------------------------------------------+ //
// |                                                 EVENT FUNCTIONS                                                  | //
// |                           These are the main functions that controls the whole project                           | //
// +------------------------------------------------------------------------------------------------------------------+ //
/************************************************************************************************************************/

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// This function is executed once when the program starts //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
int OnInit()
{


    // Initiate Constants
    	c::lookBack = lookBack;
	c::atrPeriod = atrPeriod;
	c::MagicStart = MagicStart;




    // do or do not not initilialize on reload
    if (UninitializeReason() != 0)
    {
        if (UninitializeReason() == REASON_CHARTCHANGE)
        {
            // if the symbol is the same, do not reload, otherwise continue below
            if (FXD_CURRENT_SYMBOL == Symbol()) {return INIT_SUCCEEDED;}
        }
        else
        {
            return INIT_SUCCEEDED;
        }
    }
    FXD_CURRENT_SYMBOL = Symbol();

	v::atrNew = 0;
	v::atrMax = 0;
	v::atrMin = 0;
	v::incr = 0;




    Comment("");
    for (int i=ObjectsTotal(ChartID()); i>=0; i--)
    {
        string name = ObjectName(ChartID(), i);
        if (StringSubstr(name,0,8) == "fxd_cmnt") {ObjectDelete(ChartID(), name);}
    }
    ChartRedraw();



    //-- disable virtual stops in optimization, because graphical objects does not work
    // http://docs.mql4.com/runtime/testing
    if (IsOptimization()) {
        USE_VIRTUAL_STOPS = false;
    }

    //-- set initial local and server time
    TimeAtStart("set");

    //-- set initial balance
    AccountBalanceAtStart();

    //-- draw the initial spread info meter
    FXD_DRAW_SPREAD_INFO = !(MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_VISUAL_MODE));
    if (FXD_DRAW_SPREAD_INFO) DrawSpreadInfo();

    //-- draw initial status
    DrawStatus("waiting for tick...");

    //-- working with offline charts
    if (MQLInfoInteger(MQL_PROGRAM_TYPE) == PROGRAM_EXPERT)
    {
        FXD_CHART_IS_OFFLINE = ChartGetInteger(0, CHART_IS_OFFLINE);
    }

    if (MQLInfoInteger(MQL_PROGRAM_TYPE) != PROGRAM_SCRIPT)
    {
        if (FXD_CHART_IS_OFFLINE == true || (ENABLE_EVENT_TRADE == 1 && ON_TRADE_REALTIME == 1))
        {
            FXD_ONTIMER_TAKEN = true;
            EventSetMillisecondTimer(1);
        }
        if (ENABLE_EVENT_TIMER) {
            OnTimerSet(ON_TIMER_PERIOD);
        }
    }



    FXD_MILS_INIT_END     = (double)GetTickCount();
    FXD_FIRST_TICK_PASSED = false; // reset is needed when changing inputs

    return(INIT_SUCCEEDED);
}

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// This function is executed on every incoming tick //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
void OnTick()
{
    FXD_TICKS_FROM_START++;

    if (FXD_TICKS_FROM_START == 1) DrawStatus("working");

    //-- special system actions
    if (FXD_DRAW_SPREAD_INFO) DrawSpreadInfo();
    TicksData(""); // Collect ticks (if needed)
    if (OrdersTotal()) // this makes things faster
    {
        if (USE_VIRTUAL_STOPS) {VirtualStopsDriver();}
        ExpirationDriver();
        OCODriver(); // Check and close OCO orders
    }
    if (ENABLE_EVENT_TRADE) {OnTradeListener();}

FeedStatistics();


    // skip ticks
    if (TimeLocal() < FXD_TICKSKIP_UNTIL) {return;}

	block0(0);


    return;
}


//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// This function is executed on a period basis //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
void OnTimer()
{
    //-- to simulate ticks in offline charts, Timer is used instead of infinite loop
    //-- the next function checks for changes in price and calls OnTick() manually
    if (FXD_CHART_IS_OFFLINE && RefreshRates()) {
        OnTick();
    }
    if (ON_TRADE_REALTIME == 1) {
        OnTradeListener();
    }

    static datetime t0 = 0;
    datetime t = 0;
    bool ok = false;

    if (FXD_ONTIMER_TAKEN)
    {
        if (FXD_ONTIMER_TAKEN_TIME > 0)
        {
            if (FXD_ONTIMER_TAKEN_IN_MILLISECONDS == true)
            {
                t = GetTickCount();
            }
            else
            {
                t = TimeLocal();
            }
            if ((t - t0) >= FXD_ONTIMER_TAKEN_TIME)
            {
                t0 = t;
                ok = true;
            }
        }

        if (ok == false) {
            return;
        }
    }

}


//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// This function is executed when chart event happens //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
void OnChartEvent(
    const int id,         // Event ID
    const long& lparam,   // Parameter of type long event
    const double& dparam, // Parameter of type double event
    const string& sparam  // Parameter of type string events
)
{
    //-- write parameter to the system global variables
    FXD_ONCHART.id     = id;
    FXD_ONCHART.lparam = lparam;
    FXD_ONCHART.dparam = dparam;
    FXD_ONCHART.sparam = sparam;


    return;
}

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// This function is executed once when the program ends //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
void OnDeinit(const int reason)
{
    int reson = UninitializeReason();
    if (reson == REASON_CHARTCHANGE || reson == REASON_PARAMETERS || reason == REASON_TEMPLATE) {return;}

    //-- if Timer was set, kill it here
    EventKillTimer();

    DrawStatus("stopped");
    DrawSpreadInfo();



    if (MQLInfoInteger(MQL_TESTER)) {
        Print("Backtested in "+DoubleToString((GetTickCount()-FXD_MILS_INIT_END)/1000, 2)+" seconds");
        double tc = GetTickCount()-FXD_MILS_INIT_END;
        if (tc > 0)
        {
            Print("Average ticks per second: "+DoubleToString(FXD_TICKS_FROM_START/tc, 0));
        }
    }

    if (MQLInfoInteger(MQL_PROGRAM_TYPE) == PROGRAM_EXPERT)
    {
        switch(UninitializeReason())
        {
            case REASON_PROGRAM		: Print("Expert Advisor self terminated"); break;
            case REASON_REMOVE		: Print("Expert Advisor removed from the chart"); break;
            case REASON_RECOMPILE	: Print("Expert Advisorhas been recompiled"); break;
            case REASON_CHARTCHANGE	: Print("Symbol or chart period has been changed"); break;
            case REASON_CHARTCLOSE	: Print("Chart has been closed"); break;
            case REASON_PARAMETERS	: Print("Input parameters have been changed by a user"); break;
            case REASON_ACCOUNT		: Print("Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings"); break;
            case REASON_TEMPLATE		: Print("A new template has been applied"); break;
            case REASON_INITFAILED	: Print("OnInit() handler has returned a nonzero value"); break;
            case REASON_CLOSE			: Print("Terminal has been closed"); break;
        }
    }

    return;
}

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// This function is executed on trade events - open, close, modify //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
void EventTrade()
{


    OnTradeQueue(-1);
}

/************************************************************************************************************************/
// +------------------------------------------------------------------------------------------------------------------+ //
// |                                             Functions of blocks                                                  | //
// |              Functions that contain the actual code of the blocks and their input parameters as well             | //
// +------------------------------------------------------------------------------------------------------------------+ //
/************************************************************************************************************************/


// Block 10
void block0(int _parent_=0)
{
if (disabled[0] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=0;
	
	int Variable1 = v::incr;
	int Variable2 = v::atrMax;
	int Variable3 = v::atrMin;
	int Variable4 = 0;
	int Variable5 = 0;
	
	
	v::incr=ic_value_value((double)0);
	v::atrMax=ic_value_value((double)0);
	v::atrMin=ic_value_value((double)100000);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	
		block1(0);
	 	block8(0);
	
}



// Block 11
void block1(int _parent_=0)
{
if (disabled[1] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=1;
	
	int Cycles = c::lookBack;
	
	
	for (int i=1; i<=Cycles; i++) {	block2(1);
	 	block7(1);
	}
	/* Yellow Output */
}



// Block 12
void block2(int _parent_=0)
{
if (disabled[2] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=2;
	
	int Variable1 = v::atrNew;
	int Variable2 = 0;
	int Variable3 = 0;
	int Variable4 = 0;
	int Variable5 = 0;
	
	
	v::atrNew=ic_indicators_iATR((int)c::atrPeriod, (string)CurrentSymbol(), (ENUM_TIMEFRAMES)CurrentTimeframe(), (int)v::incr);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	
		block3(2);
	 	block5(2);
	
}



// Block 13
void block3(int _parent_=0)
{
if (disabled[3] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=3;
	
	
	if (0|(v::atrNew>v::atrMax)|0) {	block4(3);
	} else {/* Yellow Output */}
}



// Block 14
void block4(int _parent_=0)
{
if (disabled[4] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=4;
	
	int Variable1 = v::atrMax;
	int Variable2 = 0;
	int Variable3 = 0;
	int Variable4 = 0;
	int Variable5 = 0;
	
	
	v::atrMax=ic_value_value((double)v::atrNew);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	
	/* Orange Output */
}



// Block 15
void block5(int _parent_=0)
{
if (disabled[5] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=5;
	
	
	if (0|(v::atrNew<v::atrMin)|0) {	block6(5);
	} else {/* Yellow Output */}
}



// Block 16
void block6(int _parent_=0)
{
if (disabled[6] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=6;
	
	int Variable1 = v::atrMin;
	int Variable2 = 0;
	int Variable3 = 0;
	int Variable4 = 0;
	int Variable5 = 0;
	
	
	v::atrMin=ic_value_value((double)v::atrNew);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	
	/* Orange Output */
}



// Block 17
void block7(int _parent_=0)
{
if (disabled[7] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=7;
	
	int Variable1 = v::incr;
	int Variable2 = 0;
	int Variable3 = 0;
	int Variable4 = 0;
	int Variable5 = 0;
	
	
	v::incr=ic_value_value((double)v::incr)+1;
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	//=ic_value_value((double)1);
	
	/* Orange Output */
}



// Block 18
void block8(int _parent_=0)
{
if (disabled[8] || FXD_BREAK) {return;}
	FXD_CURRENT_FUNCTION_ID=8;
	
	static string Title = "info:";
	static string ObjChartSubWindow = "";
	int ObjCorner = CORNER_LEFT_UPPER;
	int ObjX = 5;
	int ObjY = 24;
	int ObjTitleFontSize = 13;
	int ObjFontSize = 10;
	static string Label1 = "atrMax:";
	static string Label2 = "atrMin:";
	static string Label3 = "";
	static string Label4 = "";
	static string Label5 = "";
	static string Label6 = "";
	static string Label7 = "";
	static string Label8 = "";
	
	
	if (!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE))
	{
	   static bool initialized = false;
	   long ObjChartID   = 0;
	   
	   int ObjAnchor     = ANCHOR_LEFT;
	   if (ObjCorner == CORNER_RIGHT_UPPER || ObjCorner == CORNER_RIGHT_LOWER)
	   {
	      ObjAnchor     = ANCHOR_RIGHT;
	   }
	
	   string namebase = "fxd_cmnt_"+"18";
	   
	   int subwindow = WindowFindVisible(ObjChartID, ObjChartSubWindow);
	
	   if (subwindow >= 0)
	   {
	      //-- draw comment title
	      if ((string)Title != "")
	      {
	         string nametitle = namebase;
	         if(ObjectFind(ObjChartID,nametitle) < 0)
	         {
	            if (!ObjectCreate(ObjChartID,nametitle,OBJ_LABEL,subwindow,0,0,0,0))
	            {
	               Print(__FUNCTION__,": failed to create text object! Error code = ",GetLastError());
	            }
	            else
	            {
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_FONTSIZE,(int)(ObjTitleFontSize));
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_COLOR,clrGold);
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_BACK,0);
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_SELECTABLE,1);
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_SELECTED,0);
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_HIDDEN,1);
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_CORNER,ObjCorner);
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_ANCHOR,ObjAnchor);
	               
	               ObjectSetString(ObjChartID,nametitle,OBJPROP_FONT,"Georgia");
	               
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_XDISTANCE,ObjX);
	               ObjectSetInteger(ObjChartID,nametitle,OBJPROP_YDISTANCE,ObjY);
	            }
	         }
	         else
	         {
	            ObjX = (int)ObjectGetInteger(ObjChartID, nametitle, OBJPROP_XDISTANCE);
	            ObjY = (int)ObjectGetInteger(ObjChartID, nametitle, OBJPROP_YDISTANCE);
	         }
	         
	         
	         ObjectSetString(ObjChartID,nametitle,OBJPROP_TEXT,(string)Title);
	         
	         ObjY = (int)(ObjY+ObjTitleFontSize/3);
	      }
	      
	      //-- draw comment rows
	      for (int i=1; i<=8; i++)
	      {
	         string text="";
	         string textlbl="";
	         
	         switch(i)
	         {
	            case 1: if (Label1 != "") { textlbl = Label1; text = (string)(ic_value_value((double)v::atrMax)); if ("double"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	            case 2: if (Label2 != "") { textlbl = Label2; text = (string)(ic_value_value((double)v::atrMin)); if ("double"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	            case 3: if (Label3 != "") { textlbl = Label3; text = (string)(ic_text_text((string)"")); if ("string"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	            case 4: if (Label4 != "") { textlbl = Label4; text = (string)(ic_text_text((string)"")); if ("string"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	            case 5: if (Label5 != "") { textlbl = Label5; text = (string)(ic_text_text((string)"")); if ("string"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	            case 6: if (Label6 != "") { textlbl = Label6; text = (string)(ic_text_text((string)"")); if ("string"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	            case 7: if (Label7 != "") { textlbl = Label7; text = (string)(ic_text_text((string)"")); if ("string"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	            case 8: if (Label8 != "") { textlbl = Label8; text = (string)(ic_text_text((string)"")); if ("string"=="datetime") {text = TimeToString((int)text, TIME_DATE|TIME_SECONDS);} } break;
	         }
	         
	         string name    = namebase+"_"+(string)i;
	         string namelbl = name+"_l";
	
	         if (textlbl == "")
	         {
	            if (!initialized)
	            {
	               //-- pre-delete
	               ObjectDelete(ObjChartID,namelbl);
	               ObjectDelete(ObjChartID,name);   
	            }
	            
	            continue;
	         }
	
	         //-- draw initial objects
	         if(ObjectFind(ObjChartID, name) < 0)
	         {
	            if (textlbl == "")
	            {
	               continue;
	            }
	            
	            if (ObjectCreate(ObjChartID,namelbl,OBJ_LABEL,subwindow,0,0,0,0))
	            {
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_CORNER,ObjCorner);
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_ANCHOR,ObjAnchor);
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_BACK,0);
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_SELECTABLE,0);
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_SELECTED,0);
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_HIDDEN,1);
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_FONTSIZE,ObjFontSize);
	               ObjectSetInteger(ObjChartID,namelbl,OBJPROP_COLOR,clrDarkGray);
	               ObjectSetString(ObjChartID,namelbl,OBJPROP_FONT,"Verdana");
	            }
	            else
	            {
	               Print(__FUNCTION__,": failed to create text object! Error code = ",GetLastError());
	            }
	            
	            if (ObjectCreate(ObjChartID,name,OBJ_LABEL,subwindow,0,0,0,0))
	            {
	               ObjectSetInteger(ObjChartID,name,OBJPROP_CORNER,ObjCorner);
	               ObjectSetInteger(ObjChartID,name,OBJPROP_ANCHOR,ObjAnchor);
	               ObjectSetInteger(ObjChartID,name,OBJPROP_BACK,0);
	               ObjectSetInteger(ObjChartID,name,OBJPROP_SELECTABLE,0);
	               ObjectSetInteger(ObjChartID,name,OBJPROP_SELECTED,0);
	               ObjectSetInteger(ObjChartID,name,OBJPROP_HIDDEN,1);
	               ObjectSetInteger(ObjChartID,name,OBJPROP_FONTSIZE,ObjFontSize);
	               ObjectSetInteger(ObjChartID,name,OBJPROP_COLOR,clrWhite);
	               ObjectSetString(ObjChartID,name,OBJPROP_FONT,"Verdana");
	            }
	            else
	            {
	               Print(__FUNCTION__,": failed to create text object! Error code = ",GetLastError());
	            }
	         }
	         else
	         {
	            if (textlbl == "")
	            {
	               ObjectDelete(ObjChartID,namelbl);
	               ObjectDelete(ObjChartID,name);
	               continue;
	            }
	         }
	         
	         ObjY  = (int)(ObjY + ObjFontSize + ObjFontSize/2);
	         
	         //-- update label objects
	         ObjectSetInteger(ObjChartID,namelbl,OBJPROP_XDISTANCE,ObjX);
	         ObjectSetInteger(ObjChartID,namelbl,OBJPROP_YDISTANCE,ObjY);
	         ObjectSetString(ObjChartID,namelbl,OBJPROP_TEXT,(string)textlbl);
	         
	         //-- update value objects
	         int x=0;
	         int xsizelbl = (int)ObjectGetInteger(ObjChartID, namelbl, OBJPROP_XSIZE);
	         
	         if (xsizelbl == 0) {
	            //-- when the object is newly created, it returns 0 for XSIZE and YSIZE, so here we will trick it somehow
	            xsizelbl = (int)(StringLen((string)textlbl)*ObjFontSize/1.5 + ObjFontSize/2);
	         }
	         
	         x = ObjX+(xsizelbl + ObjFontSize/2);
	         
	         ObjectSetInteger(ObjChartID,name,OBJPROP_XDISTANCE,x);
	         ObjectSetInteger(ObjChartID,name,OBJPROP_YDISTANCE,ObjY);
	         ObjectSetString(ObjChartID,name,OBJPROP_TEXT,(string)text);
	      }
			
			ChartRedraw();
	   }
	   initialized = true;
	}
	
	/* Orange Output */
	
}



double ic_value_value(double Value)
{
return(Value);
}


double ic_indicators_iATR(int ATRperiod, string SYMBOL, ENUM_TIMEFRAMES TIMEFRAME, int SHIFT)
{
SHIFT=SHIFT+IndicatorMoreShift();
	double retval=iATR(SYMBOL,TIMEFRAME,ATRperiod,SHIFT);
	SetLastIndicatorData(retval,SYMBOL,TIMEFRAME,SHIFT);
	return(retval);
}


string ic_text_text(string Text)
{
return(Text);
}



/************************************************************************************************************************/
// +------------------------------------------------------------------------------------------------------------------+ //
// |                                                   Functions                                                      | //
// |                                 System and Custom functions used in the program                                  | //
// +------------------------------------------------------------------------------------------------------------------+ //
/************************************************************************************************************************/


double AccountBalanceAtStart()
{
   // This function MUST be run once at pogram's start
	static double memory=0;
   if (memory==0) {memory=AccountBalance();}
   return(memory);
}

template<typename T>
int ArraySearch(T &array[], T value)
{
	static int index;    
	static int size;
	
	index = -1;
	size  = ArraySize(array);

	for (int i=0; i<size; i++)
	{
		if (array[i] == value)
		{
			index = i;
			break;
		}  
	}

   return index;
}

template<typename T>
bool ArrayStripKey(T &array[], int key)
{
	int x    = 0;
	int size = ArraySize(array);
	
	for (int i=0; i<size; i++)
	{
		if (i != key)
		{
			array[x] = array[i];
			x++;
		}
	}
		
	if (x < size)
	{
		ArrayResize(array, x);
		
		return true; // stripped
	}
	
	return false; // not stripped
}

int CheckForTradingError(int error_code=-1, string msg_prefix="")
{
   // return 0 -> no error
   // return 1 -> overcomable error
   // return 2 -> fatal error
   
   if (error_code<0) {
      error_code=GetLastError();  
   }
   
   int retval=0;
   static int tryouts=0;
   
   //-- error check -----------------------------------------------------
   switch(error_code)
   {
      //-- no error
      case 0:
         retval=0;
         break;
      //-- overcomable errors
      case 1: // No error returned
         RefreshRates();
         retval=1;
         break;
      case 4: //ERR_SERVER_BUSY
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         Sleep(1000);
         RefreshRates();
         retval=1;
         break;
      case 6: //ERR_NO_CONNECTION
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         while(!IsConnected()) {Sleep(100);}
         while(IsTradeContextBusy()) {Sleep(50);}
         RefreshRates();
         retval=1;
         break;
      case 128: //ERR_TRADE_TIMEOUT
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         RefreshRates();
         retval=1;
         break;
      case 129: //ERR_INVALID_PRICE
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         if (!IsTesting()) {while(RefreshRates()==false) {Sleep(1);}}
         retval=1;
         break;
      case 130: //ERR_INVALID_STOPS
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Waiting for a new tick to retry.."));}
         if (!IsTesting()) {while(RefreshRates()==false) {Sleep(1);}}
         retval=1;
         break;
      case 135: //ERR_PRICE_CHANGED
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Waiting for a new tick to retry.."));}
         if (!IsTesting()) {while(RefreshRates()==false) {Sleep(1);}}
         retval=1;
         break;
      case 136: //ERR_OFF_QUOTES
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Waiting for a new tick to retry.."));}
         if (!IsTesting()) {while(RefreshRates()==false) {Sleep(1);}}
         retval=1;
         break;
      case 137: //ERR_BROKER_BUSY
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         Sleep(1000);
         retval=1;
         break;
      case 138: //ERR_REQUOTE
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Waiting for a new tick to retry.."));}
         if (!IsTesting()) {while(RefreshRates()==false) {Sleep(1);}}
         retval=1;
         break;
      case 142: //This code should be processed in the same way as error 128.
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         RefreshRates();
         retval=1;
         break;
      case 143: //This code should be processed in the same way as error 128.
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         RefreshRates();
         retval=1;
         break;
      /*case 145: //ERR_TRADE_MODIFY_DENIED
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Waiting for a new tick to retry.."));}
         while(RefreshRates()==false) {Sleep(1);}
         return(1);
      */
      case 146: //ERR_TRADE_CONTEXT_BUSY
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code),". Retrying.."));}
         while(IsTradeContextBusy()) {Sleep(50);}
         RefreshRates();
         retval=1;
         break;
      //-- critical errors
      default:
         if (msg_prefix!="") {Print(StringConcatenate(msg_prefix,": ",ErrorMessage(error_code)));}
         retval=2;
         break;
   }

   if (retval==0) {tryouts=0;}
   else if (retval==1) {
      tryouts++;
      if (tryouts>=10) {
         tryouts=0;
         retval=2;
      } else {
         Print("retry #"+(string)tryouts+" of 10");
      }
   }
   
   return(retval);
}

bool CloseTrade(int ticket, double slippage=0, color arrowcolor=CLR_NONE)
{
   bool success=false;
   if (!OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) {return(false);}
   
   while(true)
   {
      //-- wait if needed -----------------------------------------------
      WaitTradeContextIfBusy();
      //-- close --------------------------------------------------------
      success=OrderClose(ticket,attrLots(),attrClosePrice(),(int)(slippage*PipValue(attrSymbol())),arrowcolor);
      if (success==true) {
         if (USE_VIRTUAL_STOPS) {
            VirtualStopsDriver("clear",ticket);
         }
         RegisterEvent("trade");
         return(true);
      }
      //-- errors -------------------------------------------------------
      int erraction=CheckForTradingError(GetLastError(), "Closing trade #"+(string)ticket+" error");
      switch(erraction)
      {
         case 0: break;    // no error
         case 1: continue; // overcomable error
         case 2: break;    // fatal error
      }
      break;
   }
   return(false);
}

string CurrentSymbol(string symbol="")
{
   static string memory="";
   if (symbol!="") {memory=symbol;} else
   if (memory=="") {memory=Symbol();}
   return(memory);
}

ENUM_TIMEFRAMES CurrentTimeframe(ENUM_TIMEFRAMES tf=-1)
{
	static ENUM_TIMEFRAMES memory=0;
   if (tf>=0) {memory=tf;}
   return(memory);
}

bool DeleteOrder(int ticket, color arrowcolor)
{
   bool success=false;
   if (!OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) {return(false);}
   
   while(true)
   {
      //-- wait if needed -----------------------------------------------
      WaitTradeContextIfBusy();
      //-- delete -------------------------------------------------------
      success=OrderDelete(ticket,arrowcolor);
      if (success==true) {
         if (USE_VIRTUAL_STOPS) {
            VirtualStopsDriver("clear",ticket);
         }
         RegisterEvent("trade");
         return(true);
      }
      //-- error check --------------------------------------------------
      int erraction=CheckForTradingError(GetLastError(), "Deleting order #"+(string)ticket+" error");
      switch(erraction)
      {
         case 0: break;    // no error
         case 1: continue; // overcomable error
         case 2: break;    // fatal error
      }
      break;
   }
   return(false);
}

void DrawSpreadInfo()
{
   static bool allow_draw = true;
   if (allow_draw==false) {return;}
   if (MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_VISUAL_MODE)) {allow_draw=false;} // Allowed to draw only once in testing mode

   static bool passed         = false;
   static double max_spread   = 0;
   static double min_spread   = EMPTY_VALUE;
   static double avg_spread   = 0;
   static double avg_add      = 0;
   static double avg_cnt      = 0;

   double custom_point = CustomPoint(Symbol());
   double current_spread = 0;
   if (custom_point > 0) {
      current_spread = (SymbolInfoDouble(Symbol(),SYMBOL_ASK)-SymbolInfoDouble(Symbol(),SYMBOL_BID))/custom_point;
   }
   if (current_spread > max_spread) {max_spread = current_spread;}
   if (current_spread < min_spread) {min_spread = current_spread;}
   
   avg_cnt++;
   avg_add     = avg_add + current_spread;
   avg_spread  = avg_add / avg_cnt;

   int x=0; int y=0;
   string name;

   // create objects
   if (passed == false)
   {
      passed=true;
      
      name="fxd_spread_current_label";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+1);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+1);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 18);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrDarkOrange);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "Spread:");
      }
      name="fxd_spread_max_label";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+148);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+17);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 7);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrOrangeRed);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "max:");
      }
      name="fxd_spread_avg_label";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+148);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+9);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 7);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrDarkOrange);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "avg:");
      }
      name="fxd_spread_min_label";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+148);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+1);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 7);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrGold);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "min:");
      }
      name="fxd_spread_current";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+93);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+1);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 18);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrDarkOrange);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "0");
      }
      name="fxd_spread_max";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+173);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+17);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 7);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrOrangeRed);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "0");
      }
      name="fxd_spread_avg";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+173);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+9);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 7);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrDarkOrange);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "0");
      }
      name="fxd_spread_min";
      if (ObjectFind(0, name)==-1) {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x+173);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y+1);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
         ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 7);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrGold);
         ObjectSetString(0, name, OBJPROP_FONT, "Arial");
         ObjectSetString(0, name, OBJPROP_TEXT, "0");
      }
   }
   
   ObjectSetString(0, "fxd_spread_current", OBJPROP_TEXT, DoubleToStr(current_spread,2));
   ObjectSetString(0, "fxd_spread_max", OBJPROP_TEXT, DoubleToStr(max_spread,2));
   ObjectSetString(0, "fxd_spread_avg", OBJPROP_TEXT, DoubleToStr(avg_spread,2));
   ObjectSetString(0, "fxd_spread_min", OBJPROP_TEXT, DoubleToStr(min_spread,2));
}

string DrawStatus(string text="")
{
   static string memory;
   if (text=="") {
      return(memory);
   }
   
   static bool passed = false;
   int x=210; int y=0;
   string name;

   //-- draw the objects once
   if (passed == false)
   {
      passed = true;
      name="fxd_status_title";
      ObjectCreate(0,name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0,name, OBJPROP_BACK, false);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
      ObjectSetInteger(0,name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0,name, OBJPROP_YDISTANCE, y+17);
      ObjectSetString(0,name, OBJPROP_TEXT, "Status");
      ObjectSetString(0,name, OBJPROP_FONT, "Arial");
      ObjectSetInteger(0,name, OBJPROP_FONTSIZE, 7);
      ObjectSetInteger(0,name, OBJPROP_COLOR, clrGray);
      
      name="fxd_status_text";
      ObjectCreate(0,name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0,name, OBJPROP_BACK, false);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
      ObjectSetInteger(0,name, OBJPROP_XDISTANCE, x+2);
      ObjectSetInteger(0,name, OBJPROP_YDISTANCE, y+1);
      ObjectSetString(0,name, OBJPROP_FONT, "Arial");
      ObjectSetInteger(0,name, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0,name, OBJPROP_COLOR, clrAqua);
   }

   //-- update the text when needed
   if (text != memory) {
      memory=text;
      ObjectSetString(0,"fxd_status_text", OBJPROP_TEXT, text);
   }
   
   return(text);
}

string ErrorMessage(int error_code=-1)
{
	string e = "";
	
	if (error_code < 0) {error_code = GetLastError();}
	
	switch(error_code)
	{
		//-- codes returned from trade server
		case 0:	return("");
		case 1:	e = "No error returned"; break;
		case 2:	e = "Common error"; break;
		case 3:	e = "Invalid trade parameters"; break;
		case 4:	e = "Trade server is busy"; break;
		case 5:	e = "Old version of the client terminal"; break;
		case 6:	e = "No connection with trade server"; break;
		case 7:	e = "Not enough rights"; break;
		case 8:	e = "Too frequent requests"; break;
		case 9:	e = "Malfunctional trade operation (never returned error)"; break;
		case 64:  e = "Account disabled"; break;
		case 65:  e = "Invalid account"; break;
		case 128: e = "Trade timeout"; break;
		case 129: e = "Invalid price"; break;
		case 130: e = "Invalid Sl or TP"; break;
		case 131: e = "Invalid trade volume"; break;
		case 132: e = "Market is closed"; break;
		case 133: e = "Trade is disabled"; break;
		case 134: e = "Not enough money"; break;
		case 135: e = "Price changed"; break;
		case 136: e = "Off quotes"; break;
		case 137: e = "Broker is busy (never returned error)"; break;
		case 138: e = "Requote"; break;
		case 139: e = "Order is locked"; break;
		case 140: e = "Only long trades allowed"; break;
		case 141: e = "Too many requests"; break;
		case 145: e = "Modification denied because order too close to market"; break;
		case 146: e = "Trade context is busy"; break;
		case 147: e = "Expirations are denied by broker"; break;
		case 148: e = "Amount of open and pending orders has reached the limit"; break;
		case 149: e = "Hedging is prohibited"; break;
		case 150: e = "Prohibited by FIFO rules"; break;
		
		//-- mql4 errors
		case 4000: e = "No error"; break;
		case 4001: e = "Wrong function pointer"; break;
		case 4002: e = "Array index is out of range"; break;
		case 4003: e = "No memory for function call stack"; break;
		case 4004: e = "Recursive stack overflow"; break;
		case 4005: e = "Not enough stack for parameter"; break;
		case 4006: e = "No memory for parameter string"; break;
		case 4007: e = "No memory for temp string"; break;
		case 4008: e = "Not initialized string"; break;
		case 4009: e = "Not initialized string in array"; break;
		case 4010: e = "No memory for array string"; break;
		case 4011: e = "Too long string"; break;
		case 4012: e = "Remainder from zero divide"; break;
		case 4013: e = "Zero divide"; break;
		case 4014: e = "Unknown command"; break;
		case 4015: e = "Wrong jump"; break;
		case 4016: e = "Not initialized array"; break;
		case 4017: e = "dll calls are not allowed"; break;
		case 4018: e = "Cannot load library"; break;
		case 4019: e = "Cannot call function"; break;
		case 4020: e = "Expert function calls are not allowed"; break;
		case 4021: e = "Not enough memory for temp string returned from function"; break;
		case 4022: e = "System is busy"; break;
		case 4050: e = "Invalid function parameters count"; break;
		case 4051: e = "Invalid function parameter value"; break;
		case 4052: e = "String function internal error"; break;
		case 4053: e = "Some array error"; break;
		case 4054: e = "Incorrect series array using"; break;
		case 4055: e = "Custom indicator error"; break;
		case 4056: e = "Arrays are incompatible"; break;
		case 4057: e = "Global variables processing error"; break;
		case 4058: e = "Global variable not found"; break;
		case 4059: e = "Function is not allowed in testing mode"; break;
		case 4060: e = "Function is not confirmed"; break;
		case 4061: e = "Send mail error"; break;
		case 4062: e = "String parameter expected"; break;
		case 4063: e = "Integer parameter expected"; break;
		case 4064: e = "Double parameter expected"; break;
		case 4065: e = "Array as parameter expected"; break;
		case 4066: e = "Requested history data in update state"; break;
		case 4099: e = "End of file"; break;
		case 4100: e = "Some file error"; break;
		case 4101: e = "Wrong file name"; break;
		case 4102: e = "Too many opened files"; break;
		case 4103: e = "Cannot open file"; break;
		case 4104: e = "Incompatible access to a file"; break;
		case 4105: e = "No order selected"; break;
		case 4106: e = "Unknown symbol"; break;
		case 4107: e = "Invalid price parameter for trade function"; break;
		case 4108: e = "Invalid ticket"; break;
		case 4109: e = "Trade is not allowed in the expert properties"; break;
		case 4110: e = "Longs are not allowed in the expert properties"; break;
		case 4111: e = "Shorts are not allowed in the expert properties"; break;
		
		//-- objects errors
		case 4200: e = "Object is already exist"; break;
		case 4201: e = "Unknown object property"; break;
		case 4202: e = "Object is not exist"; break;
		case 4203: e = "Unknown object type"; break;
		case 4204: e = "No object name"; break;
		case 4205: e = "Object coordinates error"; break;
		case 4206: e = "No specified subwindow"; break;
		case 4207: e = "Graphical object error"; break;  
		case 4210: e = "Unknown chart property"; break;
		case 4211: e = "Chart not found"; break;
		case 4212: e = "Chart subwindow not found"; break;
		case 4213: e = "Chart indicator not found"; break;
		case 4220: e = "Symbol select error"; break;
		case 4250: e = "Notification error"; break;
		case 4251: e = "Notification parameter error"; break;
		case 4252: e = "Notifications disabled"; break;
		case 4253: e = "Notification send too frequent"; break;
		
		//-- ftp errors
		case 4260: e = "FTP server is not specified"; break;
		case 4261: e = "FTP login is not specified"; break;
		case 4262: e = "FTP connection failed"; break;
		case 4263: e = "FTP connection closed"; break;
		case 4264: e = "FTP path not found on server"; break;
		case 4265: e = "File not found in the MQL4\\Files directory to send on FTP server"; break;
		case 4266: e = "Common error during FTP data transmission"; break;
		
		//-- filesystem errors
		case 5001: e = "Too many opened files"; break;
		case 5002: e = "Wrong file name"; break;
		case 5003: e = "Too long file name"; break;
		case 5004: e = "Cannot open file"; break;
		case 5005: e = "Text file buffer allocation error"; break;
		case 5006: e = "Cannot delete file"; break;
		case 5007: e = "Invalid file handle (file closed or was not opened)"; break;
		case 5008: e = "Wrong file handle (handle index is out of handle table)"; break;
		case 5009: e = "File must be opened with FILE_WRITE flag"; break;
		case 5010: e = "File must be opened with FILE_READ flag"; break;
		case 5011: e = "File must be opened with FILE_BIN flag"; break;
		case 5012: e = "File must be opened with FILE_TXT flag"; break;
		case 5013: e = "File must be opened with FILE_TXT or FILE_CSV flag"; break;
		case 5014: e = "File must be opened with FILE_CSV flag"; break;
		case 5015: e = "File read error"; break;
		case 5016: e = "File write error"; break;
		case 5017: e = "String size must be specified for binary file"; break;
		case 5018: e = "Incompatible file (for string arrays-TXT, for others-BIN)"; break;
		case 5019: e = "File is directory, not file"; break;
		case 5020: e = "File does not exist"; break;
		case 5021: e = "File cannot be rewritten"; break;
		case 5022: e = "Wrong directory name"; break;
		case 5023: e = "Directory does not exist"; break;
		case 5024: e = "Specified file is not directory"; break;
		case 5025: e = "Cannot delete directory"; break;
		case 5026: e = "Cannot clean directory"; break;
		
		//-- other errors
		case 5027: e = "Array resize error"; break;
		case 5028: e = "String resize error"; break;
		case 5029: e = "Structure contains strings or dynamic arrays"; break;
		
		//-- http request
		case 5200: e = "Invalid URL"; break;
		case 5201: e = "Failed to connect to specified URL"; break;
		case 5202: e = "Timeout exceeded"; break;
		case 5203: e = "HTTP request failed"; break;

		default:	e = "Unknown error";
	}

	e = StringConcatenate(e, " (", error_code, ")");
	
	return e;
}

void ExpirationDriver()
{
   static int last_checked_ticket;
   static int db_tickets[];
   static int db_expirations[];

   static int total; total   = OrdersTotal();
   static int size;  size    = 0;
   static int do_reset; do_reset=false;
   static string print;
   static int i;
   
   //-- check expirations and close trades
   size = ArraySize(db_tickets);
   if (size>0)
   {
      if (total==0) {
         ArrayResize(db_tickets, 0);
         ArrayResize(db_expirations, 0);
      }
      else
      {
         for (i=0; i<size; i++)
         {
            WaitTradeContextIfBusy();
            if (!OrderSelect(db_tickets[i],SELECT_BY_TICKET, MODE_TRADES)) {continue;}
            if (OrderSymbol() != Symbol()) {continue;}
            
            if (TimeCurrent() >= OrderOpenTime()+db_expirations[i]) {
               
               //-- trying to skip conflicts with the same functionality running from neighbour EA
               WaitTradeContextIfBusy();
               if (!OrderSelect(db_tickets[i],SELECT_BY_TICKET, MODE_TRADES)) {continue;}
               if (OrderCloseTime()>0) {continue;}
               
               //-- closing the trade
               if (CloseTrade(OrderTicket())) 
               {
                  print = "#"+(string)OrderTicket()+" was closed due to expiration";
                  Print(print);
                  last_checked_ticket=0;
                  do_reset = true;
                  total    = OrdersTotal();
               }
            }
         }
      }
   }
   
   //-- check the ticket of the newest trade
   if (do_reset==false && total>0)
   {
      if (OrderSelect(total-1,SELECT_BY_POS)) {
         if (OrderTicket()!=last_checked_ticket) {
            do_reset = true;
         }
      }
   }

   //-- rebuild the database of trades with expirations
   if (do_reset==true)
   {
      static string comment;
      ArrayResize(db_tickets, 0);
      ArrayResize(db_expirations, 0);
      for (int pos=0; pos<total; pos++)
      {
         if (!OrderSelect(pos,SELECT_BY_POS)) {continue;}
         last_checked_ticket = OrderTicket();

         comment = OrderComment();
         int exp_pos_begin = StringFind(comment, "[exp:");
         if (exp_pos_begin >= 0)
         {
            exp_pos_begin = exp_pos_begin+5;
            int exp_pos_end = StringFind(comment, "]", exp_pos_begin);
            if (exp_pos_end == -1) {continue;}
            
            size = ArraySize(db_tickets);
            ArrayResize(db_tickets, size+1);
            ArrayResize(db_expirations, size+1);
            db_tickets[size]     = OrderTicket();
            db_expirations[size] = (int)StringToInteger(StringSubstr(comment, exp_pos_begin, exp_pos_end));
         }
      }
   }
}

bool FeedStatistics(){GetStatistics();return(0);}

double GetStatistics(string get="") {
   
   if (false) {FeedStatistics();}
   //////
   // main static variables
   static datetime start_time=-1;
   static double initial_money=-1;
   static double total_net_profit=-1;
   
   int shorts_now_count=0;
   int longs_now_count=0;
   
   static int shorts_hist_count=0;
   static int longs_hist_count=0;
   
   static double longs_hist_profit=0;
   static double longs_hist_loss=0;
   static double shorts_hist_profit=0;
   static double shorts_hist_loss=0;
   static double longs_hist_profit_count=0;
   static double longs_hist_loss_count=0;
   static double shorts_hist_profit_count=0;
   static double shorts_hist_loss_count=0;
   
   static double largest_profit_trade=0;
   static double smallest_profit_trade=0;
   static double largest_loss_trade=0;
   static double smallest_loss_trade=0;
   static double profit_trades_count=0;
   static double loss_trades_count=0;
   static double average_profit_trade=0;
   static double average_loss_trade=0;
   
   static int consec_wins=0;
   static int consec_loss=0;
   static double last_profit=0;
   static bool consec_check_started=false;
   static int max_consec_wins=0;
   static int max_consec_loss=0;
   static double avg_consec_wins=0;
   static double avg_consec_loss=0;
   static int consec_profits_count=0;
   static int consec_losses_count=0;
   
   static double profit_factor=1;
   static double gross_profit=0;
   static double gross_loss=0;
   
   static double drawdown_abs=0;
   static double drawdown_rel=0;
   static double drawdown_max=0;
   static double maxpeak=0;
   static double minpeak=0;
   
   static double drawdown_balance_abs=0;
   static double drawdown_balance_rel=0;
   static double drawdown_balance_max=0;
   static double max_balance_peak=0;
   static double min_balance_peak=0;
   
   double profit_factor_live=0;
   double gross_profit_now=0;
   double gross_profit_live=0;
   double gross_loss_now=0;
   double gross_loss_live=0;
   //////
   
   //////
   // system static variables
   static int last_checked_trades_ticket=-1;
   static int last_checked_history_ticket=-1;
   static int orders_history_total=0;
   static int orders_history_total_checked=0; 
   static int orders_total=0;
   double retval=0;
   //////
   
   int pos=0;
   if (initial_money==-1) {initial_money=AccountEquity();}
   if (start_time==-1) {start_time=TimeCurrent();}
   total_net_profit=AccountEquity()-initial_money;
   
   if (OrdersHistoryTotal()!=orders_history_total)
   {
      orders_history_total=OrdersHistoryTotal();
      for (pos=OrdersHistoryTotal()-1; pos>=0; pos--)
      {
         if (OrderSelect(pos,SELECT_BY_POS,MODE_HISTORY))
         {
            //if (OrderOpenTime()>=start_time) {
               if (orders_history_total > orders_history_total_checked)
               {
                  orders_history_total_checked++;
                  double PureProfit=OrderProfit()+OrderCommission()+OrderSwap();
                  if (PureProfit>largest_profit_trade) {largest_profit_trade=PureProfit;}
                  if (PureProfit<largest_loss_trade) {largest_loss_trade=PureProfit;}
                  if (PureProfit>0 && (PureProfit<smallest_profit_trade || smallest_profit_trade==0)) {smallest_profit_trade=PureProfit;}
                  if (PureProfit<0 && (PureProfit>smallest_loss_trade || smallest_loss_trade==0)) {smallest_loss_trade=PureProfit;}
               
                  if (OrderType()==OP_BUY) {longs_hist_count++;}
                  if (OrderType()==OP_SELL) {shorts_hist_count++;}
               
                  if (PureProfit>0)
                  {
                     if (OrderType()==OP_BUY) {longs_hist_profit_count++; longs_hist_profit=longs_hist_profit+PureProfit;}
                     if (OrderType()==OP_SELL) {shorts_hist_profit_count++; shorts_hist_profit=shorts_hist_profit+PureProfit;}
                     gross_profit=gross_profit+PureProfit;
                     profit_trades_count++;
                     average_profit_trade=gross_profit/profit_trades_count;
                     if (last_profit>0 || consec_check_started==false) {
                        consec_check_started=true; consec_wins++;
                     } else {consec_wins=1; consec_loss=0; consec_profits_count++;}
                    
                     if (consec_wins>max_consec_wins) {max_consec_wins=consec_wins;}
                     avg_consec_wins=profit_trades_count/(consec_profits_count+1);
                     last_profit=PureProfit;
                  }
                  else if (PureProfit<0)
                  {
                     if (OrderType()==OP_BUY) {longs_hist_loss_count++; longs_hist_loss=longs_hist_loss+PureProfit;}
                     if (OrderType()==OP_SELL) {shorts_hist_loss_count++; shorts_hist_loss=shorts_hist_loss+PureProfit;}
                     gross_loss=gross_loss+PureProfit;
                     loss_trades_count++;
                     average_loss_trade=gross_loss/loss_trades_count;
                     if (last_profit<0 || consec_check_started==false) {
                        consec_check_started=true; consec_loss++;
                     }
                     else {
                        consec_loss=1;
                        consec_wins=0;
                        consec_losses_count++;
                     }
                  
                     if (consec_loss>max_consec_loss) {
                        max_consec_loss=consec_loss;
                     }
                     avg_consec_loss=loss_trades_count/(consec_losses_count+1);
                     last_profit=PureProfit;
                  }
               }
            //} else {break;}
         }
      }
   }
   
   // Equity: Drawdown Maximum && Drawdown Relative
   if (AccountEquity()>maxpeak) {maxpeak=AccountEquity();}
   if ((maxpeak-AccountEquity())>drawdown_max) {drawdown_max=(maxpeak-AccountEquity()); drawdown_rel=NormalizeDouble((drawdown_max/maxpeak)*100,2);}
   
   // Equity: Drawdown Absolute
   if ((AccountEquity()<initial_money && (initial_money-AccountEquity())>drawdown_abs) || drawdown_abs==0) {drawdown_abs=(initial_money-AccountEquity());}
   
   // Balance: Drawdown Maximum && Drawdown Relative
   if (AccountBalance()>max_balance_peak) {max_balance_peak=AccountBalance();}
   if ((max_balance_peak-AccountBalance())>drawdown_balance_max) {drawdown_balance_max=(max_balance_peak-AccountBalance()); drawdown_balance_rel=NormalizeDouble((drawdown_balance_max/max_balance_peak)*100,2);}
   
   // Balance: Drawdown Absolute
   if ((AccountBalance()<initial_money && (initial_money-AccountBalance())>drawdown_balance_abs) || drawdown_balance_abs==0) {drawdown_balance_abs=(initial_money-AccountBalance());}
   
   if (get!="") {
   
      for (pos=OrdersTotal()-1; pos>=0; pos--)
      {
         if (OrderSelect(pos,SELECT_BY_POS,MODE_TRADES))
         {
            //if (OrderOpenTime()>=start_time) {
               if (OrderType()==OP_BUY) {longs_now_count++;}
               else if (OrderType()==OP_SELL) {shorts_now_count++;}
				  
               if (OrderProfit()+OrderCommission()+OrderSwap()>0) {
                  gross_profit_now=gross_profit_now+OrderProfit()+OrderCommission()+OrderSwap();
               }
               else if (OrderProfit()+OrderCommission()+OrderSwap()<0) {
                  gross_loss_now=gross_loss_now+OrderProfit()+OrderCommission()+OrderSwap();
               }
               if (OrderTicket()>last_checked_trades_ticket) {
                  last_checked_trades_ticket=OrderTicket();
               }
            //} else {break;}
         }
      }
      
      // Profit Factor
      if (gross_loss<0) {
         profit_factor=MathAbs(NormalizeDouble(gross_profit/gross_loss,2));
      }
      else {
         profit_factor=MathAbs(NormalizeDouble(gross_profit,2));
      }
      if (profit_factor==0) {profit_factor=1;}
      
      // Gross Profit / Loss (Live)
      gross_profit_live=gross_profit+gross_profit_now;
      gross_loss_live=gross_loss+gross_loss_now;
      
      // Profit Factor (Live)
      if ((gross_loss+gross_loss_now)<0) {
         profit_factor_live=MathAbs(NormalizeDouble(((gross_profit+gross_profit_now)/(gross_loss+gross_loss_now)),2));
      }
      else {
         profit_factor_live=MathAbs(NormalizeDouble((gross_profit+gross_profit_now),2));
      }
      if (profit_factor_live==0) {profit_factor_live=1;}
      
      // Total Trades
      int longs_total_count   =longs_hist_count+longs_now_count;
      int shorts_total_count  =shorts_hist_count+shorts_now_count;
      int trades_hist_count   =longs_hist_count+shorts_hist_count;
      int trades_now_count    =longs_now_count+shorts_now_count;
      int trades_total_count  =longs_total_count+shorts_total_count;
      
      if (get=="initial_money")        {return(initial_money);}
      //---
      if (get=="profit_factor_history"){return(profit_factor);}
      if (get=="profit_factor_total")  {return(profit_factor_live);}
      //---
      if (get=="gross_profit_history") {return(gross_profit);}
      if (get=="gross_profit_now")     {return(gross_profit_now);}
      if (get=="gross_profit_total")   {return(gross_profit_live);}
      //---
      if (get=="gross_loss_history")   {return(gross_loss);}
      if (get=="gross_loss_now")       {return(gross_loss_now);}
      if (get=="gross_loss_total")     {return(gross_loss_live);}
      //---
      if (get=="trades_count_history") {return(trades_hist_count);}
      if (get=="trades_count_now")     {return(trades_now_count);}
      if (get=="trades_count_total")   {return(trades_total_count);}
      //---
      if (get=="longs_count_history")  {return(longs_hist_count);}
      if (get=="longs_count_now")      {return(longs_now_count);}
      if (get=="longs_count_total")    {return(longs_total_count);}
      //---
      if (get=="shorts_count_history") {return(shorts_hist_count);}
      if (get=="shorts_count_now")     {return(shorts_now_count);}
      if (get=="shorts_count_total")   {return(shorts_total_count);}
      //---
      if (get=="drawdown_equity_relative") {return(drawdown_rel);}
      if (get=="drawdown_equity_absolute") {return(drawdown_abs);}
      if (get=="drawdown_equity_maximal")  {return(drawdown_max);}
      //---
      if (get=="drawdown_balance_relative") {return(drawdown_balance_rel);}
      if (get=="drawdown_balance_absolute") {return(drawdown_balance_abs);}
      if (get=="drawdown_balance_maximal")  {return(drawdown_balance_max);}
      //---
      if (get=="consec_wins_max" || get=="consec_wins_maximum" || get=="consec_wins_maximal") {return(max_consec_wins);}
      if (get=="consec_wins_avg" || get=="consec_wins_average") {return(avg_consec_wins);}
      //---
      //---
      if (get=="consec_losses_max" || get=="consec_losses_maximum" || get=="consec_losses_maximal") {return(max_consec_loss);}
      if (get=="consec_losses_avg" || get=="consec_losses_average") {return(avg_consec_loss);}
   }
   return(-1);
}

string GetSymbol(string symbol="")
{
   static string memory="";
   if (symbol=="") {
      if (memory=="") {memory=Symbol();}
   }
   else {memory=symbol;}
   return(memory);
}

int IndicatorMoreShift(bool set=false, int shift=0)
{
	static int mem;
   if (set==true) {mem=shift;}
   else {
      int return_val=mem; mem=0; // reset
      return(return_val);
   }
   return(mem);
}

int LastIndicatorShift(bool set=false, int shift=0)
{
   static int mem;
   if (set==true) {mem=shift;}
   return(mem);
}

string LastIndicatorSymbol(bool set=false, string symbol="")
{
	static string mem;
   if (set==true) {mem=symbol;}
   return(mem);
}

int LastIndicatorTimeframe(bool set=false, int timeframe=0)
{
   static int mem;
   if (set==true) {mem=timeframe;}
   return(mem);
}

double LastIndicatorValue(bool set=false, double value=0)
{
   static double mem;
   if (set==true) {mem=value;}
   return(mem);
}

int OCODriver()
{
	static int last_known_ticket = 0;
   static int orders1[];
   static int orders2[];
   int i, size;
   
   int total = OrdersTotal();
   
   for (int pos=total-1; pos>=0; pos--)
   {
      if (OrderSelect(pos,SELECT_BY_POS,MODE_TRADES))
      {
         int ticket = OrderTicket();
         
         //-- end here if we reach the last known ticket
         if (ticket == last_known_ticket) {break;}
         
         //-- set the last known ticket, only if this is the first iteration
         if (pos == total-1) {
            last_known_ticket = ticket;
         }
         
         //-- we are searching for pending orders, skip trades
         if (OrderType() <= OP_SELL) {continue;}
         
         //--
         if (StringSubstr(OrderComment(), 0, 5) == "[oco:")
         {
            int ticket_oco = StrToInteger(StringSubstr(OrderComment(), 5, StringLen(OrderComment())-1)); 
            
            bool found = false;
            size = ArraySize(orders2);
            for (i=0; i<size; i++)
            {
               if (orders2[i] == ticket_oco) {
                  found = true;
                  break;
               }
            }
            
            if (found == false) {
               ArrayResize(orders1, size+1);
               ArrayResize(orders2, size+1);
               orders1[size] = ticket_oco;
               orders2[size] = ticket;
            }
         }
      }
   }
   
   size = ArraySize(orders1);
   int dbremove = false;
   for (i=0; i<size; i++)
   {
      if (OrderSelect(orders1[i], SELECT_BY_TICKET, MODE_TRADES) == false || OrderType() <= OP_SELL)
      {
         if (OrderSelect(orders2[i], SELECT_BY_TICKET, MODE_TRADES)) {
            if (DeleteOrder(orders2[i],clrWhite))
            {
               dbremove = true;
            }
         }
         else {
            dbremove = true;
         }
         
         if (dbremove == true)
         {
            ArrayStripKey(orders1, i);
            ArrayStripKey(orders2, i);
         }
      }
   }
   
   size = ArraySize(orders2);
   dbremove = false;
   for (i=0; i<size; i++)
   {
      if (OrderSelect(orders2[i], SELECT_BY_TICKET, MODE_TRADES) == false || OrderType() <= OP_SELL)
      {
         if (OrderSelect(orders1[i], SELECT_BY_TICKET, MODE_TRADES)) {
            if (DeleteOrder(orders1[i],clrWhite))
            {
               dbremove = true;
            }
         }
         else {
            dbremove = true;
         }
         
         if (dbremove == true)
         {
            ArrayStripKey(orders1, i);
            ArrayStripKey(orders2, i);
         }
      }
   }
   
   return true;
}

bool OnTimerSet(double seconds)
{
   if (FXD_ONTIMER_TAKEN)
   {
      if (seconds<=0) {
         FXD_ONTIMER_TAKEN_IN_MILLISECONDS = false;
         FXD_ONTIMER_TAKEN_TIME = 0;
      }
      else if (seconds < 1) {
         FXD_ONTIMER_TAKEN_IN_MILLISECONDS = true;
         FXD_ONTIMER_TAKEN_TIME = seconds*1000; 
      }
      else {
         FXD_ONTIMER_TAKEN_IN_MILLISECONDS = false;
         FXD_ONTIMER_TAKEN_TIME = seconds;
      }
      
      return true;
   }

   if (seconds<=0) {
      EventKillTimer();
   }
   else if (seconds < 1) {
      return (EventSetMillisecondTimer((int)(seconds*1000)));  
   }
   else {
      return (EventSetTimer((int)seconds));
   }
   
   return true;
}

void OnTradeListener()
{
   if (!ENABLE_EVENT_TRADE) {return;}

   int i=-1, j=-1, k=-1; int ti=-1; int ty=-1;
   int size=-1;
   static datetime start_time=-1;
  
   int pos=0;
   
   if (start_time==-1) {start_time=TimeCurrent();}

   string e_reason="";
   string e_detail="";
   
   ///////
   // TRADES AND ORDERS
   int tickets_now[]; ArrayResize(tickets_now,0);
   int tn=0;
   static int    memory_ti[];
   static int    memory_ty[];
   static double memory_sl[];
   static double memory_tp[];
   static double memory_vl[];
   static bool loaded=false;
   
   int total=OrdersTotal();
   
   // initial fill of the local DB
   if (loaded==false)
   {
      loaded=true;
      for (pos=total-1; pos>=0; pos--)
      {
         if (OrderSelect(pos,SELECT_BY_POS,MODE_TRADES))
         {
            ArrayResize(memory_ti,tn+1);
            ArrayResize(memory_ty,tn+1);
            ArrayResize(memory_sl,tn+1);
            ArrayResize(memory_tp,tn+1);
            ArrayResize(memory_vl,tn+1);
            memory_ti[tn]=OrderTicket();
            memory_ty[tn]=OrderType();
            memory_sl[tn]=attrStopLoss();
            memory_tp[tn]=attrTakeProfit();
            memory_vl[tn]=OrderLots();
            tn++;
         }
      }
      return;
   }
   tn=0;
   
   bool pending_opens=false;
   
   for (pos=total-1; pos>=0; pos--)
   {
      if (OrderSelect(pos,SELECT_BY_POS,MODE_TRADES))
      {
         ArrayResize(tickets_now,tn+1);
         tickets_now[tn]=OrderTicket();
         tn++;
         
         // Trades and Orders
         i=-1; ti=-1; ty=-1; size=ArraySize(memory_ti);
         
         if (size>0)
         {
           for (i=0; i<size; i++)
           {
              if (memory_ti[i]==OrderTicket())
              {
                 if (memory_ty[i]==OrderType()) {
                    ty=OrderType();
                  }
                  else {
                     pending_opens=true;
                  }
                  ti=OrderTicket(); break;
              }
           }
         }

         // Order become a trade
         if (ti>0 && ty<0)
         {
            memory_ti[i]=OrderTicket();
            memory_ty[i]=OrderType();
           
            memory_sl[i]=attrStopLoss();
            memory_tp[i]=attrTakeProfit();
            memory_vl[i]=OrderLots();
            e_reason="new";
            e_detail="";
            break;
         }

         // New trade/order opened
         else if (ti<0 && ty<0)
         {
            ArrayResize(memory_ti,size+1); memory_ti[size]=OrderTicket();
            ArrayResize(memory_ty,size+1); memory_ty[size]=OrderType();
            ArrayResize(memory_sl,size+1); memory_sl[size]=attrStopLoss();
            ArrayResize(memory_tp,size+1); memory_tp[size]=attrTakeProfit();
            ArrayResize(memory_vl,size+1); memory_vl[size]=OrderLots();
            e_reason="new";
            e_detail="";
            break;
         }
         
         // Check for Lots, SL or TP modification
         else if (ty>=0 && i>-1) {
            if (memory_vl[i]!=OrderLots())
            {
               memory_vl[i]=OrderLots();
               e_reason="modify";
               e_detail="lots";
               break;
            }
            else {
               if (memory_sl[i]!=attrStopLoss())   {memory_sl[i]=attrStopLoss(); e_reason="modify"; e_detail="sl"; break;}
               if (memory_tp[i]!=attrTakeProfit()) {memory_tp[i]=attrTakeProfit(); e_reason="modify"; if (e_detail=="sl") {e_detail="sltp";} else {e_detail="tp";} break;}
            }
         }
      }
   }
   
   // Check for closed orders/trades
   bool missing=true;
   if (e_reason=="" && pending_opens==false && ArraySize(tickets_now)<ArraySize(memory_ti))
   {
      for(i=ArraySize(memory_ti)-1; i>=0; i--) { // for each ticket in the memory...
         for(j=0; j<ArraySize(tickets_now); j++) { // check if trade exists now
            if (memory_ti[i]==tickets_now[j]) {missing=false; break;}
         }
         if (missing==true) {
            if (OrderSelect(memory_ti[i],SELECT_BY_TICKET))
            {
               // This can happen more than once
               ArrayStripKey(memory_ti,i);
               ArrayStripKey(memory_ty,i);
               ArrayStripKey(memory_sl,i);
               ArrayStripKey(memory_tp,i);
               ArrayStripKey(memory_vl,i);
               
               e_reason="closed";
               e_detail="";
               break;
            }
         }
         missing=true;
      }
   }
   // TRADES AND ORDERS
   ///////
   
   if (e_reason!="") {
      UpdateEventValues(e_reason,e_detail);
      EventTrade();
      OnTradeListener();
      if (USE_VIRTUAL_STOPS && e_reason=="closed") {
         ObjectDelete("#"+(string)OrderTicket()+" sl");
         ObjectDelete("#"+(string)OrderTicket()+" tp");
      }
      return;
   }
}

int OnTradeQueue(int queue=0)
{
   static int mem=0;
   mem=mem+queue;
   return(mem);
}

double PipValue(string symbol="")
{
   if (symbol=="") {symbol=GetSymbol();}
   return(CustomPoint(symbol)/MarketInfo(symbol,MODE_POINT));
   /*
   if (symbol=="") {symbol=GetSymbol();}
   int digits=MarketInfo(symbol,MODE_DIGITS);
   if ((digits==2 || digits==4)) {return(POINT_FORMAT/0.0001);}
   if ((digits==3 || digits==5)) {return(POINT_FORMAT/0.00001);}
   if ((digits==6))              {return(POINT_FORMAT/0.000001);}
   return(1);
   */
}

// Collect events, if any
void RegisterEvent(string command="")
{
   int ticket=OrderTicket();
	OnTradeListener();
   ticket=OrderSelect(ticket,SELECT_BY_TICKET);
   return;
}

void SetLastIndicatorData(double value=0, string symbol="", int timeframe=0, int shift=0)
{
   LastIndicatorValue(true,value);
   LastIndicatorSymbol(true,symbol);
   LastIndicatorTimeframe(true,timeframe);
   LastIndicatorShift(true,shift);
   IndicatorMoreShift(true,0); // reset
   return;
}

void StringExplode(string delimiter, string explode, string &sReturn[])
{
   static int ilBegin; ilBegin = -1;
   static int ilEnd; ilEnd = 0;
   static int ilElement; ilElement = 0;
   
   static string sDelimiter; sDelimiter = delimiter;
   static string sExplode; sExplode = explode;
   
   while (ilEnd != -1)
   {
      ilEnd = StringFind(sExplode, sDelimiter, ilBegin+1);
      ArrayResize(sReturn,ilElement+1);
      sReturn[ilElement] = "";     
      if (ilEnd == -1){
         if (ilBegin+1 != StringLen(sExplode)){
            sReturn[ilElement] = StringSubstr(sExplode, ilBegin+1, StringLen(sExplode));
         }
      } else { 
         if (ilBegin+1 != ilEnd){
            sReturn[ilElement] = StringSubstr(sExplode, ilBegin+1, ilEnd-ilBegin-1);
         }
      }      
      ilBegin = StringFind(sExplode, sDelimiter,ilEnd);  
      ilElement++;    
   }
}

void StringExplode(string delimiter, string explode, int &sReturn[])
{
   static int ilBegin; ilBegin = -1;
   static int ilEnd; ilEnd = 0;
   static int ilElement; ilElement = 0;
   
   static string sDelimiter; sDelimiter = delimiter;
   static string sExplode; sExplode = explode;

   while (ilEnd != -1)
   {
      ilEnd = StringFind(sExplode, sDelimiter, ilBegin+1);
      ArrayResize(sReturn,ilElement+1);
      sReturn[ilElement] = 0;     
      if (ilEnd == -1){
         if (ilBegin+1 != StringLen(sExplode)){
            sReturn[ilElement] = (int)StrToInteger(StringSubstr(sExplode, ilBegin+1, StringLen(sExplode)));
         }
      } else { 
         if (ilBegin+1 != ilEnd){
            sReturn[ilElement] = (int)StrToInteger(StringSubstr(sExplode, ilBegin+1, ilEnd-ilBegin-1));
         }
      }      
      ilBegin = StringFind(sExplode, sDelimiter,ilEnd);  
      ilElement++;    
   }
}

void StringExplode(string delimiter, string explode, double &sReturn[])
{
   static int ilBegin; ilBegin = -1;
   static int ilEnd; ilEnd = 0;
   static int ilElement; ilElement = 0;
   
   static string sDelimiter; sDelimiter = delimiter;
   static string sExplode; sExplode = explode;

   while (ilEnd != -1)
   {
      ilEnd = StringFind(sExplode, sDelimiter, ilBegin+1);
      ArrayResize(sReturn,ilElement+1);
      sReturn[ilElement] = 0;     
      if (ilEnd == -1){
         if (ilBegin+1 != StringLen(sExplode)){
            sReturn[ilElement] = (double)StrToDouble(StringSubstr(sExplode, ilBegin+1, StringLen(sExplode)));
         }
      } else { 
         if (ilBegin+1 != ilEnd){
            sReturn[ilElement] = (double)StrToDouble(StringSubstr(sExplode, ilBegin+1, ilEnd-ilBegin-1));
         }
      }      
      ilBegin = StringFind(sExplode, sDelimiter,ilEnd);  
      ilElement++;    
   }
}

double TicksData(string symbol="", int type=0, int shift=0)
{
   
   //return(MarketInfo(symbol,type));
   static bool collecting_ticks=false;
   //static string feeded_symbols[];
   static string symbols[];
   static int zero_sid[];
   static double memoryASK[][100];
   static double memoryBID[][100];
   int sid=0, size=0, i=0, id=0;
   double ask=0, bid=0, retval=0;
   bool exists=false;
   
   if (ArraySize(symbols)==0)
   {
      ArrayResize(symbols,1);
      ArrayResize(zero_sid,1);
      ArrayResize(memoryASK,1);
      ArrayResize(memoryBID,1);
      
      symbols[0] = _Symbol;
   }
	
   if (type>0 && shift>0) {collecting_ticks=true;}
   if (collecting_ticks==false) {
      if (type>0 && shift==0) {
         // going to get ticks
      } else {return(0);}
   }
	
	if (symbol=="") {symbol=_Symbol;}
   
	if (type==0)
	{
      //StringExplode(",",symbol,feeded_symbols);
	   //for (s=0; s<ArraySize(feeded_symbols); s++)
	   //{
	      //symbol=feeded_symbols[s];
         //if (symbol=="") {symbol=Symbol();}
	      exists=false;
         size=ArraySize(symbols);
	      for (i=0; i<size; i++) {
	         if (symbols[i]==symbol) {exists=true; sid=i; break;}
	      }
         if (exists==false) {
            int newsize=ArraySize(symbols)+1;
            ArrayResize(symbols,newsize); symbols[newsize-1]=symbol;
            ArrayResize(zero_sid,newsize);
            ArrayResize(memoryASK,newsize);
            ArrayResize(memoryBID,newsize);
            sid=newsize;
         }
         if (sid>=0) {
            ask=MarketInfo(symbol,MODE_ASK);
            bid=MarketInfo(symbol,MODE_BID);
            if (bid==0 && MQLInfoInteger(MQL_TESTER)) {
               Print("Ticks data collector error: "+symbol+" cannot be backtested. Only the current symbol can be backtested. The EA will be terminated.");
               ExpertRemove();
            }
            if (symbol==_Symbol || ask!=memoryASK[sid][0] || bid!=memoryBID[sid][0])
            {
               memoryASK[sid][zero_sid[sid]]=ask;
               memoryBID[sid][zero_sid[sid]]=bid;
               zero_sid[sid]=zero_sid[sid]+1;
               if (zero_sid[sid]==100) {zero_sid[sid]=0;}
	         }
   	   }
      //}
   }
   else {
      if (shift<=0) {
         if (type==MODE_ASK) {
            return(MarketInfo(symbol, MODE_ASK));
         }
         else if (type==MODE_BID) {
            return(MarketInfo(symbol, MODE_BID)); 
         }
         else {
            double mid=((MarketInfo(symbol, MODE_ASK)+MarketInfo(symbol, MODE_BID))/2);
            return(mid);
         }
      }
      else {
         size=ArraySize(symbols);
         for (i=0; i<size; i++) {
            if (symbols[i]==symbol) {sid=i;}
         }
         if (shift<100) {
            id=zero_sid[sid]-shift-1; if(id<0) {id=id+100;}
            
            if (type==MODE_ASK) {
               retval=(memoryASK[sid][id]);
               if (retval==0) {retval=MarketInfo(symbol,MODE_ASK);}
            }
            else if (type==MODE_BID) {
               retval=(memoryBID[sid][id]);
               if (retval==0) {retval=MarketInfo(symbol,MODE_BID);}
            }
            //Print(shift+" "+id+" "+retval);
         }
      }
   }
   return(retval);
}

datetime TimeAtStart(string cmd="server")
{
   static datetime local=0;
   static datetime server=0;
	
   if (cmd=="local") {return(local);}
   else if (cmd=="server") {return(server);}
   else if (cmd=="set") {
      local=TimeLocal();
      server=TimeCurrent();
   }
   return(0);
}

void UpdateEventValues(string e_reason="",string e_detail="")
{
   OnTradeQueue(1);
   e_Reason(true,e_reason);
   e_ReasonDetail(true,e_detail);
   e_attrClosePrice (true,attrClosePrice());
   e_attrComment    (true,attrComment());
   e_attrCommission (true,attrCommission());
   e_attrExpiration (true,attrExpiration());
   e_attrLots       (true,attrLots());
   e_attrMagicNumber(true,attrMagicNumber());
   e_attrOpenPrice  (true,attrOpenPrice());
   e_attrProfit     (true,attrProfit());
   e_attrStopLoss   (true,attrStopLoss());
   e_attrSymbol     (true,attrSymbol());
   e_attrTakeProfit (true,attrTakeProfit());
   e_attrTicket     (true,attrTicket());
   e_attrType       (true,attrType());
   e_attrOpenTime(true,attrOpenTime());
   e_attrCloseTime(true,attrCloseTime());
   e_attrSwap(true,attrSwap());
}

double VirtualStopsDriver(string _command="", int _ti=-1, double _sl=0, double _tp=0, double _slp=0, double _tpp=0)
{
   if (!USE_VIRTUAL_STOPS) {return(0);} // Virtual stops are not enabled => stop here
   
   static int mem_to_ti[]; // tickets
   static int mem_to[];    // timeouts
   static int last_checked_ticket=0;
   
   static string command;  command=_command;
   static int ti;          ti=_ti;
   static double sl;       sl=_sl;
   static double tp;       tp=_tp;
   static double slp;      slp=_slp;
   static double tpp;      tpp=_tpp;
   
   static int i; i=0;
   static int ii; ii=-1;
   static int size; size=0;
   static int error; error=0;
   static int pos;
   static int total;
   static string name;
   static double ask, bid;
   static string print;
   
   // Listen trades/orders
   if (command=="" || command=="listen")
   {
      //-- delete lines of virtual stops of manually closed trades ------
      total = OrdersHistoryTotal();
      if (total>0)
      {
         static int prev_ticket; prev_ticket=0;
         for (pos=total-1; pos>=0; pos--)
         {
            if (OrderSelect(pos,SELECT_BY_POS,MODE_HISTORY))
            {
               if (OrderTicket()==last_checked_ticket) {break;}
               prev_ticket=OrderTicket();
               static bool clear; clear=true;
               
               name = "#"+(string)OrderTicket()+" sl";
               if (ObjectFind(name)<0) {
                  error=GetLastError();
               }
               else {
                  clear=false;
                  ObjectDelete(name);
               }
               
               name = "#"+(string)OrderTicket()+" tp";
               if (ObjectFind(name)<0) {
                  clear=true;
                  error=GetLastError();
               }
               else {
                  clear=false;
                  ObjectDelete(name);
               }
            }
         }
      
         if (prev_ticket==0) {prev_ticket=OrderTicket();}
         last_checked_ticket = prev_ticket;
      }
      
      //-- parse trades -------------------------------------------------
      total = OrdersTotal();
      for (pos=0; pos<total; pos++)
      {
         if (OrderSelect(pos,SELECT_BY_POS))
         {
            static int ticket;
            static string symbol;
            static double lots;
            static double cp;
            ticket   = OrderTicket();
            symbol   = OrderSymbol();
            lots     = OrderLots();
            cp       = OrderClosePrice();
            
            // check SL and TP
            static double sl_lvl;
            static double tp_lvl;
            
            name = "#"+(string)ticket+" sl";
            sl_lvl = ObjectGet(name,OBJPROP_PRICE1);
            name = "#"+(string)ticket+" tp";
            tp_lvl = ObjectGet(name,OBJPROP_PRICE1);
            
            // close trade/order
            if (OrderType()==OP_BUY)
            {
               bid = MarketInfo(symbol,MODE_BID);
               if ((sl_lvl>0 && bid<=sl_lvl) || (tp_lvl>0 && bid>=tp_lvl))
               {
                  if (VIRTUAL_STOPS_TIMEOUT>0 && (sl_lvl>0 && bid<=sl_lvl))
                  {
                     i=ArraySearch(mem_to_ti, ticket);
                     if (i<0)
                     { // start timeout
                        size = ArraySize(mem_to_ti);
                        ArrayResize(mem_to_ti, size+1);
                        ArrayResize(mem_to, size+1);
                        mem_to_ti[size]   = ticket;
                        mem_to[size]      = (int)TimeLocal();
                        print = StringConcatenate("#",ticket," timeout of ",VIRTUAL_STOPS_TIMEOUT," seconds started");
                        Print(print);
                        return(0);
                     }
                     else {
                        if (TimeLocal()-mem_to[i] <= VIRTUAL_STOPS_TIMEOUT) {return(0);}
                     }
                  }
                  if (OrderClose(ticket, lots, cp, 0, clrNONE))
                  {
                     OnTradeListener(); // check this before deleting the lines
                     name = "#"+(string)OrderTicket()+" sl";
                     ObjectDelete(name);
                     name = "#"+(string)OrderTicket()+" tp";
                     ObjectDelete(name);
                  }
                  return(0);
               }
               else
               {
                  if (VIRTUAL_STOPS_TIMEOUT>0) {
                     i=ArraySearch(mem_to_ti,ticket);
                     if (i>=0) {
                        ArrayStripKey(mem_to_ti,i);
                        ArrayStripKey(mem_to,i);
                     }
                  }
               }
            }
            else if (OrderType()==OP_SELL)
            {
               ask = MarketInfo(symbol,MODE_ASK);
               if ((sl_lvl>0 && ask>=sl_lvl) || (tp_lvl>0 && ask<=tp_lvl))
               {
                  if (VIRTUAL_STOPS_TIMEOUT>0 && (sl_lvl>0 && ask>=sl_lvl))
                  {
                     i=ArraySearch(mem_to_ti, ticket);
                     if (i<0)
                     { // start timeout
                        size = ArraySize(mem_to_ti);
                        ArrayResize(mem_to_ti, size+1);
                        ArrayResize(mem_to, size+1);
                        mem_to_ti[size]   = ticket;
                        mem_to[size]      = (int)TimeLocal();
                        print = StringConcatenate("#",ticket," timeout of ",VIRTUAL_STOPS_TIMEOUT," seconds started");
                        Print(print);
                        return(0);
                     }
                     else {
                        if (TimeLocal()-mem_to[i] <= VIRTUAL_STOPS_TIMEOUT) {return(0);}
                     }
                  }
                  if (OrderClose(ticket, lots, cp, 0, clrNONE))
                  {
                     OnTradeListener(); // check this before deleting the lines
                     name = "#"+(string)OrderTicket()+" sl";
                     ObjectDelete(name);
                     name = "#"+(string)OrderTicket()+" tp";
                     ObjectDelete(name);
                  }
                  return(0);
               }
               else
               {
                  if (VIRTUAL_STOPS_TIMEOUT>0)
                  {
                     i=ArraySearch(mem_to_ti,ticket);
                     if (i>=0) {
                        ArrayStripKey(mem_to_ti,i);
                        ArrayStripKey(mem_to,i);
                     }
                  }
               }
            }
         }
      }
   }
   // Set SL and TP
   else if ((command=="set" || command=="modify" || command=="clear" || command=="partial") && ti>-1)
   {
      static string settext;
      // update record (add/modify)
      name = "#"+(string)ti+" sl";
      if (sl>0) {
         if (ObjectFind(name)==-1)
         {
            ObjectCreate(name,OBJ_HLINE,0,0,sl);
            ObjectSet(name,OBJPROP_WIDTH,1);
            ObjectSet(name,OBJPROP_COLOR,DeepPink);
            ObjectSet(name,OBJPROP_STYLE,STYLE_DOT);
            settext = name+" (virtual)";
            ObjectSetText(name, settext);
            error=GetLastError();
         }
         else {
            ObjectSet(name,OBJPROP_PRICE1,sl);
         }
      } else {ObjectDelete(name);}
      
      name="#"+(string)ti+" tp";
      if (tp>0)
      {
         if (ObjectFind(name)==-1) {
            ObjectCreate(name,OBJ_HLINE,0,0,tp);
            ObjectSet(name,OBJPROP_WIDTH,1);
            ObjectSet(name,OBJPROP_COLOR,DodgerBlue);
            ObjectSet(name,OBJPROP_STYLE,STYLE_DOT);
            settext = name+" (virtual)";
            ObjectSetText(name, settext);
            error=GetLastError();
         }
         else {
            ObjectSet(name, OBJPROP_PRICE1, tp);
         }
      }
      else {
         ObjectDelete(name);
      }
      
      // print message
      if (command=="set" || command=="modify") {
         print = command+" #"+(string)ti+": virtual sl "+DoubleToStr(sl,Digits)+" tp "+DoubleToStr(tp,Digits);
         Print(print);
      }
      return(1);
   }
   
   // Get SL or TP
   else if ((command=="get sl" || command=="get tp") && ti>0)
   {
      if (command=="get sl")
      {
         name = "#"+(string)ti+" sl";
         if (ObjectFind(name) == -1) {error=GetLastError();return(0);} 
         return(ObjectGet(name, OBJPROP_PRICE1));
      }
      else if (command=="get tp")
      {
         name = "#"+(string)ti+" tp";
         if (ObjectFind(name) == -1) {error=GetLastError();return(0);}
         return(ObjectGet(name, OBJPROP_PRICE1));
      }
      return(0);
   }
   
   return(1);
}

void WaitTradeContextIfBusy()
{
	if(IsTradeContextBusy()) {
      while(true)
      {
         Sleep(1);
         if(!IsTradeContextBusy()) {
            RefreshRates();
            break;
         }
      }
   }
   return;
}

int WindowFindVisible(long chart_id, string term)
{
   //-- the search term can be chart name, such as Force(13), or subwindow index
   if (term == "" || term == "0") {return 0;}
   
   int subwindow = (int)StringToInteger(term);
  
   if (subwindow == 0 && StringLen(term) > 1)
   {
      subwindow = ChartWindowFind(chart_id, term);
   }
   
   if (subwindow > 0 && !ChartGetInteger(chart_id, CHART_WINDOW_IS_VISIBLE, subwindow))
   {
      return -1;  
   }
   
   return subwindow;
}

double attrClosePrice(string sel="")
{
   return(OrderClosePrice());
}

datetime attrCloseTime(string sel="")
{
   return(OrderCloseTime());
}

string attrComment(string sel="")
{
   return(OrderComment());
}

double attrCommission(string sel="")
{
   if (sel=="e" || sel=="event") {return(e_attrCommission());}
   return(OrderCommission());
}

datetime attrExpiration(string sel="")
{
   return(OrderExpiration());
}

double attrLots(string sel="")
{
   return(OrderLots());
}

int attrMagicNumber(string sel="")
{
   return(OrderMagicNumber());
}

double attrOpenPrice(string sel="")
{
   return(OrderOpenPrice());
}

datetime attrOpenTime(string sel="")
{
   return(OrderOpenTime());
}

double attrProfit(string sel="")
{
   return(OrderProfit());
}

double attrStopLoss()
{
   if (USE_VIRTUAL_STOPS) {return(VirtualStopsDriver("get sl",OrderTicket()));}
   return(OrderStopLoss());
}

double attrSwap(string sel="")
{
   return(OrderSwap());
}

string attrSymbol(string sel="")
{
   return(OrderSymbol());
}

double attrTakeProfit()
{
   if (USE_VIRTUAL_STOPS) {return(VirtualStopsDriver("get tp",OrderTicket()));}
   return(OrderTakeProfit());
}

int attrTicket()
{
   return(OrderTicket());
}

int attrType()
{
   return(OrderType());
}

string e_Reason(bool set=false, string inp="") {
   static string mem[];
   int queue=OnTradeQueue()-1;
   if(set==true){
      ArrayResize(mem,queue+1);
      mem[queue]=inp;
   }
   return(mem[queue]);
}

string e_ReasonDetail(bool set=false, string inp="") {static string mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrClosePrice(bool set=false, double inp=-1) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

datetime e_attrCloseTime(bool set=false, datetime inp=-1) {static datetime mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

string e_attrComment(bool set=false, string inp="") {static string mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrCommission(bool set=false, double inp=0) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

datetime e_attrExpiration(bool set=false, datetime inp=0) {static datetime mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrLots(bool set=false, double inp=-1) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

int e_attrMagicNumber(bool set=false, int inp=-1) {static int mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrOpenPrice(bool set=false, double inp=-1) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

datetime e_attrOpenTime(bool set=false, datetime inp=-1) {static datetime mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrProfit(bool set=false, double inp=0) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrStopLoss(bool set=false, double inp=-1) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrSwap(bool set=false, double inp=0) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

string e_attrSymbol(bool set=false, string inp="") {static string mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

double e_attrTakeProfit(bool set=false, double inp=-1) {static double mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

int e_attrTicket(bool set=false, int inp=-1) {static int mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}

int e_attrType(bool set=false, int inp=-1) {static int mem[];int queue=OnTradeQueue()-1;if(set==true){ArrayResize(mem,queue+1);mem[queue]=inp;}return(mem[queue]);}




int CustomDigits(string symbol="")
{
    if (symbol == "") {symbol = GetSymbol();}
    double point = CustomPoint(symbol);
    if (point==0) {return(0);}
    int digits=0;

    while(true)
    {
        if (point>=1) {break;}
        point = point*10;
        digits++;
    }

    return(digits);
}

double CustomPoint(string symbol="")
{
    static string symbols[];
    static double points[];
    static string last_symbol = "-";
    static double last_point  = 0;
    static int last_i         = 0;
    static int size           = 0;

    //-- variant A) use the cache for the last used symbol
    if (symbol == "") {symbol = GetSymbol();}
    if (symbol == last_symbol)
    {
        return(last_point);
    }

    //-- variant B) search in the array cache
    int i			= last_i;
    int start_i	= i;
    bool found	= false;

    if (size>0)
    {
        while(true)
        {
            if (symbols[i] == symbol)
            {
                last_symbol	= symbol;
                last_point	= points[i];
                last_i		= i;

                return(last_point);
            }

            i++;
            if (i >= size)
            {
                i=0;
            }
            if (i == start_i) {break;}
        }
    }

    //-- variant C) add this symbol to the cache
    i		= size;
    size	= size+1;

    ArrayResize(symbols, size);
    ArrayResize(points, size);

    symbols[i]	= symbol;
    points[i]	= 0;
    last_symbol	= symbol;
    last_i		= i;

    //-- unserialize rules from FXD_POINT_FORMAT_RULES
    string rules[];
    StringExplode(",", POINT_FORMAT_RULES, rules);

    int rules_count = ArraySize(rules);

    if (rules_count > 0)
    {
        string rule[];
        for (int r=0; r < rules_count; r++)
        {
            StringExplode("=", rules[r], rule);

            //-- a single rule must contain 2 parts, [0] from and [1] to
            if (ArraySize(rule) != 2) {continue;}
            double from = StringToDouble(rule[0]);
            double to	= StringToDouble(rule[1]);

            //-- "to" must be a positive number, different than 0
            if (to <= 0) {continue;}

            //-- "from" can be a number or a string
            // a) string
            if (from == 0 && StringLen(rule[0]) > 0)
            {
                string s_from = rule[0];
                int pos = StringFind(s_from, "?");

                if (pos < 0) // ? not found
                {
                    if (StringFind(symbol, s_from) == 0) {points[i] = to;}
                }
                else if (pos == 0) // ? is the first symbol => match the second symbol
                {
                    if (StringFind(symbol, StringSubstr(s_from, 1), 3) == 3) {points[i] = to;}
                }
                else if (pos > 0) // ? is the second symbol => match the first symbol
                {
                    if (StringFind(symbol, StringSubstr(s_from, 0, pos)) == 0) {points[i] = to;}
                }
            }

            // b) number
            if (from == 0) {continue;}

            if (SymbolInfoDouble(symbol, SYMBOL_POINT) == from)
            {
                points[i] = to;
            }
        }
    }

    if (points[i] == 0) {points[i] = SymbolInfoDouble(symbol, SYMBOL_POINT);}
    last_point=points[i];

    return(last_point);
}


// Blocks Lookup Functions
string fxdBlocksLookupTable[9] = {"10","11","12","13","14","15","16","17","18"};

int fxdBlocksLookupTableTranslateID(string id)
{
    return ArraySearch(fxdBlocksLookupTable, id);
}
string fxdBlocksLookupTableTranslateID(int id)
{
    return fxdBlocksLookupTable[id];
}

int fxdBlockToggle(int id)
{
    disabled[id] = !disabled[id];
    return disabled[id];
}

void fxdBlockTurnOn(int id)
{
    disabled[id] = false;
}
void fxdBlockTurnOff(int id)
{
    disabled[id] = true;
}

bool fxdBlockRun(int id=0)
{

    switch(id)
    {

        case 0: block0(); break;


        case 1: block1(); break;


        case 2: block2(); break;


        case 3: block3(); break;


        case 4: block4(); break;


        case 5: block5(); break;


        case 6: block6(); break;


        case 7: block7(); break;


        case 8: block8(); break;


        default: return false;
    }


    return true;
}


void fxdBlockGetNextBlocks(int id, int &list[])
{
    switch(id)
    {
		case 1: ArrayResize(list, 1); list[0]=0; break;
		case 2: ArrayResize(list, 1); list[0]=1; break;
		case 3: ArrayResize(list, 1); list[0]=2; break;
		case 4: ArrayResize(list, 1); list[0]=3; break;
		case 5: ArrayResize(list, 1); list[0]=2; break;
		case 6: ArrayResize(list, 1); list[0]=5; break;
		case 7: ArrayResize(list, 1); list[0]=1; break;
		case 8: ArrayResize(list, 1); list[0]=0; break;

        default: ArrayResize(list, 0);
    }
}

void fxdBlockGetNextBlocks(string id, int &list[])
{
    fxdBlockGetNextBlocks(fxdBlocksLookupTableTranslateID(id), list);
}


class FxdWaiting
{
    private:
        int beginning_id;
        ushort bank  [][2][20]; // 2 banks, 20 possible parallel waiting blocks per chain of blocks
        ushort state [][2];     // second dimention values: 0 - count of the blocks put on hold, 1 - current bank id

    public:
        void Initialize(int count)
        {
            ArrayResize(bank, count);
            ArrayResize(state, count);
        }

        bool Run(int id = 0)
        {
            beginning_id = id;

            int range = ArrayRange(state, 0);
			if (range < id+1) {
				ArrayResize(bank, id+1);
				ArrayResize(state, id+1);

				// set values to 0, otherwise they have random values
				for (int ii=range; ii<id+1; ii++)
				{
				   state[ii][0] = 0;
				   state[ii][1] = 0;
				}
			}

            // are there blocks put on hold?
            int count = state[id][0];
            int bank_id = state[id][1];
            // if no block are put on hold -> escape
            if (count == 0) {return false;}
            else
            {
                state[id][0] = 0; // null the count
                state[id][1] = (bank_id) ? 0 : 1; // switch to the other bank
            }

            //== now we will run the blocks put on hold



            for (int i = 0; i < count; i++)
            {
                int block_to_run = bank[id][bank_id][i];
                //Print(count + " " + block_to_run);
                fxdBlockRun(block_to_run);
            }

            //state[id][0] = 0;


            return true;
        }

        void Accumulate(int block_id = 0)
        {
            int count   = ++state[beginning_id][0];
            int bank_id = state[beginning_id][1];

            bank[beginning_id][bank_id][count-1] = (ushort)block_id;
        }
};
FxdWaiting fxdWait;



//+------------------------------------------------------------------+
//| END                                                              |
//| Created with fxDreema EA Builder           https://fxdreema.com/ |
//+------------------------------------------------------------------+

/*<fxdreema:eNrtW+tv2zYQ/1cKFxhaDCn0lqy0A5rMxgrk0SVut+6LQMu0y0YWDYnOY0X+991RpB6unbiJnHSN/SGR+bg7HX939xNFk9APv+ahaYadmKcpjQXjad7ZJWEQfmWhARc2DnDDzpjRZNTZzWFG5/de/+2HgwF+88JOzudZTPGLFXZMQ7UKkk2o0K1mZ/eaheYd5JlL5VlSnnUHedZSebaUZ99Bnr1UniPlOa3Z50p57h3kuUvleVKe19p6+FKe3xpeApB3LduHCY/PJCS7CEkTMWmZqCSQoBUZT2S3aWBjN+x8JBkjw4SaSvt5GLI0zpSejySZYxdIwfGgjEkzMQbOsS+Sf7FpQYGl7uyj7ocZ0nrQ3Nk/Pjnp7Q/eHR9hCzQUN1AzRy5sV5pDRHZILusGWY9gkN00iKV1g+xWDPJwgfGzvlVO2VEZ47TlHXN9O9wldrgPY0dhCMogLKUSuE7YGbD4TIkfsRxNBAOGkKMlyOmloFmRuiEOrmsWlkEGMlkexfNc8KmeGegAK0IPo/MqF3SqFE35iCaRulO4h0M+YuMr7aJcNoPyGcnIlIIBIJ7zbJQ3fTdjUFpKY0FMHpNE4yPl2ZQk2h85RVmCZ/JOXBnxpcsw4VfXdu3aKcGPSVJfFwkkF0TMGyvBUz4e41Cj8OyICFK5Xw4RTBQWuuVdPytv+/Uw+w0zyhsDr4pgrq5Z+qbCPOgHXdK7Sj/oS+hYyLsJPBhigzY+w+++gdphwAVhora2fC5mc5HXRTBo0yPgKy2/XkuBcS5Hy1xsYhIl8dkk4/N0tBPzhGcqMz534eN5nWJVGj1D+dEIHwMWowvKJp9BzREqMZ0K5RGALyMV9DCnVBDRrSYAcQjgoNlOLq7Qu0eSE5grEzq2gfH7V7HGGoiw4zBMOD/bIxgO7YeK2UKogAkHHNd0s+FRWH9viNt2Ye6zFzOS5886aeeZYFOav0REV66+Dcx+E8ym+/1oNu+H5qC/tx/0F9EM8H1ex/GOxnHljTbArKXBnVvfR1HKEnxEL24jKXBjLB2xWGIgYm8HJ0sqjqu0QO+MZowXUx2kpbFU9L5sBV2nnw73jg9qkISZg3eHvf7J28NenVWA7tM/3vUHSqNR41XfwYAWSqr1CKXdXmKH/Qh2bKnOBqiO9XNRHaNGdYzNUh3LW051GpnphgJgNAuA5fhPkc7YN9IZkLXPIYHjPotG2ovCwYpIvtwEqbFbCArLqNlOklPAGp3SVPxPWI5Vd30F618mYrfxOL4a4KZjNwHe9R+a4fT7e45nLAJcOXkJwam5tn2a49yR5ixsfbS0FwNmmQ0ateUkW07yg3ASZ8tJ7vFsuoKT4KbLmtTEdIxG5rad7lOkJu5dqcnrYl9rI9TE3VKTVdQkKahJuTF/0+aLsUBNvCdNTby7UpPmS5AtNdlSk5+cmnhbarIBasLSdamJ5TV3TWz3Sb4E8n+ct/pBka1Xb22D8F/NbbbeZuuHz9b+Nlu3vLmNYf6reWuatt3Fze0n+QQZrErTlj72NSgPTuCbyjEPNbaOh1/2P5NMnM6Hf7F0xC+abxyxm2cqSnA8RNRR7yQ66PUH0Yf373snKnxg4N8q+tyq6ZOKEEumHXQjNEpb+mDlKfu3eV4P/QQDFvv0kY0DMqSJLjTFVkO40ecCtSV6Q0bTVlk1q1gabqDa1KwqnobWsGrNogPrLAB/Ef5ZZQtmSNWtHx5v179msdmY/jWLzMb0e0v0ew+o31+i339A/cES/cHG9bdf34MW6rvcTJri9lfvctOFvXu/A3p4Arm69mvXQe262yoR8Ev33Fr0nYWibz78Ab0xfFzvkY80XUuMgZMESyfFU1lXG05iwc5ptCoIUOgs419oLKIUICjb0KNzgBAjSZToc3K1geJqpsFHL2c0E3pWDDCPEpJO5mRClabDPw8c7QtYdzAlo/BPRhR0W4bp7xj2juEPIBBcP7SCV77v/NOYMpXEcN0pUzJhcQQo
zCRKvMDARbaw1LKcgVOjhA0zkl1FuKKzb+LV7FYjZVCvGod+YlOaRTkF/2LQgjZDWTHjDHheNldraOvfa2gAjjM+VbgxXhmGqVKM4Mpv0Fj9KGNxkl9MWpzmqWaz/PXF4sRAT7xh5nWRk+Y5sBKWiTmgAFLdLFcTUjmhcGetN0JXyCgqwhy9iCIoOGhC0/hqiRAcs9AfZTRpHJVfMoSMRo0hcEHPIWHkCBSNO6ER3i17YzyoGukjdyoxwp1OMjL7HAHeYRQpX6+A1AGfDfgeFzLRl9UEkJXWkgm4tDqESUB1vaI1OouaA7lPB5CNHFyUUKd5nLGZNkClf1cxxBoHLiypnRNc1NrsbUWtUz64N1KTo84A6920RUtqXd+YAX0jPh8WmWw9S+plRtPy5SoVY29fJXLuFSoLOt6iSqxTcptrUWHZce+1VbqKIkwgNdcqDu7iASWRkzFarv8DpQ+G6Q==
:fxdreema>*/