//+------------------------------------------------------------------------------+//
//)   ____  _  _  ____  ____  ____  ____  __  __    __      ___  _____  __  __   (//
//)  ( ___)( \/ )(  _ \(  _ \( ___)( ___)(  \/  )  /__\    / __)(  _  )(  \/  )  (//
//)   )__)  )  (  )(_) ))   / )__)  )__)  )    (  /(__)\  ( (__  )(_)(  )    (   (//
//)  (__)  (_/\_)(____/(_)\_)(____)(____)(_/\/\_)(__)(__)()\___)(_____)(_/\/\_)  (//
//)   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-9855"
//--
// Point Format Rules
#define POINT_FORMAT_RULES "0.001=0.01,0.00001=0.0001,0.000001=0.0001" // this is deserialized in a special function later
#define ENABLE_SPREAD_METER true
#define ENABLE_STATUS true
//--
// 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 double big_grid = 20;input double small_grid = 7;input double volume = 0.01;input double big_SL = 27;input double big_TP = 27;input double small_TP = 10;input int MagicStart = 9855; // Magic Number, kind of...
class c
{
		public:
	static double big_grid;
	static double small_grid;
	static double volume;
	static double big_SL;
	static double big_TP;
	static double small_TP;
	static int MagicStart;
};
double c::big_grid;
double c::small_grid;
double c::volume;
double c::big_SL;
double c::big_TP;
double c::small_TP;
int c::MagicStart;


//--
// Variables (Global Variables)
class v
{
		public:
};



//--
// Externs (Global Variables)
input string inp1_ListOfSymbols = "EURUSD";
input string inp2_ListOfSymbols = "USDCHF";
input string inp3_ListOfSymbols = "EURCHF";
class _externs
{
		public:
	static string inp1_ListOfSymbols;
	static string inp2_ListOfSymbols;
	static string inp3_ListOfSymbols;
};
string _externs::inp1_ListOfSymbols = inp1_ListOfSymbols;
string _externs::inp2_ListOfSymbols = inp2_ListOfSymbols;
string _externs::inp3_ListOfSymbols = inp3_ListOfSymbols;



//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        = 24;
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;

/************************************************************************************************************************/
// +------------------------------------------------------------------------------------------------------------------+ //
// |                                                 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::big_grid = big_grid;
	c::small_grid = small_grid;
	c::volume = volume;
	c::big_SL = big_SL;
	c::big_TP = big_TP;
	c::small_TP = small_TP;
	c::MagicStart = MagicStart;




	// Initiate Externs
	_externs::inp1_ListOfSymbols = inp1_ListOfSymbols;
	_externs::inp2_ListOfSymbols = inp2_ListOfSymbols;
	_externs::inp3_ListOfSymbols = inp3_ListOfSymbols;



	// 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();





	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 (MQLInfoInteger(MQL_OPTIMIZATION)) {
		USE_VIRTUAL_STOPS = false;
	}

	//-- set initial local and server time
	TimeAtStart("set");

	//-- set initial balance
	AccountBalanceAtStart();

	//-- draw the initial spread info meter
	if (ENABLE_SPREAD_METER == false) {
		FXD_DRAW_SPREAD_INFO = false;
	}
	else {
		FXD_DRAW_SPREAD_INFO = !(MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_VISUAL_MODE));
	}
	if (FXD_DRAW_SPREAD_INFO) DrawSpreadInfo();

	//-- draw initial status
	if (ENABLE_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);
		}
	}


	//-- Initialize blocks classes
	ArrayResize(_blocks_, 24);

	_blocks_[0] = new Block0();
	_blocks_[1] = new Block1();
	_blocks_[2] = new Block2();
	_blocks_[3] = new Block3();
	_blocks_[4] = new Block4();
	_blocks_[5] = new Block5();
	_blocks_[6] = new Block6();
	_blocks_[7] = new Block7();
	_blocks_[8] = new Block8();
	_blocks_[9] = new Block9();
	_blocks_[10] = new Block10();
	_blocks_[11] = new Block11();
	_blocks_[12] = new Block12();
	_blocks_[13] = new Block13();
	_blocks_[14] = new Block14();
	_blocks_[15] = new Block15();
	_blocks_[16] = new Block16();
	_blocks_[17] = new Block17();
	_blocks_[18] = new Block18();
	_blocks_[19] = new Block19();
	_blocks_[20] = new Block20();
	_blocks_[21] = new Block21();
	_blocks_[22] = new Block22();
	_blocks_[23] = new Block23();

	// fill the lookup table
	ArrayResize(fxdBlocksLookupTable, ArraySize(_blocks_));
	for (int i=0; i<ArraySize(_blocks_); i++)
	{
		fxdBlocksLookupTable[i] = _blocks_[i].__block_user_number;
	}

	// fill the list of inbound blocks for each BlockCalls instance
	for (int i=0; i<ArraySize(_blocks_); i++)
	{
		_blocks_[i].__announceThisBlock();
	}

	// List of initially disabled blocks
	int disabled_blocks_list[] = {};
	for (int l = 0; l < ArraySize(disabled_blocks_list); l++) {
		_blocks_[disabled_blocks_list[l]].__disabled = true;
	}



	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 (ENABLE_STATUS && FXD_TICKS_FROM_START == 1) DrawStatus("working");

	//-- special system actions
	if (FXD_DRAW_SPREAD_INFO) DrawSpreadInfo();
	TicksData(""); // Collect ticks (if needed)
	TicksPerSecond(false, true); // Collect ticks per second

	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;}

	//-- run blocks
	int blocks_to_run[] = {0,1,2};
	for (int i=0; i<ArraySize(blocks_to_run); i++) {
		_blocks_[blocks_to_run[i]].run();
	}


	return;
}



//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//
// This function is executed on trade events - open, close, modify //
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^//
void EventTrade()
{


	OnTradeQueue(-1);
}

//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();

	if (ENABLE_STATUS) DrawStatus("stopped");
	if (ENABLE_SPREAD_METER) 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;
		}
	}

	// delete dynamic pointers
	for (int i=0; i<ArraySize(_blocks_); i++)
	{
		delete _blocks_[i];
		_blocks_[i] = NULL;
	}
	ArrayResize(_blocks_, 0);

	return;
}

/************************************************************************************************************************/
// +------------------------------------------------------------------------------------------------------------------+ //
// |	                                         Classes of blocks                                                    | //
// |              Classes that contain the actual code of the blocks and their input parameters as well               | //
// +------------------------------------------------------------------------------------------------------------------+ //
/************************************************************************************************************************/

/**
	The base class for all block calls
   */
class BlockCalls
{
	public:
		bool __disabled; // whether or not the block is disabled

		string __block_user_number;
        int __block_number;
		int __block_waiting;
		int __parent_number;
		int __inbound_blocks[];
		int __outbound_blocks[];
		void __addInboundBlock(int id = 0) {
			int size = ArraySize(__inbound_blocks);
			for (int i = 0; i < size; i++) {
				if (__inbound_blocks[i] == id) {
					return;
				}
			}
			ArrayResize(__inbound_blocks, size + 1);
			__inbound_blocks[size] = id;
		}

		/**
		   Announce this block to the list of inbound connections of all the blocks to which this block is connected to
		   */
		void __announceThisBlock()
		{
		   // add the current block number to the list of inbound blocks
		   // for each outbound block that is provided
			for (int i = 0; i < ArraySize(__outbound_blocks); i++)
			{
				int block = __outbound_blocks[i]; // outbound block number
				int size  = ArraySize(_blocks_[block].__inbound_blocks); // the size of its inbound list

				// skip if the current block was already added
				for (int j = 0; j < size; j++) {
					if (_blocks_[block].__inbound_blocks[j] == __block_number)
					{
						return;
					}
				}

				// add the current block number to the list of inbound blocks of the other block
				ArrayResize(_blocks_[block].__inbound_blocks, size + 1);
				_blocks_[block].__inbound_blocks[size] = __block_number;
			}
		}

		// this is here, because it is used in the "run" function
		virtual void _execute_() = 0;

		/**
			In the derived class this method should be used to set dynamic parameters or other stuff before the main execute.
			This method is automatically called within the main "run" method below, before the execution of the main class.
			*/
		virtual void _beforeExecute_() {return;};
		bool _beforeExecuteEnabled; // for speed

		/**
			Same as _beforeExecute_, but to work after the execute method.
			*/
		virtual void _afterExecute_() {return;};
		bool _afterExecuteEnabled; // for speed

		/**
			This is the method that is used to run the block
			*/
		virtual void run(int _parent_=0) {
			__parent_number = _parent_;
			if (__disabled || FXD_BREAK) {return;}
			FXD_CURRENT_FUNCTION_ID = __block_number;

			if (_beforeExecuteEnabled) {_beforeExecute_();}
			_execute_();
			if (_afterExecuteEnabled) {_afterExecute_();}

			if (__block_waiting && FXD_CURRENT_FUNCTION_ID == __block_number) {fxdWait.Accumulate(FXD_CURRENT_FUNCTION_ID);}
		}
};

BlockCalls *_blocks_[];


// "Set "Current Market" for next blocks" model
template<typename T1>
class MDL_SetCurrentSymbol2: public BlockCalls
{
	public: /* Input Parameters */
	T1 ListOfSymbols;
	/* Static Parameters */
	string symbols0;
	string symbols[];
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDL_SetCurrentSymbol2()
	{
		ListOfSymbols = "EURUSD,GBPUSD,AUDUSD";
		/* Static Parameters (initial value) */
		symbols0 = "";
	}

	public: /* The main method */
	virtual void _execute_()
	{
		int i,s,size;
		
		//-- explode and correct symbols list, then check for existence
		if (ListOfSymbols != symbols0)
		{
		   string symbols_tmp[];
		   
		   //- explode
		   symbols0 = ListOfSymbols;
		   StringExplode(",", ListOfSymbols, symbols_tmp);
		   
		   //- trim
		   size = ArraySize(symbols_tmp);
		   for (i=0; i<size; i++) {
		      symbols_tmp[i]=StringTrim(symbols_tmp[i]);
		   }
		   
		   //- check for existence
		   string symbols_all[];
		   SymbolsList(symbols_all, false);
		
		   s=0;
		   ArrayResize(symbols, size);
		   for (i=0; i<size; i++)
		   {
		      //- exclude non-existing symbol
		      if (ArraySearch(symbols_all, symbols_tmp[i])==-1)
		      {
		         Alert("Symbol "+symbols_tmp[i]+" does not exists and will be excluded from the list in block #" + __block_user_number);
		         continue;
		      }
		      symbols[s]=symbols_tmp[i];
		      s++;
		   }
		   ArrayResize(symbols, s);
		}
		
		// Create a loop
		size=ArraySize(symbols);
		for (i=0; i<size; i++)
		{
		   CurrentSymbol(symbols[i]);
		   _callback_(1);
		}
		
		CurrentSymbol(Symbol()); // Reset the current symbol
		_callback_(0);
	}
};

// "Round numbers detector" model
template<typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7>
class MDL_RoundNumbersDetector: public BlockCalls
{
	public: /* Input Parameters */
	T1 ModeDistance;
	T2 Market;
	T3 RoundNumbersDistance;
	T4 RoundNumbersPips;
	T5 RoundNumbersOffset;
	T6 RoundNumbersOffsetPips;
	T7 ResetPercentage;
	/* Static Parameters */
	double up;
	double dn;
	int latest_direction;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDL_RoundNumbersDetector()
	{
		ModeDistance = "fraction";
		Market = CurrentSymbol();
		RoundNumbersDistance = 0.0100;
		RoundNumbersPips = 100;
		RoundNumbersOffset = 0;
		RoundNumbersOffsetPips = 0;
		ResetPercentage = 0;
		/* Static Parameters (initial value) */
		up =  0;
		dn =  0;
		latest_direction =  0;
	}

	public: /* The main method */
	virtual void _execute_()
	{
		// similar to "next", 1 = up, 2 = dn
		
		int next        = 0; // 0 = no pass, 1 = normal output, 2 = second output
		double gridsize = 0;
		double offset   = 0;
		double price    = iClose(Market, 0, 0);
		
		if (ModeDistance == "pips")
		{
			gridsize = toDigits(MathAbs(RoundNumbersPips), Market);
			offset   = toDigits(RoundNumbersOffsetPips, Market);
		}
		else if (ModeDistance == "fraction") {
			gridsize = MathAbs(RoundNumbersDistance);
			offset   = RoundNumbersOffset;
		}
		
		if (ResetPercentage > 0)
		{
			if (
				(latest_direction == 1 && (price >= ((up - gridsize) + (gridsize*(ResetPercentage/100))) || price <= ((up - gridsize) - (gridsize*(ResetPercentage/100))) ))
				||
				(latest_direction == 2 && (price <= ((dn + gridsize) - (gridsize*(ResetPercentage/100))) || price >= ((dn + gridsize) + (gridsize*(ResetPercentage/100))) ))
				)
			{
				up = 0;
				latest_direction = 0;
			}
		}
		
		// Initial calculation
		if ((up == 0) && gridsize != 0)
		{
			int digits = (int)SymbolInfoInteger(Market, SYMBOL_DIGITS);
			
			up = MathCeil(price/gridsize) * gridsize;
			up = up + offset;
			up = NormalizeDouble(up, digits);
			
			dn = MathFloor(price/gridsize) * gridsize;
			dn = dn + offset;
			dn = NormalizeDouble(dn, digits);
		}
		
		if (price >= up)
		{
			dn = up - gridsize;
			up += gridsize;
			
			next = 1;
			latest_direction = 1;
		}
		else if (price <= dn)
		{
			up = dn + gridsize;
			dn -= gridsize;
			
			next = 2;
			latest_direction = 2;
		}
		
		     if (next == 1) {_callback_(1);}
		else if (next == 2) {_callback_(0);}
	}
};

// "No trade nearby" model
template<typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename _T7_,typename T8,typename T9,typename T10,typename T11>
class MDL_NoNearbyRunning: public BlockCalls
{
	public: /* Input Parameters */
	T1 OrdersGroup;
	T2 OrdersScope;
	T3 SymbolScope;
	T4 SYMBOL;
	T5 BuysOrSells;
	T6 ModeBasePrice;
	T7 BasePrice; virtual _T7_ _BasePrice_(){return(_T7_)0;}
	T8 ModeRange;
	T9 RangePips;
	T10 RangeFraction;
	T11 RangePosition;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDL_NoNearbyRunning()
	{
		OrdersGroup = "";
		OrdersScope = "group";
		SymbolScope = "symbol";
		SYMBOL = CurrentSymbol();
		BuysOrSells = "both";
		ModeBasePrice = "current";
		ModeRange = "pips";
		RangePips = 10;
		RangeFraction = 0.0010;
		RangePosition = 0;
	}

	public: /* The main method */
	virtual void _execute_()
	{
		double price = 0;
		bool use_current_price = (ModeBasePrice == "current");
		if (!use_current_price) {price = _BasePrice_();}
		
		int next = true;
		int total = OrdersTotal();
		
		for (int pos=total-1; pos>=0; pos--)
		{
		   if (!OrderSelect(pos,SELECT_BY_POS,MODE_TRADES)) {continue;}
		   if (!FilterOrderBy(OrdersScope,(string)OrdersGroup, SymbolScope,SYMBOL, BuysOrSells)) {continue;}
			
			double distance = RangeFraction;
			
			if (ModeRange == "pips") {distance = toDigits(RangePips, SYMBOL);}
		   
			if (OrderType() == OP_BUY)
			{
				if (use_current_price) {price = SymbolAsk(SYMBOL);}
				switch (RangePosition)
				{
					case 0: if (price <= (OrderOpenPrice() + distance/2) && price >= (OrderOpenPrice() - distance/2)) {next = false;} break;
					case 1: if (price <= OrderOpenPrice() + distance && price >= OrderOpenPrice()) {next = false;} break;
					case 2: if (price <= OrderOpenPrice() && price >= OrderOpenPrice() - distance) {next = false;} break;
				}
			}
			else
			{
				if (use_current_price) {price = SymbolBid(SYMBOL);}
				switch (RangePosition)
				{
					case 0: if (price <= (OrderOpenPrice() + distance/2) && price >= (OrderOpenPrice() - distance/2)) {next = false;} break;
					case 1: if (price <= OrderOpenPrice() && price >= OrderOpenPrice() - distance) {next = false;} break;
					case 2: if (price <= OrderOpenPrice() + distance && price >= OrderOpenPrice()) {next = false;} break;
				}
			}
			
			if (next == false) {break;}
		}
		
		if (next == true) {_callback_(1);} else {_callback_(0);}
	}
};

// "Once per bar" model
template<typename T1,typename T2,typename T3>
class MDL_OncePerBar: public BlockCalls
{
	public: /* Input Parameters */
	T1 SYMBOL;
	T2 TIMEFRAME;
	T3 PassMaxTimes;
	/* Static Parameters */
	datetime old_value;
	int pass_count;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDL_OncePerBar()
	{
		SYMBOL = CurrentSymbol();
		TIMEFRAME = CurrentTimeframe();
		PassMaxTimes = 1;
		/* Static Parameters (initial value) */
		pass_count =  0;
	}

	public: /* The main method */
	virtual void _execute_()
	{
		bool next = false;
		
		
		
		
		datetime new_value = iTime(SYMBOL, TIMEFRAME, 1);
		
		if (new_value > old_value)
		{
			pass_count++;
			if (pass_count >= PassMaxTimes)
			{
				old_value = new_value;
				pass_count = 0;
			}
			
			next = true;
		}
		
		if (next) {_callback_(1);} else {_callback_(0);}
	}
};

// "Buy now" model
template<typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename T8,typename T9,typename _T9_,typename T10,typename T11,typename T12,typename T13,typename T14,typename T15,typename T16,typename T17,typename T18,typename T19,typename T20,typename T21,typename T22,typename T23,typename T24,typename T25,typename T26,typename T27,typename T28,typename T29,typename T30,typename T31,typename T32,typename T33,typename T34,typename T35,typename _T35_,typename T36,typename _T36_,typename T37,typename _T37_,typename T38,typename T39,typename T40,typename T41,typename _T41_,typename T42,typename _T42_,typename T43,typename _T43_,typename T44,typename T45,typename T46,typename T47,typename T48,typename _T48_,typename T49,typename T50,typename T51>
class MDL_BuyNow: public BlockCalls
{
	public: /* Input Parameters */
	T1 OrdersGroup;
	T2 SYMBOL;
	T3 VolumeMode;
	T4 VolumeSize;
	T5 VolumeSizeRisk;
	T6 VolumeRisk;
	T7 VolumePercent;
	T8 VolumeBlockPercent;
	T9 dVolumeSize; virtual _T9_ _dVolumeSize_(){return(_T9_)0;}
	T10 FixedRatioUnitSize;
	T11 FixedRatioDelta;
	T12 mmMgInitialLots;
	T13 mmMgMultiplyOnLoss;
	T14 mmMgMultiplyOnProfit;
	T15 mmMgAddLotsOnLoss;
	T16 mmMgAddLotsOnProfit;
	T17 mmMgResetOnLoss;
	T18 mmMgResetOnProfit;
	T19 mm1326InitialLots;
	T20 mm1326Reverse;
	T21 mmFiboInitialLots;
	T22 mmDalembertInitialLots;
	T23 mmDalembertReverse;
	T24 mmLabouchereInitialLots;
	T25 mmLabouchereList;
	T26 mmLabouchereReverse;
	T27 mmSeqBaseLots;
	T28 mmSeqOnLoss;
	T29 mmSeqOnProfit;
	T30 mmSeqReverse;
	T31 VolumeUpperLimit;
	T32 StopLossMode;
	T33 StopLossPips;
	T34 StopLossPercentTP;
	T35 dlStopLoss; virtual _T35_ _dlStopLoss_(){return(_T35_)0;}
	T36 dpStopLoss; virtual _T36_ _dpStopLoss_(){return(_T36_)0;}
	T37 ddStopLoss; virtual _T37_ _ddStopLoss_(){return(_T37_)0;}
	T38 TakeProfitMode;
	T39 TakeProfitPips;
	T40 TakeProfitPercentSL;
	T41 dlTakeProfit; virtual _T41_ _dlTakeProfit_(){return(_T41_)0;}
	T42 dpTakeProfit; virtual _T42_ _dpTakeProfit_(){return(_T42_)0;}
	T43 ddTakeProfit; virtual _T43_ _ddTakeProfit_(){return(_T43_)0;}
	T44 ExpMode;
	T45 ExpDays;
	T46 ExpHours;
	T47 ExpMinutes;
	T48 dExp; virtual _T48_ _dExp_(){return(_T48_)0;}
	T49 Slippage;
	T50 MyComment;
	T51 ArrowColorBuy;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDL_BuyNow()
	{
		OrdersGroup = "";
		SYMBOL = CurrentSymbol();
		VolumeMode = "fixed";
		VolumeSize = 0.1;
		VolumeSizeRisk = 50;
		VolumeRisk = 2.5;
		VolumePercent = 100;
		VolumeBlockPercent = 3;
		FixedRatioUnitSize = 0.01;
		FixedRatioDelta = 20;
		mmMgInitialLots = 0.1;
		mmMgMultiplyOnLoss = 2;
		mmMgMultiplyOnProfit = 1;
		mmMgAddLotsOnLoss = 0;
		mmMgAddLotsOnProfit = 0;
		mmMgResetOnLoss = 0;
		mmMgResetOnProfit = 1;
		mm1326InitialLots = 0.1;
		mm1326Reverse = false;
		mmFiboInitialLots = 0.1;
		mmDalembertInitialLots = 0.1;
		mmDalembertReverse = false;
		mmLabouchereInitialLots = 0.1;
		mmLabouchereList = "1,2,3,4,5,6";
		mmLabouchereReverse = false;
		mmSeqBaseLots = 0.1;
		mmSeqOnLoss = "3,2,6";
		mmSeqOnProfit = "1";
		mmSeqReverse = false;
		VolumeUpperLimit = 0;
		StopLossMode = "fixed";
		StopLossPips = 50;
		StopLossPercentTP = 100;
		TakeProfitMode = "fixed";
		TakeProfitPips = 50;
		TakeProfitPercentSL = 100;
		ExpMode = "GTC";
		ExpDays = 0;
		ExpHours = 1;
		ExpMinutes = 0;
		Slippage = 4;
		MyComment = "Long trade";
		ArrowColorBuy = Blue;
	}

	public: /* The main method */
	virtual void _execute_()
	{
		SetSymbol(SYMBOL);
		
		//-- stops ------------------------------------------------------------------
		double sll=0, slp=0, tpl=0, tpp=0;
		
		     if (StopLossMode=="fixed")        {slp=StopLossPips;}
		else if (StopLossMode=="dynamicPips")  {slp=_dpStopLoss_();}
		else if (StopLossMode=="dynamicDigits"){slp=toPips(_ddStopLoss_(),SYMBOL);}
		else if (StopLossMode=="dynamicLevel") {sll=_dlStopLoss_();}
		
		     if (TakeProfitMode=="fixed")         {tpp=TakeProfitPips;}
		else if (TakeProfitMode=="dynamicPips")   {tpp=_dpTakeProfit_();}
		else if (TakeProfitMode=="dynamicDigits") {tpp=toPips(_ddTakeProfit_(),SYMBOL);}
		else if (TakeProfitMode=="dynamicLevel")  {tpl=_dlTakeProfit_();}
		
		if (StopLossMode == "percentTP") {
		   if (tpp > 0) {slp = tpp*StopLossPercentTP/100;}
		   if (tpl > 0) {slp = toPips(MathAbs(SymbolAsk(SYMBOL) - tpl), SYMBOL)*StopLossPercentTP/100;}
		}
		if (TakeProfitMode == "percentSL") {
		   if (slp > 0) {tpp = slp*TakeProfitPercentSL/100;}
		   if (sll > 0) {tpp = toPips(MathAbs(SymbolAsk(SYMBOL) - sll), SYMBOL)*TakeProfitPercentSL/100;}
		}
		
		//-- lots -------------------------------------------------------------------
		double lots=0;
		double pre_sll=sll; if (pre_sll==0) {pre_sll=SymbolAsk(SYMBOL);}
		double pre_sl_pips=toPips(SymbolAsk(SYMBOL)-(pre_sll-toDigits(slp,SYMBOL)));
		
		     if (VolumeMode=="fixed")             {lots=DynamicLots(VolumeMode, VolumeSize);}
		else if (VolumeMode=="block-equity")      {lots=DynamicLots(VolumeMode, VolumeBlockPercent);}
		else if (VolumeMode=="block-balance")     {lots=DynamicLots(VolumeMode, VolumeBlockPercent);}
		else if (VolumeMode=="block-freemargin")  {lots=DynamicLots(VolumeMode, VolumeBlockPercent);}
		else if (VolumeMode=="equity")            {lots=DynamicLots(VolumeMode, VolumePercent);}
		else if (VolumeMode=="balance")           {lots=DynamicLots(VolumeMode, VolumePercent);}
		else if (VolumeMode=="freemargin")        {lots=DynamicLots(VolumeMode, VolumePercent);}
		else if (VolumeMode=="equityRisk")        {lots=DynamicLots(VolumeMode, VolumeRisk, pre_sl_pips);}
		else if (VolumeMode=="balanceRisk")       {lots=DynamicLots(VolumeMode, VolumeRisk, pre_sl_pips);}
		else if (VolumeMode=="freemarginRisk")    {lots=DynamicLots(VolumeMode, VolumeRisk, pre_sl_pips);}
		else if (VolumeMode=="fixedRisk")         {lots=DynamicLots(VolumeMode, VolumeSizeRisk, pre_sl_pips);}
		else if (VolumeMode=="fixedRatio")        {lots=DynamicLots(VolumeMode, FixedRatioUnitSize, FixedRatioDelta);}
		else if (VolumeMode=="dynamic")           {lots=AlignLots(_dVolumeSize_());}
		else if (VolumeMode=="1326")              {lots=Bet1326((int)OrdersGroup, SYMBOL, mm1326InitialLots, mm1326Reverse);}
		else if (VolumeMode=="fibonacci")         {lots=BetFibonacci((int)OrdersGroup, SYMBOL, mmFiboInitialLots);}
		else if (VolumeMode=="dalembert")         {lots=BetDalembert((int)OrdersGroup, SYMBOL, mmDalembertInitialLots, mmDalembertReverse);}
		else if (VolumeMode=="labouchere")        {lots=BetLabouchere((int)OrdersGroup, SYMBOL, mmLabouchereInitialLots, mmLabouchereList, mmLabouchereReverse);}
		else if (VolumeMode=="martingale")        {lots=BetMartingale((int)OrdersGroup, SYMBOL, mmMgInitialLots, mmMgMultiplyOnLoss, mmMgMultiplyOnProfit, mmMgAddLotsOnLoss, mmMgAddLotsOnProfit, mmMgResetOnLoss, mmMgResetOnProfit);}
		else if (VolumeMode=="sequence")          {lots=BetSequence((int)OrdersGroup, SYMBOL, mmSeqBaseLots, mmSeqOnLoss, mmSeqOnProfit, mmSeqReverse);}
		
		lots = AlignLots(lots, 0, VolumeUpperLimit);
		
		datetime exp = ExpirationTime(ExpMode,ExpDays,ExpHours,ExpMinutes,_dExp_());
		
		//-- send -------------------------------------------------------------------
		int ticket=BuyNow(SYMBOL, lots, sll, tpl, slp, tpp, Slippage, (MagicStart+(int)OrdersGroup), MyComment, ArrowColorBuy, exp);
		
		if (ticket>0) {_callback_(1);} else {_callback_(0);}
	}
};

// "Sell now" model
template<typename T1,typename T2,typename T3,typename T4,typename T5,typename T6,typename T7,typename T8,typename T9,typename _T9_,typename T10,typename T11,typename T12,typename T13,typename T14,typename T15,typename T16,typename T17,typename T18,typename T19,typename T20,typename T21,typename T22,typename T23,typename T24,typename T25,typename T26,typename T27,typename T28,typename T29,typename T30,typename T31,typename T32,typename T33,typename T34,typename T35,typename _T35_,typename T36,typename _T36_,typename T37,typename _T37_,typename T38,typename T39,typename T40,typename T41,typename _T41_,typename T42,typename _T42_,typename T43,typename _T43_,typename T44,typename T45,typename T46,typename T47,typename T48,typename _T48_,typename T49,typename T50,typename T51>
class MDL_SellNow: public BlockCalls
{
	public: /* Input Parameters */
	T1 OrdersGroup;
	T2 SYMBOL;
	T3 VolumeMode;
	T4 VolumeSize;
	T5 VolumeSizeRisk;
	T6 VolumeRisk;
	T7 VolumePercent;
	T8 VolumeBlockPercent;
	T9 dVolumeSize; virtual _T9_ _dVolumeSize_(){return(_T9_)0;}
	T10 FixedRatioUnitSize;
	T11 FixedRatioDelta;
	T12 mmMgInitialLots;
	T13 mmMgMultiplyOnLoss;
	T14 mmMgMultiplyOnProfit;
	T15 mmMgAddLotsOnLoss;
	T16 mmMgAddLotsOnProfit;
	T17 mmMgResetOnLoss;
	T18 mmMgResetOnProfit;
	T19 mm1326InitialLots;
	T20 mm1326Reverse;
	T21 mmFiboInitialLots;
	T22 mmDalembertInitialLots;
	T23 mmDalembertReverse;
	T24 mmLabouchereInitialLots;
	T25 mmLabouchereList;
	T26 mmLabouchereReverse;
	T27 mmSeqBaseLots;
	T28 mmSeqOnLoss;
	T29 mmSeqOnProfit;
	T30 mmSeqReverse;
	T31 VolumeUpperLimit;
	T32 StopLossMode;
	T33 StopLossPips;
	T34 StopLossPercentTP;
	T35 dlStopLoss; virtual _T35_ _dlStopLoss_(){return(_T35_)0;}
	T36 dpStopLoss; virtual _T36_ _dpStopLoss_(){return(_T36_)0;}
	T37 ddStopLoss; virtual _T37_ _ddStopLoss_(){return(_T37_)0;}
	T38 TakeProfitMode;
	T39 TakeProfitPips;
	T40 TakeProfitPercentSL;
	T41 dlTakeProfit; virtual _T41_ _dlTakeProfit_(){return(_T41_)0;}
	T42 dpTakeProfit; virtual _T42_ _dpTakeProfit_(){return(_T42_)0;}
	T43 ddTakeProfit; virtual _T43_ _ddTakeProfit_(){return(_T43_)0;}
	T44 ExpMode;
	T45 ExpDays;
	T46 ExpHours;
	T47 ExpMinutes;
	T48 dExp; virtual _T48_ _dExp_(){return(_T48_)0;}
	T49 Slippage;
	T50 MyComment;
	T51 ArrowColorSell;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDL_SellNow()
	{
		OrdersGroup = "";
		SYMBOL = CurrentSymbol();
		VolumeMode = "fixed";
		VolumeSize = 0.1;
		VolumeSizeRisk = 50;
		VolumeRisk = 2.5;
		VolumePercent = 100;
		VolumeBlockPercent = 3;
		FixedRatioUnitSize = 0.01;
		FixedRatioDelta = 20;
		mmMgInitialLots = 0.1;
		mmMgMultiplyOnLoss = 2;
		mmMgMultiplyOnProfit = 1;
		mmMgAddLotsOnLoss = 0;
		mmMgAddLotsOnProfit = 0;
		mmMgResetOnLoss = 0;
		mmMgResetOnProfit = 1;
		mm1326InitialLots = 0.1;
		mm1326Reverse = false;
		mmFiboInitialLots = 0.1;
		mmDalembertInitialLots = 0.1;
		mmDalembertReverse = false;
		mmLabouchereInitialLots = 0.1;
		mmLabouchereList = "1,2,3,4,5,6";
		mmLabouchereReverse = false;
		mmSeqBaseLots = 0.1;
		mmSeqOnLoss = "3,2,6";
		mmSeqOnProfit = "1";
		mmSeqReverse = false;
		VolumeUpperLimit = 0;
		StopLossMode = "fixed";
		StopLossPips = 50;
		StopLossPercentTP = 100;
		TakeProfitMode = "fixed";
		TakeProfitPips = 50;
		TakeProfitPercentSL = 100;
		ExpMode = "GTC";
		ExpDays = 0;
		ExpHours = 1;
		ExpMinutes = 0;
		Slippage = 4;
		MyComment = "Short trade";
		ArrowColorSell = Red;
	}

	public: /* The main method */
	virtual void _execute_()
	{
		SetSymbol(SYMBOL);
		
		//-- stops ------------------------------------------------------------------
		double sll=0, slp=0, tpl=0, tpp=0;
		
		     if (StopLossMode=="fixed")        {slp=StopLossPips;}
		else if (StopLossMode=="dynamicPips")  {slp=_dpStopLoss_();}
		else if (StopLossMode=="dynamicDigits"){slp=toPips(_ddStopLoss_(),SYMBOL);}
		else if (StopLossMode=="dynamicLevel") {sll=_dlStopLoss_();}
		
		     if (TakeProfitMode=="fixed")         {tpp=TakeProfitPips;}
		else if (TakeProfitMode=="dynamicPips")   {tpp=_dpTakeProfit_();}
		else if (TakeProfitMode=="dynamicDigits") {tpp=toPips(_ddTakeProfit_(),SYMBOL);}
		else if (TakeProfitMode=="dynamicLevel")  {tpl=_dlTakeProfit_();}
		
		if (StopLossMode == "percentTP") {
		   if (tpp > 0) {slp = tpp*StopLossPercentTP/100;}
		   if (tpl > 0) {slp = toPips(MathAbs(SymbolAsk(SYMBOL) - tpl), SYMBOL)*StopLossPercentTP/100;}
		}
		if (TakeProfitMode == "percentSL") {
		   if (slp > 0) {tpp = slp*TakeProfitPercentSL/100;}
		   if (sll > 0) {tpp = toPips(MathAbs(SymbolAsk(SYMBOL) - sll), SYMBOL)*TakeProfitPercentSL/100;}
		}
		
		//-- lots -------------------------------------------------------------------
		double lots=0;
		double pre_sll=sll; if (pre_sll==0) {pre_sll=SymbolBid(SYMBOL);}
		double pre_sl_pips=toPips((pre_sll+toDigits(slp,SYMBOL))-SymbolBid(SYMBOL));
		
		     if (VolumeMode=="fixed")             {lots=DynamicLots(VolumeMode, VolumeSize);}
		else if (VolumeMode=="block-equity")      {lots=DynamicLots(VolumeMode, VolumeBlockPercent);}
		else if (VolumeMode=="block-balance")     {lots=DynamicLots(VolumeMode, VolumeBlockPercent);}
		else if (VolumeMode=="block-freemargin")  {lots=DynamicLots(VolumeMode, VolumeBlockPercent);}
		else if (VolumeMode=="equity")            {lots=DynamicLots(VolumeMode, VolumePercent);}
		else if (VolumeMode=="balance")           {lots=DynamicLots(VolumeMode, VolumePercent);}
		else if (VolumeMode=="freemargin")        {lots=DynamicLots(VolumeMode, VolumePercent);}
		else if (VolumeMode=="equityRisk")        {lots=DynamicLots(VolumeMode, VolumeRisk, pre_sl_pips);}
		else if (VolumeMode=="balanceRisk")       {lots=DynamicLots(VolumeMode, VolumeRisk, pre_sl_pips);}
		else if (VolumeMode=="freemarginRisk")    {lots=DynamicLots(VolumeMode, VolumeRisk, pre_sl_pips);}
		else if (VolumeMode=="fixedRisk")         {lots=DynamicLots(VolumeMode, VolumeSizeRisk, pre_sl_pips);}
		else if (VolumeMode=="fixedRatio")        {lots=DynamicLots(VolumeMode, FixedRatioUnitSize, FixedRatioDelta);}
		else if (VolumeMode=="dynamic")           {lots=AlignLots(_dVolumeSize_());}
		else if (VolumeMode=="1326")              {lots=Bet1326((int)OrdersGroup, SYMBOL, mm1326InitialLots, mm1326Reverse);}
		else if (VolumeMode=="fibonacci")         {lots=BetFibonacci((int)OrdersGroup, SYMBOL, mmFiboInitialLots);}
		else if (VolumeMode=="dalembert")         {lots=BetDalembert((int)OrdersGroup, SYMBOL, mmDalembertInitialLots, mmDalembertReverse);}
		else if (VolumeMode=="labouchere")        {lots=BetLabouchere((int)OrdersGroup, SYMBOL, mmLabouchereInitialLots, mmLabouchereList, mmLabouchereReverse);}
		else if (VolumeMode=="martingale")        {lots=BetMartingale((int)OrdersGroup, SYMBOL, mmMgInitialLots, mmMgMultiplyOnLoss, mmMgMultiplyOnProfit, mmMgAddLotsOnLoss, mmMgAddLotsOnProfit, mmMgResetOnLoss, mmMgResetOnProfit);}
		else if (VolumeMode=="sequence")          {lots=BetSequence((int)OrdersGroup, SYMBOL, mmSeqBaseLots, mmSeqOnLoss, mmSeqOnProfit, mmSeqReverse);}
		
		lots = AlignLots(lots, 0, VolumeUpperLimit);
		
		datetime exp = ExpirationTime(ExpMode,ExpDays,ExpHours,ExpMinutes,_dExp_());
		
		//-- send -------------------------------------------------------------------
		int ticket=SellNow(SYMBOL, lots, sll, tpl, slp, tpp, Slippage, (MagicStart+(int)OrdersGroup), MyComment, ArrowColorSell, exp);
		
		if (ticket>0) {_callback_(1);} else {_callback_(0);}
	}
};


//------------------------------------------------------------------------------------------------------------------------

// "Ask, Bid, Mid" model
class MDLIC_prices_prices
{
	public: /* Input Parameters */
	string Price;
	int TickID;
	string SYMBOL;
	/* Static Parameters */
	int digits;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDLIC_prices_prices()
	{
		Price = "ASK";
		TickID = 0;
		SYMBOL = CurrentSymbol();
		/* Static Parameters (initial value) */
		digits =  (int)SymbolInfoInteger(SYMBOL, SYMBOL_DIGITS);
	}

	public: /* The main method */
	double _execute_()
	{
		double retval = 0;
		int tID       = TickID + FXD_MORE_SHIFT;
		
		     if (Price == "ASK")      {retval = TicksData(SYMBOL,MODE_ASK,tID);}
		else if (Price == "BID")      {retval = TicksData(SYMBOL,MODE_BID,tID);}
		else if (Price == "MID")      {retval = ((TicksData(SYMBOL,MODE_ASK,tID)+TicksData(SYMBOL,MODE_BID,tID))/2);}
		else if (Price == "BIDHIGH")  {retval = SymbolInfoDouble(SYMBOL,SYMBOL_BIDHIGH);}
		else if (Price == "BIDLOW")   {retval = SymbolInfoDouble(SYMBOL,SYMBOL_BIDLOW);}
		else if (Price == "ASKHIGH")  {retval = SymbolInfoDouble(SYMBOL,SYMBOL_ASKHIGH);}
		else if (Price == "ASKLOW")   {retval = SymbolInfoDouble(SYMBOL,SYMBOL_ASKLOW);}
		else if (Price == "LAST")     {retval = SymbolInfoDouble(SYMBOL,SYMBOL_LAST);}
		else if (Price == "LASTHIGH") {retval = SymbolInfoDouble(SYMBOL,SYMBOL_LASTHIGH);}
		else if (Price == "LASTLOW")  {retval = SymbolInfoDouble(SYMBOL,SYMBOL_LASTLOW);}
		
		return NormalizeDouble(retval, digits);
	}
};

// "Numeric" model
class MDLIC_value_value
{
	public: /* Input Parameters */
	double Value;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDLIC_value_value()
	{
		Value = 1;
	}

	public: /* The main method */
	double _execute_()
	{
		return Value;
	}
};

// "Time" model
class MDLIC_value_time
{
	public: /* Input Parameters */
	int ModeTime;
	int TimeSource;
	string TimeStamp;
	int TimeCandleID;
	string TimeMarket;
	ENUM_TIMEFRAMES TimeCandleTimeframe;
	int TimeComponentYear;
	int TimeComponentMonth;
	int TimeComponentDay;
	int TimeComponentHour;
	int TimeComponentMinute;
	int TimeComponentSecond;
	int ModeTimeShift;
	int TimeShiftYears;
	int TimeShiftMonths;
	int TimeShiftWeeks;
	int TimeShiftDays;
	int TimeShiftHours;
	int TimeShiftMinutes;
	int TimeShiftSeconds;
	bool TimeSkipWeekdays;
	/* Static Parameters */
	datetime retval;
	datetime retval0;
	int ModeTime0;
	int smodeshift;
	virtual void _callback_(int r) {return;}

	public: /* Constructor */
	MDLIC_value_time()
	{
		ModeTime = 0;
		TimeSource = 0;
		TimeStamp = "00:00";
		TimeCandleID = 1;
		TimeMarket = "";
		TimeCandleTimeframe = 0;
		TimeComponentYear = 0;
		TimeComponentMonth = 0;
		TimeComponentDay = 0;
		TimeComponentHour = 12;
		TimeComponentMinute = 0;
		TimeComponentSecond = 0;
		ModeTimeShift = 0;
		TimeShiftYears = 0;
		TimeShiftMonths = 0;
		TimeShiftWeeks = 0;
		TimeShiftDays = 0;
		TimeShiftHours = 0;
		TimeShiftMinutes = 0;
		TimeShiftSeconds = 0;
		TimeSkipWeekdays = false;
		/* Static Parameters (initial value) */
		retval =  0;
		retval0 =  0;
		ModeTime0 =  0;
		smodeshift =  0;
	}

	public: /* The main method */
	datetime _execute_()
	{
		if(ModeTime==0) {
		        if (TimeSource == 0) {retval = TimeCurrent();}
			else if (TimeSource == 1) {retval = TimeLocal();}
			else if (TimeSource == 2) {retval = TimeGMT();}
		}
		else if(ModeTime==1) {
		      retval  = StringToTime(TimeStamp);
		      retval0 = retval;
		}
		else if(ModeTime==2) {
		   retval = TimeFromComponents(TimeSource, TimeComponentYear, TimeComponentMonth, TimeComponentDay, TimeComponentHour, TimeComponentMinute, TimeComponentSecond);
		}
		else if(ModeTime==3) {
			
			
		   if (TimeMarket == "") {TimeMarket = Symbol();}
		   retval = iTime(TimeMarket,TimeCandleTimeframe,TimeCandleID);
		}
		
		if (ModeTimeShift>0) {
		   int sh=1;
		   if (ModeTimeShift==1) {sh=-1;}
		   
		   static int years0=0,months0=0;
		   
		   if (
		      ModeTimeShift!=smodeshift
		      || TimeShiftYears!=years0 || TimeShiftMonths!=months0
		   )
		   {
		      years0=TimeShiftYears; months0=TimeShiftMonths;
		      
		      if (TimeShiftYears>0 || TimeShiftMonths>0) {
		         int year=0,month=0,week=0,day=0,hour=0,minute=0,second=0;
		         if (ModeTime==3) {
		            year=TimeComponentYear; month=TimeComponentYear;    day=TimeComponentDay;
		            hour=TimeComponentHour; minute=TimeComponentMinute; second=TimeComponentSecond;
		         }
		         else {
		            year=TimeYear(retval); month=TimeMonth(retval);   day=TimeDay(retval);
		            hour=TimeHour(retval); minute=TimeMinute(retval); second=TimeSeconds(retval);
		         }
		         
		         year  = year + TimeShiftYears * sh;
		         month = month + TimeShiftMonths * sh;
		
		         if (month<0) {month=12-month;}
		         else if (month>12) {month=month-12;}
		
		         retval = StringToTime(IntegerToString(year)+"."+IntegerToString(month)+"."+IntegerToString(day)+" "+IntegerToString(hour)+":"+IntegerToString(minute)+":"+IntegerToString(second));
		      }
		   }
		
		   retval = retval + TimeShiftWeeks*604800*sh+TimeShiftDays*86400*sh+TimeShiftHours*3600*sh+TimeShiftMinutes*60*sh+TimeShiftSeconds*sh;
		      
		   if (TimeSkipWeekdays==true) {
		      int weekday=TimeDayOfWeek(retval);
		      
		      if (sh>0) { // forward
		         if (weekday==0) {retval=retval+86400;}
		         else if (weekday==6) {retval=retval+172800;}
		      }
		      else if (sh<0) { // back
		         if (weekday==0) {retval=retval-172800;}
		         else if (weekday==6) {retval=retval-86400;}
		      }
		   }
		}
		
		smodeshift = ModeTimeShift;
		ModeTime0  = ModeTime;
		
		return (datetime)retval;
	}
};


//------------------------------------------------------------------------------------------------------------------------

// Block 1 (Set \"Current Market\" for next blocks)
class Block0: public MDL_SetCurrentSymbol2<string>
{

	public: /* Constructor */
	Block0() {
		__block_number = 0;
		__block_user_number = "1";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {3};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[3].run(0);
		}
	}

	virtual void _beforeExecute_()
	{
		ListOfSymbols = _externs::inp1_ListOfSymbols;
	}
};

// Block 2 (Set \"Current Market\" for next blocks)
class Block1: public MDL_SetCurrentSymbol2<string>
{

	public: /* Constructor */
	Block1() {
		__block_number = 1;
		__block_user_number = "2";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {4};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[4].run(1);
		}
	}

	virtual void _beforeExecute_()
	{
		ListOfSymbols = _externs::inp2_ListOfSymbols;
	}
};

// Block 3 (Set \"Current Market\" for next blocks)
class Block2: public MDL_SetCurrentSymbol2<string>
{

	public: /* Constructor */
	Block2() {
		__block_number = 2;
		__block_user_number = "3";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {5};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[5].run(2);
		}
	}

	virtual void _beforeExecute_()
	{
		ListOfSymbols = _externs::inp3_ListOfSymbols;
	}
};

// Block 4 (Round numbers detector)
class Block3: public MDL_RoundNumbersDetector<string,string,double,double,double,double,double>
{

	public: /* Constructor */
	Block3() {
		__block_number = 3;
		__block_user_number = "4";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[2] = {6,7};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		ModeDistance = "pips";
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 0) {
			_blocks_[7].run(3);
		}
		else if (value == 1) {
			_blocks_[6].run(3);
		}
	}

	virtual void _beforeExecute_()
	{
		Market = CurrentSymbol();
		RoundNumbersPips = c::big_grid;
	}
};

// Block 5 (Round numbers detector)
class Block4: public MDL_RoundNumbersDetector<string,string,double,double,double,double,double>
{

	public: /* Constructor */
	Block4() {
		__block_number = 4;
		__block_user_number = "5";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[2] = {8,9};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		ModeDistance = "pips";
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 0) {
			_blocks_[9].run(4);
		}
		else if (value == 1) {
			_blocks_[8].run(4);
		}
	}

	virtual void _beforeExecute_()
	{
		Market = CurrentSymbol();
		RoundNumbersPips = c::big_grid;
	}
};

// Block 6 (Round numbers detector)
class Block5: public MDL_RoundNumbersDetector<string,string,double,double,double,double,double>
{

	public: /* Constructor */
	Block5() {
		__block_number = 5;
		__block_user_number = "6";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[2] = {10,11};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		ModeDistance = "pips";
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 0) {
			_blocks_[11].run(5);
		}
		else if (value == 1) {
			_blocks_[10].run(5);
		}
	}

	virtual void _beforeExecute_()
	{
		Market = CurrentSymbol();
		RoundNumbersPips = c::small_grid;
	}
};

// Block 7 (No trade nearby)
class Block6: public MDL_NoNearbyRunning<string,string,string,string,string,string,MDLIC_prices_prices,double,string,double,double,int>
{

	public: /* Constructor */
	Block6() {
		__block_number = 6;
		__block_user_number = "7";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {12};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		OrdersGroup = "1";
		BuysOrSells = "buys";
	}

	public: /* Custom methods */
	virtual double _BasePrice_() {
		BasePrice.SYMBOL = CurrentSymbol();

		return BasePrice._execute_();
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[12].run(6);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		RangePips = c::big_grid;
	}
};

// Block 8 (No trade nearby)
class Block7: public MDL_NoNearbyRunning<string,string,string,string,string,string,MDLIC_prices_prices,double,string,double,double,int>
{

	public: /* Constructor */
	Block7() {
		__block_number = 7;
		__block_user_number = "8";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {13};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		OrdersGroup = "2";
		BuysOrSells = "sells";
	}

	public: /* Custom methods */
	virtual double _BasePrice_() {
		BasePrice.SYMBOL = CurrentSymbol();

		return BasePrice._execute_();
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[13].run(7);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		RangePips = c::big_grid;
	}
};

// Block 9 (No trade nearby)
class Block8: public MDL_NoNearbyRunning<string,string,string,string,string,string,MDLIC_prices_prices,double,string,double,double,int>
{

	public: /* Constructor */
	Block8() {
		__block_number = 8;
		__block_user_number = "9";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {14};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		OrdersGroup = "1";
		BuysOrSells = "buys";
	}

	public: /* Custom methods */
	virtual double _BasePrice_() {
		BasePrice.SYMBOL = CurrentSymbol();

		return BasePrice._execute_();
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[14].run(8);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		RangePips = c::big_grid;
	}
};

// Block 10 (No trade nearby)
class Block9: public MDL_NoNearbyRunning<string,string,string,string,string,string,MDLIC_prices_prices,double,string,double,double,int>
{

	public: /* Constructor */
	Block9() {
		__block_number = 9;
		__block_user_number = "10";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {15};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		OrdersGroup = "2";
		BuysOrSells = "sells";
	}

	public: /* Custom methods */
	virtual double _BasePrice_() {
		BasePrice.SYMBOL = CurrentSymbol();

		return BasePrice._execute_();
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[15].run(9);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		RangePips = c::big_grid;
	}
};

// Block 11 (No trade nearby)
class Block10: public MDL_NoNearbyRunning<string,string,string,string,string,string,MDLIC_prices_prices,double,string,double,double,int>
{

	public: /* Constructor */
	Block10() {
		__block_number = 10;
		__block_user_number = "11";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {16};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		OrdersGroup = "1";
		BuysOrSells = "sells";
	}

	public: /* Custom methods */
	virtual double _BasePrice_() {
		BasePrice.SYMBOL = CurrentSymbol();

		return BasePrice._execute_();
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[16].run(10);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		RangePips = c::small_grid;
	}
};

// Block 12 (No trade nearby)
class Block11: public MDL_NoNearbyRunning<string,string,string,string,string,string,MDLIC_prices_prices,double,string,double,double,int>
{

	public: /* Constructor */
	Block11() {
		__block_number = 11;
		__block_user_number = "12";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {17};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
		// Block input parameters
		OrdersGroup = "2";
		BuysOrSells = "buys";
	}

	public: /* Custom methods */
	virtual double _BasePrice_() {
		BasePrice.SYMBOL = CurrentSymbol();

		return BasePrice._execute_();
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[17].run(11);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		RangePips = c::small_grid;
	}
};

// Block 13 (Once per bar)
class Block12: public MDL_OncePerBar<string,ENUM_TIMEFRAMES,int>
{

	public: /* Constructor */
	Block12() {
		__block_number = 12;
		__block_user_number = "13";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {18};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[18].run(12);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		TIMEFRAME = CurrentTimeframe();
	}
};

// Block 14 (Once per bar)
class Block13: public MDL_OncePerBar<string,ENUM_TIMEFRAMES,int>
{

	public: /* Constructor */
	Block13() {
		__block_number = 13;
		__block_user_number = "14";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {19};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[19].run(13);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		TIMEFRAME = CurrentTimeframe();
	}
};

// Block 15 (Once per bar)
class Block14: public MDL_OncePerBar<string,ENUM_TIMEFRAMES,int>
{

	public: /* Constructor */
	Block14() {
		__block_number = 14;
		__block_user_number = "15";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {20};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[20].run(14);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		TIMEFRAME = CurrentTimeframe();
	}
};

// Block 16 (Once per bar)
class Block15: public MDL_OncePerBar<string,ENUM_TIMEFRAMES,int>
{

	public: /* Constructor */
	Block15() {
		__block_number = 15;
		__block_user_number = "16";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {21};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[21].run(15);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		TIMEFRAME = CurrentTimeframe();
	}
};

// Block 17 (Once per bar)
class Block16: public MDL_OncePerBar<string,ENUM_TIMEFRAMES,int>
{

	public: /* Constructor */
	Block16() {
		__block_number = 16;
		__block_user_number = "17";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {22};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[22].run(16);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		TIMEFRAME = CurrentTimeframe();
	}
};

// Block 18 (Once per bar)
class Block17: public MDL_OncePerBar<string,ENUM_TIMEFRAMES,int>
{

	public: /* Constructor */
	Block17() {
		__block_number = 17;
		__block_user_number = "18";
		_beforeExecuteEnabled = true;

		// Fill the list of outbound blocks
		int ___outbound_blocks[1] = {23};
		ArrayCopy(__outbound_blocks, ___outbound_blocks);
	}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
		if (value == 1) {
			_blocks_[23].run(17);
		}
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		TIMEFRAME = CurrentTimeframe();
	}
};

// Block 19 (Buy now)
class Block18: public MDL_BuyNow<string,string,string,double,double,double,double,double,MDLIC_value_value,double,double,double,double,double,double,double,double,int,int,double,bool,double,double,bool,double,string,bool,double,string,string,bool,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,int,int,int,MDLIC_value_time,datetime,double,string,color>
{

	public: /* Constructor */
	Block18() {
		__block_number = 18;
		__block_user_number = "19";
		_beforeExecuteEnabled = true;

		// IC input parameters
		dVolumeSize.Value = 0.1;
		dpStopLoss.Value = 100;
		ddStopLoss.Value = 0.01;
		dpTakeProfit.Value = 100;
		ddTakeProfit.Value = 0.01;
		dExp.ModeTimeShift = 2;
		dExp.TimeShiftDays = 1;
		dExp.TimeSkipWeekdays = true;
		// Block input parameters
		OrdersGroup = "1";
		MyComment = "1";
	}

	public: /* Custom methods */
	virtual double _dVolumeSize_() {return dVolumeSize._execute_();}
	virtual double _dlStopLoss_() {return dlStopLoss._execute_();}
	virtual double _dpStopLoss_() {return dpStopLoss._execute_();}
	virtual double _ddStopLoss_() {return ddStopLoss._execute_();}
	virtual double _dlTakeProfit_() {return dlTakeProfit._execute_();}
	virtual double _dpTakeProfit_() {return dpTakeProfit._execute_();}
	virtual double _ddTakeProfit_() {return ddTakeProfit._execute_();}
	virtual datetime _dExp_() {return dExp._execute_();}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		VolumeSize = c::volume;
		StopLossPips = c::big_SL;
		TakeProfitPips = c::big_TP;
		ArrowColorBuy = Blue;
	}
};

// Block 20 (Sell now)
class Block19: public MDL_SellNow<string,string,string,double,double,double,double,double,MDLIC_value_value,double,double,double,double,double,double,double,double,int,int,double,bool,double,double,bool,double,string,bool,double,string,string,bool,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,int,int,int,MDLIC_value_time,datetime,double,string,color>
{

	public: /* Constructor */
	Block19() {
		__block_number = 19;
		__block_user_number = "20";
		_beforeExecuteEnabled = true;

		// IC input parameters
		dVolumeSize.Value = 0.1;
		dpStopLoss.Value = 100;
		ddStopLoss.Value = 0.01;
		dpTakeProfit.Value = 100;
		ddTakeProfit.Value = 0.01;
		dExp.ModeTimeShift = 2;
		dExp.TimeShiftDays = 1;
		dExp.TimeSkipWeekdays = true;
		// Block input parameters
		OrdersGroup = "2";
		MyComment = "2";
	}

	public: /* Custom methods */
	virtual double _dVolumeSize_() {return dVolumeSize._execute_();}
	virtual double _dlStopLoss_() {return dlStopLoss._execute_();}
	virtual double _dpStopLoss_() {return dpStopLoss._execute_();}
	virtual double _ddStopLoss_() {return ddStopLoss._execute_();}
	virtual double _dlTakeProfit_() {return dlTakeProfit._execute_();}
	virtual double _dpTakeProfit_() {return dpTakeProfit._execute_();}
	virtual double _ddTakeProfit_() {return ddTakeProfit._execute_();}
	virtual datetime _dExp_() {return dExp._execute_();}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		VolumeSize = c::volume;
		StopLossPips = c::big_SL;
		TakeProfitPips = c::big_TP;
		ArrowColorSell = Red;
	}
};

// Block 21 (Buy now)
class Block20: public MDL_BuyNow<string,string,string,double,double,double,double,double,MDLIC_value_value,double,double,double,double,double,double,double,double,int,int,double,bool,double,double,bool,double,string,bool,double,string,string,bool,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,int,int,int,MDLIC_value_time,datetime,double,string,color>
{

	public: /* Constructor */
	Block20() {
		__block_number = 20;
		__block_user_number = "21";
		_beforeExecuteEnabled = true;

		// IC input parameters
		dVolumeSize.Value = 0.1;
		dpStopLoss.Value = 100;
		ddStopLoss.Value = 0.01;
		dpTakeProfit.Value = 100;
		ddTakeProfit.Value = 0.01;
		dExp.ModeTimeShift = 2;
		dExp.TimeShiftDays = 1;
		dExp.TimeSkipWeekdays = true;
		// Block input parameters
		OrdersGroup = "1";
		MyComment = "1";
	}

	public: /* Custom methods */
	virtual double _dVolumeSize_() {return dVolumeSize._execute_();}
	virtual double _dlStopLoss_() {return dlStopLoss._execute_();}
	virtual double _dpStopLoss_() {return dpStopLoss._execute_();}
	virtual double _ddStopLoss_() {return ddStopLoss._execute_();}
	virtual double _dlTakeProfit_() {return dlTakeProfit._execute_();}
	virtual double _dpTakeProfit_() {return dpTakeProfit._execute_();}
	virtual double _ddTakeProfit_() {return ddTakeProfit._execute_();}
	virtual datetime _dExp_() {return dExp._execute_();}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		VolumeSize = c::volume;
		StopLossPips = c::big_SL;
		TakeProfitPips = c::big_TP;
		ArrowColorBuy = Blue;
	}
};

// Block 22 (Sell now)
class Block21: public MDL_SellNow<string,string,string,double,double,double,double,double,MDLIC_value_value,double,double,double,double,double,double,double,double,int,int,double,bool,double,double,bool,double,string,bool,double,string,string,bool,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,int,int,int,MDLIC_value_time,datetime,double,string,color>
{

	public: /* Constructor */
	Block21() {
		__block_number = 21;
		__block_user_number = "22";
		_beforeExecuteEnabled = true;

		// IC input parameters
		dVolumeSize.Value = 0.1;
		dpStopLoss.Value = 100;
		ddStopLoss.Value = 0.01;
		dpTakeProfit.Value = 100;
		ddTakeProfit.Value = 0.01;
		dExp.ModeTimeShift = 2;
		dExp.TimeShiftDays = 1;
		dExp.TimeSkipWeekdays = true;
		// Block input parameters
		OrdersGroup = "2";
		MyComment = "2";
	}

	public: /* Custom methods */
	virtual double _dVolumeSize_() {return dVolumeSize._execute_();}
	virtual double _dlStopLoss_() {return dlStopLoss._execute_();}
	virtual double _dpStopLoss_() {return dpStopLoss._execute_();}
	virtual double _ddStopLoss_() {return ddStopLoss._execute_();}
	virtual double _dlTakeProfit_() {return dlTakeProfit._execute_();}
	virtual double _dpTakeProfit_() {return dpTakeProfit._execute_();}
	virtual double _ddTakeProfit_() {return ddTakeProfit._execute_();}
	virtual datetime _dExp_() {return dExp._execute_();}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		VolumeSize = c::volume;
		StopLossPips = c::big_SL;
		TakeProfitPips = c::big_TP;
		ArrowColorSell = Red;
	}
};

// Block 23 (Sell now)
class Block22: public MDL_SellNow<string,string,string,double,double,double,double,double,MDLIC_value_value,double,double,double,double,double,double,double,double,int,int,double,bool,double,double,bool,double,string,bool,double,string,string,bool,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,int,int,int,MDLIC_value_time,datetime,double,string,color>
{

	public: /* Constructor */
	Block22() {
		__block_number = 22;
		__block_user_number = "23";
		_beforeExecuteEnabled = true;

		// IC input parameters
		dVolumeSize.Value = 0.1;
		dpStopLoss.Value = 100;
		ddStopLoss.Value = 0.01;
		dpTakeProfit.Value = 100;
		ddTakeProfit.Value = 0.01;
		dExp.ModeTimeShift = 2;
		dExp.TimeShiftDays = 1;
		dExp.TimeSkipWeekdays = true;
		// Block input parameters
		OrdersGroup = "1";
		StopLossMode = "none";
		MyComment = "1";
	}

	public: /* Custom methods */
	virtual double _dVolumeSize_() {return dVolumeSize._execute_();}
	virtual double _dlStopLoss_() {return dlStopLoss._execute_();}
	virtual double _dpStopLoss_() {return dpStopLoss._execute_();}
	virtual double _ddStopLoss_() {return ddStopLoss._execute_();}
	virtual double _dlTakeProfit_() {return dlTakeProfit._execute_();}
	virtual double _dpTakeProfit_() {return dpTakeProfit._execute_();}
	virtual double _ddTakeProfit_() {return ddTakeProfit._execute_();}
	virtual datetime _dExp_() {return dExp._execute_();}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		VolumeSize = c::volume;
		TakeProfitPips = c::small_TP;
		ArrowColorSell = Red;
	}
};

// Block 24 (Buy now)
class Block23: public MDL_BuyNow<string,string,string,double,double,double,double,double,MDLIC_value_value,double,double,double,double,double,double,double,double,int,int,double,bool,double,double,bool,double,string,bool,double,string,string,bool,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,double,double,MDLIC_value_value,double,MDLIC_value_value,double,MDLIC_value_value,double,string,int,int,int,MDLIC_value_time,datetime,double,string,color>
{

	public: /* Constructor */
	Block23() {
		__block_number = 23;
		__block_user_number = "24";
		_beforeExecuteEnabled = true;

		// IC input parameters
		dVolumeSize.Value = 0.1;
		dpStopLoss.Value = 100;
		ddStopLoss.Value = 0.01;
		dpTakeProfit.Value = 100;
		ddTakeProfit.Value = 0.01;
		dExp.ModeTimeShift = 2;
		dExp.TimeShiftDays = 1;
		dExp.TimeSkipWeekdays = true;
		// Block input parameters
		OrdersGroup = "2";
		StopLossMode = "none";
		MyComment = "2";
	}

	public: /* Custom methods */
	virtual double _dVolumeSize_() {return dVolumeSize._execute_();}
	virtual double _dlStopLoss_() {return dlStopLoss._execute_();}
	virtual double _dpStopLoss_() {return dpStopLoss._execute_();}
	virtual double _ddStopLoss_() {return ddStopLoss._execute_();}
	virtual double _dlTakeProfit_() {return dlTakeProfit._execute_();}
	virtual double _dpTakeProfit_() {return dpTakeProfit._execute_();}
	virtual double _ddTakeProfit_() {return ddTakeProfit._execute_();}
	virtual datetime _dExp_() {return dExp._execute_();}

	public: /* Callback & Run */
	virtual void _callback_(int value) {
	}

	virtual void _beforeExecute_()
	{
		SYMBOL = CurrentSymbol();
		VolumeSize = c::volume;
		TakeProfitPips = c::small_TP;
		ArrowColorBuy = Blue;
	}
};


/************************************************************************************************************************/
// +------------------------------------------------------------------------------------------------------------------+ //
// |                                                   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);
}

double AlignLots(double lots, double lowerlots=0, double upperlots=0)
{
	string symbol=GetSymbol();

   double LotStep=MarketInfo(symbol,MODE_LOTSTEP);
   double LotSize=MarketInfo(symbol,MODE_LOTSIZE);
   double MinLots=MarketInfo(symbol,MODE_MINLOT);
   double MaxLots=MarketInfo(symbol,MODE_MAXLOT);
   double margin_required=MarketInfo(symbol,MODE_MARGINREQUIRED);
   
	if (lots==EMPTY_VALUE) {lots=0;}
	
   lots=MathRound(lots/LotStep)*LotStep;
   
   if (lots<MinLots) {lots=MinLots;}
   if (lots>MaxLots) {lots=MaxLots;}

   if (lowerlots > 0)
   {
      lowerlots = MathRound(lowerlots/LotStep)*LotStep;
      if (lots < lowerlots) {lots = lowerlots;}
   }
   if (upperlots > 0)
   {
      upperlots = MathRound(upperlots/LotStep)*LotStep;
      if (lots > upperlots) {lots = upperlots;}
   }
   
   return (lots);
}

double AlignStopLoss(
   string symbol,
   int type,
   double price,
   double slo=0, // original sl, used when modifying
   double sll=0,
   double slp=0,
   bool consider_freezelevel=false
   )
{
   double sl=0;

   if (MathAbs(sll)==EMPTY_VALUE) {sll=0;}
   if (MathAbs(slp)==EMPTY_VALUE) {slp=0;}
   if (sll==0 && slp==0) {return(0);} // no sl - return 0
   if (price<=0) {Print("AlignStopLoss() error: No price entered");return(-1);}
   
   double point =MarketInfo(symbol,MODE_POINT);
   int digits   =(int)MarketInfo(symbol,MODE_DIGITS);
   slp = slp*PipValue(symbol)*point;
   
   
   //-- buy-sell identifier ---------------------------------------------
   int bs=1;
   if (
      type==OP_BUY
      || type==OP_BUYSTOP
      || type==OP_BUYLIMIT
      )
   {
      bs=1;
   }
   else if (
      type==OP_SELL
      || type==OP_SELLSTOP
      || type==OP_SELLLIMIT
      )
   {
      bs=-1;
   }

	//-- prices that will be used ----------------------------------------
   double askbid=price;
   double bidask=price;
   
   if (type==OP_BUY || type==OP_SELL)
   {
      double ask =MarketInfo(symbol,MODE_ASK);
      double bid =MarketInfo(symbol,MODE_BID);
      
      askbid=ask;
      bidask=bid;
      if (bs<0) {
        askbid=bid;
        bidask=ask;
      }
   }
   
   //-- build sl level -------------------------------------------------- 
   if (sll==0 && slp!=0) {sll=price;}

   if (sll>0) {sl=sll-slp*bs;}
   
   if (sl<0) {return(-1);}
      
   sl=NormalizeDouble(sl,digits);
   slo = NormalizeDouble(slo,digits);
   if (sl == slo) {return sl;}
   
   //-- build limit levels ----------------------------------------------
   double minstops=MarketInfo(symbol,MODE_STOPLEVEL);
   if (consider_freezelevel==true) {
      double freezelevel=MarketInfo(symbol,MODE_FREEZELEVEL);
      if (freezelevel>minstops) {minstops=freezelevel;}
   }
   minstops=NormalizeDouble(minstops*point,digits);
      
   double sllimit=bidask-minstops*bs; // SL min price level
   
   //-- check and align sl, print errors --------------------------------
   //-- do not do it when the stop is the same as the original
   if (sl>0) {
      /*if (sl==askbid)
      {
         sl=0;
      }
      else */
		
      if ((bs>0 && sl>askbid) || (bs<0 && sl<askbid))
      {
         string abstr="";
         if (bs>0) {abstr="Bid";} else {abstr="Ask";}
         Print(
            "Error: Invalid SL requested (",
            DoubleToStr(sl,digits),
            " for ",abstr," price ",
            bidask,
            ")"
         );
         return(-1);
      }
      else if ((bs>0 && sl>sllimit) || (bs<0 && sl<sllimit))
      {
         if (USE_VIRTUAL_STOPS) {
            return(sl);
         }

         Print(
            "Warning: Too short SL requested (",
            DoubleToStr(sl,digits),
            " or ",
            DoubleToStr(MathAbs(sl-askbid)/point,0),
            " points), minimum will be taken (",
            DoubleToStr(sllimit,digits),
            " or ",
            DoubleToStr(MathAbs(askbid-sllimit)/point,0),
            " points)"
         );
         sl=sllimit;

         return(sl);
      }
   }
   
   // align by the ticksize
   double ticksize = MarketInfo(symbol, MODE_TICKSIZE);
   sl = MathRound(sl/ticksize)*ticksize;
   
   return(sl);
}

double AlignTakeProfit(
   string symbol,
   int type,
   double price,
   double tpo=0, // original tp, used when modifying
   double tpl=0,
   double tpp=0,
   bool consider_freezelevel=false
   )
{
   double tp=0;
   
   if (MathAbs(tpl)==EMPTY_VALUE) {tpl=0;}
   if (MathAbs(tpp)==EMPTY_VALUE) {tpp=0;}
   if (tpl==0 && tpp==0) {return(0);} // no tp - return 0
   if (price<=0) {Print("AlignTakeProfit() error: No price entered");return(-1);}

   double point = MarketInfo(symbol,MODE_POINT);
   int digits   = (int)MarketInfo(symbol,MODE_DIGITS);
   tpp=tpp*PipValue(symbol)*point;
   
   //-- buy-sell identifier ---------------------------------------------
   int bs=1;
   if (
      type==OP_BUY
      || type==OP_BUYSTOP
      || type==OP_BUYLIMIT
      )
   {
      bs=1;
   }
   else if (
      type==OP_SELL
      || type==OP_SELLSTOP
      || type==OP_SELLLIMIT
      )
   {
      bs=-1;
   }
   
   //-- prices that will be used ----------------------------------------
   double askbid=price;
   double bidask=price;
   
   if (type==OP_BUY || type==OP_SELL)
   {
      double ask =MarketInfo(symbol,MODE_ASK);
      double bid =MarketInfo(symbol,MODE_BID);
      
      askbid=ask;
      bidask=bid;
      if (bs<0) {
        askbid=bid;
        bidask=ask;
      }
   }
   
   //-- build tp level --------------------------------------------------- 
   if (tpl==0 && tpp!=0) {tpl=price;}

   if (tpl>0) {tp=tpl+tpp*bs;}
   
   if (tp<0) {return(-1);}

   tp=NormalizeDouble(tp,digits);
   tpo = NormalizeDouble(tpo,digits);
   if (tp == tpo) {return tp;}
    
   //-- build limit levels ----------------------------------------------
   double minstops=MarketInfo(symbol,MODE_STOPLEVEL);
   if (consider_freezelevel==true) {
      double freezelevel=MarketInfo(symbol,MODE_FREEZELEVEL);
      if (freezelevel>minstops) {minstops=freezelevel;}
   }
   minstops=NormalizeDouble(minstops*point,digits);
   
   double tplimit=bidask+minstops*bs; // TP min price level
   
   //-- check and align tp, print errors --------------------------------
   //-- do not do it when the stop is the same as the original
   if (tp>0) {
      /*if (tp==askbid)
      {
         tp=0;
      }
      else */
      if ((bs>0 && tp<bidask) || (bs<0 && tp>bidask))
      {
         string abstr="";
         if (bs>0) {abstr="Bid";} else {abstr="Ask";}
         Print(
            "Error: Invalid TP requested (",
            DoubleToStr(tp,digits),
            " for ",abstr," price ",
            askbid,
            ")"
            );
         return(-1);
      }
      else if ((bs>0 && tp<tplimit) || (bs<0 && tp>tplimit))
      {
         if (USE_VIRTUAL_STOPS) {
            return(tp);
         }

         Print(
            "Warning: Too short TP requested (",
            DoubleToStr(tp,digits),
            " or ",
            DoubleToStr(MathAbs(tp-askbid)/point,0),
            " points), minimum will be taken (",
            DoubleToStr(tplimit,digits),
            " or ",
            DoubleToStr(MathAbs(askbid-tplimit)/point,0),
            " points)"
         );
         tp=tplimit;
         return(tp);
      }
   }

   // align by the ticksize
   double ticksize = MarketInfo(symbol, MODE_TICKSIZE);
   tp = MathRound(tp/ticksize)*ticksize;

   return(tp);
}

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 ArrayStrip(T &array[], T value)
{
	int x    = 0;
	int size = ArraySize(array);
	
	for (int i=0; i<size; i++)
	{
		if (array[i] != value)
		{
			array[x] = array[i];
			x++;
		}
	}
	
	if (x < size)
	{
		ArrayResize(array, x);
		
		return true; // stripped
	}
	
	return false; // not stripped
}

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
}

template<typename T>
bool ArrayValue(T &array[], T value)
{
	int size   = ArraySize(array);
	
	if (size > 0)
	{
		if (InArray(array, value))
		{
			// value found -> exit
			return false; // no value added
		}
	}
	
	// value does not exists -> add it
	ArrayResize(array, size+1);
	array[size] = value;
		
	return true; // value added
}

double Bet1326(int group, string symbol, double initial_lots, bool reverse=false)
{  
   int pos=0;
   int total=0;
   double lots=0;
   double profit=0;
   int profit_or_loss=0; // 0 - unknown, 1 - profit, -1 - loss
   
   //-- try to get last lot size from running trades
   total=OrdersTotal();
   for (pos=total-1; pos>=0; pos--)
   {
      if (!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)) {continue;}
      if (OrderMagicNumber() != MagicStart+group) {continue;}
      if (OrderSymbol() != symbol) {continue;}
      if (TimeCurrent() - OrderOpenTime() < 3) {continue;}
		if (OrderExpiration() > 0 && OrderExpiration() <= OrderCloseTime()) {continue;} // no expired po
      
      if (lots==0) {
         lots=OrderLots();
      }
      
      profit = OrderClosePrice()-OrderOpenPrice();
      profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
      if (IsOrderTypeSell()) {profit = -1*profit;}
      if (profit == 0) {
         return(lots);
      }
      
      if (profit<0) {profit_or_loss=-1;}
      else {profit_or_loss=1;}
      
      break;
   }
   
   //-- if no running trade was found, search in history trades
   if (lots==0)
   {
      total=OrdersHistoryTotal();
      for (pos=total-1; pos>=0; pos--)
      {
         if(!OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY)) {continue;}
         if (OrderMagicNumber() != MagicStart+group) {continue;}
         if (OrderSymbol() != symbol) {continue;}
			if (OrderType() > OP_SELL) {continue;} // no po
         
         if (lots==0) {
            lots=OrderLots();
         }
         
         profit = OrderClosePrice()-OrderOpenPrice();
         profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
         if (IsOrderTypeSell()) {profit = -1*profit;}
         if (profit == 0) {
            return(lots);
         }
         
         if (profit<0) {profit_or_loss=-1;}
         else {profit_or_loss=1;}
         
         break;
      }
   }
   
   //--
   if (initial_lots < MarketInfo(symbol,MODE_MINLOT)) {
      initial_lots = MarketInfo(symbol,MODE_MINLOT);  
   }

   if (lots==0) {lots = initial_lots;}
   else
   {
      if ((reverse==false && profit_or_loss==1) || (reverse==true && profit_or_loss==-1))
      {
         double div = lots/initial_lots;
         
         if (div < 1.5) {lots = initial_lots*3;}
         else if (div < 2.5) {lots = initial_lots*6;}
         else if (div < 3.5) {lots = initial_lots*2;}
         else {lots = initial_lots;}
      }
      else {
         lots = initial_lots;
      }
   }
   
   return lots;
}

double BetDalembert(int group, string symbol, double initial_lots, double reverse=false)
{  
   int pos=0;
   int total=0;
   double lots=0;
   double profit=0;
   int profit_or_loss=0; // 0 - unknown, 1 - profit, -1 - loss
   
   //-- try to get last lot size from running trades
   total=OrdersTotal();
   for (pos=total-1; pos>=0; pos--)
   {
      if (!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)) {continue;}
      if (OrderMagicNumber() != MagicStart+group) {continue;}
      if (OrderSymbol() != symbol) {continue;}
      if (TimeCurrent() - OrderOpenTime() < 3) {continue;}
		if (OrderExpiration() > 0 && OrderExpiration() <= OrderCloseTime()) {continue;} // no expired po
      
      if (lots==0) {
         lots=OrderLots();
      }
      
      profit = OrderClosePrice()-OrderOpenPrice();
      profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
      if (IsOrderTypeSell()) {profit = -1*profit;}
      if (profit == 0) {
         return(lots);
      }
      
      if (profit<0) {profit_or_loss=-1;}
      else {profit_or_loss=1;}
      
      break;
   }
   
   //-- if no running trade was found, search in history trades
   if (lots==0)
   {
      total=OrdersHistoryTotal();
      for (pos=total-1; pos>=0; pos--)
      {
         if(!OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY)) {continue;}
         if (OrderMagicNumber() != MagicStart+group) {continue;}
         if (OrderSymbol() != symbol) {continue;}
			if (OrderType() > OP_SELL) {continue;} // no po
         
         if (lots==0) {
            lots=OrderLots();
         }
         
         profit = OrderClosePrice()-OrderOpenPrice();
         profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
         if (IsOrderTypeSell()) {profit = -1*profit;}
         if (profit == 0) {
            return(lots);
         }
         
         if (profit<0) {profit_or_loss=-1;}
         else {profit_or_loss=1;}
         
         break;
      }
   }
   
   //--
   if (initial_lots < MarketInfo(symbol,MODE_MINLOT)) {
      initial_lots = MarketInfo(symbol,MODE_MINLOT);  
   }

   if (lots==0) {lots = initial_lots;}
   else
   {
      if ((reverse==0 && profit_or_loss==1) || (reverse==1 && profit_or_loss==-1))
      {
         lots = lots - initial_lots;
         if (lots < initial_lots) {lots = initial_lots;}
      }
      else {
         lots = lots + initial_lots;
      }
   }
   
   return lots;
}

double BetFibonacci(
   int group,
   string symbol,
   double initial_lots
   )
{
   int pos=0;
   int total=0;
   double lots=0;
   double profit=0;
   int profit_or_loss=0; // 0 - unknown, 1 - profit, -1 - loss
   
   //-- try to get last lot size from running trades
   total=OrdersTotal();
   for (pos=total-1; pos>=0; pos--)
   {
      if (!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)) {continue;}
      if (OrderMagicNumber() != MagicStart+group) {continue;}
      if (OrderSymbol() != symbol) {continue;}
      if (TimeCurrent() - OrderOpenTime() < 3) {continue;}
		if (OrderExpiration() > 0 && OrderExpiration() <= OrderCloseTime()) {continue;} // no expired po
      
      if (lots==0) {
         lots=OrderLots();
      }
      
      profit = OrderClosePrice()-OrderOpenPrice();
      profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
      if (IsOrderTypeSell()) {profit = -1*profit;}
      if (profit == 0) {
         return(lots);
      }
      
      if (profit<0) {profit_or_loss=-1;}
      else {profit_or_loss=1;}
      
      break;
   }
   
   //-- if no running trade was found, search in history trades
   if (lots==0)
   {
      total=OrdersHistoryTotal();
      for (pos=total-1; pos>=0; pos--)
      {
         if(!OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY)) {continue;}
         if (OrderMagicNumber() != MagicStart+group) {continue;}
         if (OrderSymbol() != symbol) {continue;}
			if (OrderType() > OP_SELL) {continue;} // no po
         
         if (lots==0) {
            lots=OrderLots();
         }
         
         profit = OrderClosePrice()-OrderOpenPrice();
         profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
         if (IsOrderTypeSell()) {profit = -1*profit;}
         if (profit == 0) {
            return(lots);
         }
         
         if (profit<0) {profit_or_loss=-1;}
         else {profit_or_loss=1;}
         
         break;
      }
   }

   //--
   if (initial_lots < MarketInfo(symbol,MODE_MINLOT)) {
      initial_lots = MarketInfo(symbol,MODE_MINLOT);  
   }

   if (lots==0) {lots = initial_lots;}
   else
   {  
      int fibo1=1, fibo2=0, fibo3=0, fibo4=0;
      double div = lots/initial_lots;
      
      if (div<=0) {div=1;}

      while(true)
      {
         fibo1=fibo1+fibo2;
         fibo3=fibo2;
         fibo2=fibo1-fibo2;
         fibo4=fibo2-fibo3;
         if (fibo1 > NormalizeDouble(div, 2)) {break;}
      }
      //Print("("+fibo1 + "+" + fibo2+"+"+fibo3+") > "+div);
      if (profit_or_loss==1)
      {
         if (fibo4<=0) {fibo4=1;}
         //Print("Profit "+lots+"*"+fibo4);
         lots=initial_lots*(fibo4);
      }
      else {
         //Print("Loss "+lots+"*"+fibo1+"+"+fibo2);
         lots=initial_lots*(fibo1);
      }
   }
   
   lots=NormalizeDouble(lots, 2);
   return lots;
}

double BetLabouchere(int group, string symbol, double initial_lots, string list_of_numbers, double reverse=false)
{   
   int pos=0;
   int total=0;
   double lots=0;
   double profit=0;
   int profit_or_loss=0; // 0 - unknown, 1 - profit, -1 - loss
   
   //-- try to get last lot size from running trades
   total=OrdersTotal();
   for (pos=total-1; pos>=0; pos--)
   {
      if (!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)) {continue;}
      if (OrderMagicNumber() != MagicStart+group) {continue;}
      if (OrderSymbol() != symbol) {continue;}
      if (TimeCurrent() - OrderOpenTime() < 3) {continue;}
		if (OrderExpiration() > 0 && OrderExpiration() <= OrderCloseTime()) {continue;} // no expired po
      
      if (lots==0) {
         lots=OrderLots();
      }
      
      profit = OrderClosePrice()-OrderOpenPrice();
      profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
      if (IsOrderTypeSell()) {profit = -1*profit;}
      if (profit == 0) {
         return(lots);
      }
      
      if (profit<0) {profit_or_loss=-1;}
      else {profit_or_loss=1;}
      
      break;
   }
   
   //-- if no running trade was found, search in history trades
   if (lots==0)
   {
      total=OrdersHistoryTotal();
      for (pos=total-1; pos>=0; pos--)
      {
         if(!OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY)) {continue;}
         if (OrderMagicNumber() != MagicStart+group) {continue;}
         if (OrderSymbol() != symbol) {continue;}
			if (OrderType() > OP_SELL) {continue;} // no po
         
         if (lots==0) {
            lots=OrderLots();
         }
         
         profit = OrderClosePrice()-OrderOpenPrice();
         profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
         if (IsOrderTypeSell()) {profit = -1*profit;}
         if (profit == 0) {
            return(lots);
         }

         if (profit<0) {profit_or_loss=-1;}
         else {profit_or_loss=1;}
         
         break;
      }
   }
   
   //-- Labouchere stuff
   static int mem_group[];
   static string mem_list[];
   static int mem_ticket[];
   int start_again=false;
   
   //- get the list of numbers as it is stored in the memory, or store it
   int id=ArraySearch(mem_group, group);
   if (id == -1) {
      start_again=true;
      if (list_of_numbers=="") {list_of_numbers="1";}
      id = ArraySize(mem_group);
      ArrayResize(mem_group, id+1, id+1);
      ArrayResize(mem_list, id+1, id+1);
      ArrayResize(mem_ticket, id+1, id+1);
      mem_group[id]=group;
      mem_list[id]=list_of_numbers;
   }
   
   if (mem_ticket[id]==OrderTicket()) {
      // the last known ticket (mem_ticket[id]) should be different than OderTicket() normally
      // when failed to create a new trade - the last ticket remains the same
      // so we need to reset
      mem_list[id]=list_of_numbers;
   }
   mem_ticket[id]=OrderTicket();
   
   //- now turn the string into integer array
   int list[];
   string listS[];
   StringExplode(",", mem_list[id], listS);
   ArrayResize(list, ArraySize(listS));
   for (int s=0; s<ArraySize(listS); s++) {
      list[s]=(int)StringToInteger(StringTrim(listS[s]));  
   }

   //-- 
   int size = ArraySize(list);

   if (initial_lots < MarketInfo(symbol,MODE_MINLOT)) {
      initial_lots = MarketInfo(symbol,MODE_MINLOT);  
   }

   if (lots==0) {
      start_again=true;
   }
   
   if (start_again==true)
   {
      if (size==1) {
         lots = initial_lots*list[0];
      } else {
         lots = initial_lots*(list[0]+list[size-1]);
      }
   }
   else 
   {
      if ((reverse==0 && profit_or_loss==1) || (reverse==1 && profit_or_loss==-1))
      {
         if (size==1) {
            lots=initial_lots*list[0];
            ArrayResize(list, 0);
         }
         else if (size==2) {
            lots = initial_lots*(list[0]+list[1]);
            ArrayResize(list, 0);
         }
         else if (size>2) {
            lots = initial_lots*(list[0]+list[size-1]);
            // Cancel first and last numbers in our list
            // shift array 1 step left
            for(pos=0; pos<size-1; pos++) {
               list[pos]=list[pos+1];
            }
            ArrayResize(list,ArraySize(list)-2); // remove last 2 elements		
         }
         if (lots < initial_lots) {lots = initial_lots;}
      }
      else {
         if (size>1)
         {
            ArrayResize(list, size+1);
            list[size]=list[0]+list[size-1];
            lots = initial_lots*(list[0]+list[size]);
         } else {
            lots = initial_lots*list[0];
         }
         if (lots < initial_lots) {lots = initial_lots;}
      }

   }
   
   Print("Labouchere (for group "+(string)id+") current list of numbers:"+StringImplode(",", list));
   size=ArraySize(list);
   if (size==0) {
      ArrayStripKey(mem_group, id);
      ArrayStripKey(mem_list, id);
      ArrayStripKey(mem_ticket, id);
   } else {
      mem_list[id]=StringImplode(",", list);
   }

   return lots;
}

double BetMartingale(
   int group,
   string symbol,
   double initial_lots,
   double multiply_on_loss,
   double multiply_on_profit,
   double add_on_loss,
   double add_on_profit,
   int reset_on_loss,
   int reset_on_profit
   )
{
   int pos=0;
	int total=0;
   double lots=0;
   double profit=0;
   int profit_or_loss=0; // 0 - unknown, 1 - profit, -1 - loss
   int in_a_row=0;
   
   //-- try to get last lot size from running trades
   total=OrdersTotal();
   for (pos=total-1; pos>=0; pos--)
   {
      if (!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)) {continue;}
      if (OrderMagicNumber() != MagicStart+group) {continue;}
      if (OrderSymbol() != symbol) {continue;}
      if (TimeCurrent() - OrderOpenTime() < 3) {continue;}
		if (OrderExpiration() > 0 && OrderExpiration() <= OrderCloseTime()) {continue;} // no expired po

      if (lots==0) {
         lots=OrderLots();
      }
      
      profit = OrderClosePrice()-OrderOpenPrice();
      profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
      if (IsOrderTypeSell()) {profit = -1*profit;}
      if (profit == 0) {
         return(lots);
      }
      
      if (profit_or_loss == 0)
      {
         
         if (profit<0) {profit_or_loss=-1;}
         else {profit_or_loss=1;}
      }
      else {
              if (profit_or_loss==1 && profit<0) {break;}
         else if (profit_or_loss==-1 && profit>=0) {break;}
      }
      
      in_a_row++;
   }
   
   //-- if no running trade was found, search in history trades
   if (lots==0)
   {
      total=OrdersHistoryTotal();
      for (pos=total-1; pos>=0; pos--)
      {
         if(!OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY)) {continue;}
         if (OrderMagicNumber() != MagicStart+group) {continue;}
         if (OrderSymbol() != symbol) {continue;}
			if (OrderType() > OP_SELL) {continue;} // no po
         
         if (lots==0) {
            lots=OrderLots();
         }
         
         profit = OrderClosePrice()-OrderOpenPrice();
         profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
         if (IsOrderTypeSell()) {profit = -1*profit;}
         if (profit == 0) {
            return(lots);
         }
         
         if (profit_or_loss == 0)
         {
            if (profit<0) {profit_or_loss=-1;}
            else {profit_or_loss=1;}
         }
         else {
                 if (profit_or_loss==1 && profit<0) {break;}
            else if (profit_or_loss==-1 && profit>=0) {break;}
         }
         
         in_a_row++;
      }
   }

   //--
   /*
   if (initial_lots < MarketInfo(symbol,MODE_MINLOT)) {
      initial_lots = MarketInfo(symbol,MODE_MINLOT);  
   }*/

   if (lots==0) {lots = initial_lots;}
   else {
      if (profit_or_loss==1)
      {
         if (reset_on_profit>0 && in_a_row >= reset_on_profit) {
            lots=initial_lots;  
         }
         else {
            if (multiply_on_profit<=0) {multiply_on_profit=1;}
            lots=(lots*multiply_on_profit) + add_on_profit;
         }
      }
      else {
         if (reset_on_loss>0 && in_a_row >= reset_on_loss) {
            lots=initial_lots;  
         }
         else {
            if (multiply_on_loss<=0) {multiply_on_loss=1;}
            lots=(lots*multiply_on_loss) + add_on_loss;
         }
      }
   }
   
   return lots;
}

double BetSequence(int group, string symbol, double initial_lots, string sequence_on_loss, string sequence_on_profit, bool reverse=false)
{  
   int pos=0;
   int total=0;
   double lots=0;
   int size=0;
   double profit=0;
   int profit_or_loss=0; // 0 - unknown, 1 - profit, -1 - loss
   
   //-- try to get last lot size from running trades
   total=OrdersTotal();
   for (pos=total-1; pos>=0; pos--)
   {
      if (!OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)) {continue;}
      if (OrderMagicNumber() != MagicStart+group) {continue;}
      if (OrderSymbol() != symbol) {continue;}
      if (TimeCurrent() - OrderOpenTime() < 3) {continue;}
		if (OrderExpiration() > 0 && OrderExpiration() <= OrderCloseTime()) {continue;} // no expired po
      
      if (lots==0) {
         lots=OrderLots();
      }
      
      profit = OrderClosePrice()-OrderOpenPrice();
      profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
      if (IsOrderTypeSell()) {profit = -1*profit;}
      if (profit == 0) {
         return(lots);
      }
      
      if (profit<0) {profit_or_loss=-1;}
      else {profit_or_loss=1;}
      
      break;
   }
   
   //-- if no running trade was found, search in history trades
   if (lots==0)
   {
      total=OrdersHistoryTotal();
      for (pos=total-1; pos>=0; pos--)
      {
         if(!OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY)) {continue;}
         if (OrderMagicNumber() != MagicStart+group) {continue;}
         if (OrderSymbol() != symbol) {continue;}
			if (OrderType() > OP_SELL) {continue;} // no po
         
         if (lots==0) {
            lots=OrderLots();
         }
         
         profit = OrderClosePrice()-OrderOpenPrice();
         profit = NormalizeDouble(profit, SymbolDigits(OrderSymbol()));
         if (IsOrderTypeSell()) {profit = -1*profit;}
         if (profit == 0) {
            return(lots);
         }
         
         if (profit<0) {profit_or_loss=-1;}
         else {profit_or_loss=1;}
         
         break;
      }
   }
   
   //-- Sequence stuff
   static int mem_group[];
   static string mem_list_loss[];
   static string mem_list_profit[];
   static int mem_ticket[];
   
   //- get the list of numbers as it is stored in the memory, or store it
   int id=ArraySearch(mem_group, group);
   if (id == -1)
   {
      if (sequence_on_loss=="") {sequence_on_loss="1";}
      if (sequence_on_profit=="") {sequence_on_profit="1";}
      id = ArraySize(mem_group);
      ArrayResize(mem_group, id+1, id+1);
      ArrayResize(mem_list_loss, id+1, id+1);
      ArrayResize(mem_list_profit, id+1, id+1);
      ArrayResize(mem_ticket, id+1, id+1);
      mem_group[id]        =group;
      mem_list_loss[id]    =sequence_on_loss;
      mem_list_profit[id]  =sequence_on_profit;
   }
   
   bool loss_reset=false;
   bool profit_reset=false;
   if (profit_or_loss==-1 && mem_list_loss[id]=="") {
      loss_reset=true;
      mem_list_profit[id]="";
   }
   if (profit_or_loss==1 && mem_list_profit[id]=="") {
      profit_reset=true;
      mem_list_loss[id]="";
   }
   
   if (profit_or_loss==1 || mem_list_loss[id]=="") {
      mem_list_loss[id]=sequence_on_loss;
      if (loss_reset) {mem_list_loss[id]="1,"+mem_list_loss[id];}
      
   }
   if (profit_or_loss==-1 ||mem_list_profit[id]=="") {
      mem_list_profit[id]=sequence_on_profit;
      if (profit_reset) {mem_list_profit[id]="1,"+mem_list_profit[id];}
   }
   
   if (mem_ticket[id]==OrderTicket()) {
      // the last known ticket (mem_ticket[id]) should be different than OderTicket() normally
      // when failed to create a new trade - the last ticket remains the same
      // so we need to reset
      mem_list_loss[id]=sequence_on_loss;
      mem_list_profit[id]=sequence_on_profit;
   }
   mem_ticket[id]=OrderTicket();
   
   //- now turn the string into integer array
   int s=0;
   double list_loss[];
   double list_profit[];
   string listS[];
   StringExplode(",", mem_list_loss[id], listS);
   ArrayResize(list_loss, ArraySize(listS), ArraySize(listS));
   for (s=0; s<ArraySize(listS); s++) {
      list_loss[s]=(double)StringToDouble(StringTrim(listS[s]));  
   }
   StringExplode(",", mem_list_profit[id], listS);
   ArrayResize(list_profit, ArraySize(listS), ArraySize(listS));
   for (s=0; s<ArraySize(listS); s++) {
      list_profit[s]=(double)StringToDouble(StringTrim(listS[s]));  
   }

   //--
   if (initial_lots < MarketInfo(symbol,MODE_MINLOT)) {
      initial_lots = MarketInfo(symbol,MODE_MINLOT);  
   }

   if (lots==0) {lots = initial_lots;}
   else
   {
      if ((reverse==false && profit_or_loss==1) || (reverse==true && profit_or_loss==-1))
      {
         lots = initial_lots*list_profit[0];
         // shift array 1 step left
         size=ArraySize(list_profit);
         for(pos=0; pos<size-1; pos++) {
            list_profit[pos]=list_profit[pos+1];
         }
         if (size>0) {
            ArrayResize(list_profit, size-1, size-1);
            mem_list_profit[id]=StringImplode(",", list_profit);
         }
         // reset the opposite sequence
         //mem_list_loss[id]="";
      }
      else {
         
         lots = initial_lots*list_loss[0];
         // shift array 1 step left
         size=ArraySize(list_loss);
         for(pos=0; pos<size-1; pos++) {
            list_loss[pos]=list_loss[pos+1];
         }
         if (size>0) {
            ArrayResize(list_loss, size-1, size-1);
            mem_list_loss[id]=StringImplode(",", list_loss);
         }
         // reset the opposite sequence
         //mem_list_profit[id]="";
      }
   }
   
   return lots;
}

int BuyNow(
   string symbol,
   double lots,
   double sll,
   double tpl,
   double slp,
   double tpp,
   double slippage=0,
   int magic=0,
   string comment="",
   color arrowcolor=CLR_NONE,
   datetime expiration = 0
   )
{
   int ticket=OrderCreate(
      symbol,
      OP_BUY,
      lots,
      0,
      sll,
      tpl,
      slp,
      tpp,
      slippage,
      magic,
      comment,
      arrowcolor,
      expiration
      );
   return(ticket);
}

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);
}

double DynamicLots(string mode="balance", double value=0, double sl=0, string align="align", double RJFR_initial_lots=0)
{
   double size=0;
   string symbol=GetSymbol();
   double LotStep=MarketInfo(symbol,MODE_LOTSTEP);
   double LotSize=MarketInfo(symbol,MODE_LOTSIZE);
   double MinLots=MarketInfo(symbol,MODE_MINLOT);
   double MaxLots=MarketInfo(symbol,MODE_MAXLOT);
   double TickValue=MarketInfo(symbol,MODE_TICKVALUE);
   double point=MarketInfo(symbol,MODE_POINT);
   double ticksize=MarketInfo(symbol,MODE_TICKSIZE);
   double margin_required=MarketInfo(symbol,MODE_MARGINREQUIRED);
   
   if (mode=="fixed" || mode=="lots")     {size=value;}
   else if (mode=="block-equity")      {size=(value/100)*AccountEquity()/margin_required;}
   else if (mode=="block-balance")     {size=(value/100)*AccountBalance()/margin_required;}
   else if (mode=="block-freemargin")  {size=(value/100)*AccountFreeMargin()/margin_required;}
   else if (mode=="equity")      {size=(value/100)*AccountEquity()/(LotSize*TickValue);}
   else if (mode=="balance")     {size=(value/100)*AccountBalance()/(LotSize*TickValue);}
   else if (mode=="freemargin")  {size=(value/100)*AccountFreeMargin()/(LotSize*TickValue);}
   else if (mode=="equityRisk")     {size=((value/100)*AccountEquity())/(sl*((TickValue/ticksize)*point)*PipValue(symbol));}
   else if (mode=="balanceRisk")    {size=((value/100)*AccountBalance())/(sl*((TickValue/ticksize)*point)*PipValue(symbol));}
   else if (mode=="freemarginRisk") {size=((value/100)*AccountFreeMargin())/(sl*((TickValue/ticksize)*point)*PipValue(symbol));}
   else if (mode=="fixedRisk")   {size=(value)/(sl*((TickValue/ticksize)*point)*PipValue(symbol));}
   else if (mode=="fixedRatio" || mode=="RJFR") {
      
      /////
      // Ryan Jones Fixed Ratio MM static data
      static double RJFR_start_lots=0;
      static double RJFR_delta=0;
      static double RJFR_units=1;
      static double RJFR_target_lower=0;
      static double RJFR_target_upper=0;
      /////
      
      if (RJFR_start_lots<=0) {RJFR_start_lots=value;}
      if (RJFR_start_lots<MinLots) {RJFR_start_lots=MinLots;}
      if (RJFR_delta<=0) {RJFR_delta=sl;}
      if (RJFR_target_upper<=0) {
         RJFR_target_upper=AccountEquity()+(RJFR_units*RJFR_delta);
         Print("Fixed Ratio MM: Units=>",RJFR_units,"; Delta=",RJFR_delta,"; Upper Target Equity=>",RJFR_target_upper);
      }
      if (AccountEquity()>=RJFR_target_upper)
      {
         while(true) {
            Print("Fixed Ratio MM going up to ",(RJFR_start_lots*(RJFR_units+1))," lots: Equity is above Upper Target Equity (",AccountEquity(),">=",RJFR_target_upper,")");
            RJFR_units++;
            RJFR_target_lower=RJFR_target_upper;
            RJFR_target_upper=RJFR_target_upper+(RJFR_units*RJFR_delta);
            Print("Fixed Ratio MM: Units=>",RJFR_units,"; Delta=",RJFR_delta,"; Lower Target Equity=>",RJFR_target_lower,"; Upper Target Equity=>",RJFR_target_upper);
            if (AccountEquity()<RJFR_target_upper) {break;}
         }
      }
      else if (AccountEquity()<=RJFR_target_lower)
      {
         while(true) {
         if (AccountEquity()>RJFR_target_lower) {break;}
            if (RJFR_units>1) {         
               Print("Fixed Ratio MM going down to ",(RJFR_start_lots*(RJFR_units-1))," lots: Equity is below Lower Target Equity | ", AccountEquity()," <= ",RJFR_target_lower,")");
               RJFR_target_upper=RJFR_target_lower;
               RJFR_target_lower=RJFR_target_lower-((RJFR_units-1)*RJFR_delta);
               RJFR_units--;
               Print("Fixed Ratio MM: Units=>",RJFR_units,"; Delta=",RJFR_delta,"; Lower Target Equity=>",RJFR_target_lower,"; Upper Target Equity=>",RJFR_target_upper);
            } else {break;}
         }
      }
      size=RJFR_start_lots*RJFR_units;
   }
   
	if (size==EMPTY_VALUE) {size=0;}
	
   size=MathRound(size/LotStep)*LotStep;
   
   static bool alert_min_lots=false;
   if (size<MinLots && alert_min_lots==false) {
      alert_min_lots=true;
      Alert("You want to trade ",size," lot, but your broker's minimum is ",MinLots," lot. The trade/order will continue with ",MinLots," lot instead of ",size," lot. The same rule will be applied for next trades/orders with desired lot size lower than the minimum. You will not see this message again until you restart the program.");
   }
   
   if (align=="align") {
      if (size<MinLots) {size=MinLots;}
      if (size>MaxLots) {size=MaxLots;}
   }
   
   return (size);
}

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));
         }
      }
   }
}

datetime ExpirationTime(string mode="GTC",int days=0, int hours=0, int minutes=0, datetime custom=0)
{
	datetime expiration=TimeCurrent();
   if (mode=="GTC" || mode=="")   {expiration=0;}
   else if (mode=="today") {expiration=StrToTime((string)TimeYear(TimeCurrent())+"."+(string)TimeMonth(TimeCurrent())+"."+(string)TimeDay(TimeCurrent()))+86400;}
   else if (mode=="specified") {
      expiration=0;
      if ((days + hours + minutes)>0) {
         expiration=TimeCurrent()+(86400*days)+(3600*hours)+(60*minutes);
      }
   }
   else
   {
      if (custom <= TimeCurrent()) {
         if (custom < 31557600) {
            custom = TimeCurrent()+custom;
         }
         else {
            custom=0;
         }
      }
      expiration = custom;
   }
   return(expiration);
}

bool FeedStatistics(){GetStatistics();return(0);}

bool FilterOrderBy(string group_mode="all", string group="0", string market_mode="all", string market="", string BuysOrSells="both", string LimitsOrStops="both", int TradesOrders=0, bool onTrade = false)
{
	// TradesOrders=0 - trades only
	// TradesOrders=1 - orders only
	// TradesOrders=2 - trades and orders

	//-- db
	static string markets[];
	static string market0   = "-";
	static int markets_size = 0;
	
	static string groups[];
	static string group0   = "-";
	static int groups_size = 0;
	
	//-- local variables
	bool type_pass   = false;
	bool market_pass = false;
	bool group_pass  = false;
	
	int i, type, magic_number;
	string symbol;
	
	// Trades
	if (onTrade == false) {
		type = OrderType();
		magic_number = OrderMagicNumber();
		symbol = OrderSymbol();
	}
	else {
		type = e_attrType();
		magic_number = e_attrMagicNumber();
		symbol = e_attrSymbol();
	}
	
	if (TradesOrders==0)
	{
		if (
				(BuysOrSells=="both"  && (type==OP_BUY || type==OP_SELL))
			|| (BuysOrSells=="buys"  && type==OP_BUY)
			|| (BuysOrSells=="sells" && type==OP_SELL)
			
			)
		{
			type_pass = true;
		}
	}
	// Pending orders
	else if (TradesOrders==1)
	{
		if (
				(BuysOrSells=="both" && (type==OP_BUYLIMIT || type==OP_BUYSTOP || type==OP_SELLLIMIT || type==OP_SELLSTOP))
			||	(BuysOrSells=="buys" && (type==OP_BUYLIMIT || type==OP_BUYSTOP))
			|| (BuysOrSells=="sells" && (type==OP_SELLLIMIT || type==OP_SELLSTOP))
			)
		{
			if (
					(LimitsOrStops=="both" && (type==OP_BUYSTOP || type==OP_SELLSTOP || type==OP_BUYLIMIT || type==OP_SELLLIMIT))
				||	(LimitsOrStops=="stops" && (type==OP_BUYSTOP || type==OP_SELLSTOP))
				|| (LimitsOrStops=="limits" && (type==OP_BUYLIMIT || type==OP_SELLLIMIT))					
				)
			{
				type_pass = true;
			}
		}
	}
	//-- Trades and orders --------------------------------------------
	else
	{
		if (
				(BuysOrSells=="both")
			|| (BuysOrSells=="buys"  && (type==OP_BUY || type==OP_BUYLIMIT || type==OP_BUYSTOP))
			|| (BuysOrSells=="sells" && (type==OP_SELL || type==OP_SELLLIMIT || type==OP_SELLSTOP))
			)
		{
			type_pass = true;
		}
	}
	if (type_pass == false) {return false;}

	//-- check group
	if (group_mode=="group")
	{
		if (group == "")
		{
			if (magic_number==MagicStart)
   		{
   			group_pass=true;
   		}
	   }
	   else
	   {
			if (group0!=group)
			{
				group0=group;
				StringExplode(",",group,groups);
				groups_size = ArraySize(groups);
				for(i=0; i<groups_size; i++)
				{
					groups[i]=StringTrimRight(groups[i]);
					groups[i]=StringTrimLeft(groups[i]);
					if (groups[i]=="") {groups[i]="0";}
				}
			}
		
			for(i=0; i<groups_size; i++)
			{
				if (magic_number==(MagicStart+(int)groups[i]))
				{
					group_pass=true;
					break;
				}
			}
		}
	}
	else if (group_mode=="all" || (group_mode=="manual" && magic_number==0)) {
		group_pass = true;  
	}
	if (group_pass == false) {return false;}
	
	// check market
	if (market_mode=="all") {
		market_pass=true;
	}
	else
	{
		if (symbol == market)
	   {
	      market_pass = true;
	   }
      else
      {
			if (market0!=market)
			{
				market0=market;
				if (market=="")
				{
					markets_size = 1;
					ArrayResize(markets,1);
					markets[0]=Symbol();
				}
				else
				{
					StringExplode(",", market,markets);
					markets_size = ArraySize(markets);
					for(i=0; i<markets_size; i++)
					{
						markets[i]=StringTrimRight(markets[i]);
						markets[i]=StringTrimLeft(markets[i]);
						if (markets[i]=="") {markets[i]=Symbol();}
					}
				}
			}
			for(i=0; i<markets_size; i++) {
				if (symbol==markets[i]) {
					market_pass = true;
					break;
				}
			}
		}
	}
	if (market_pass == false) {return false;}
	
	return true;
}

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);
}

/******************************************************/
/* Checks if element exist in array                   */
/* Returns "true" if exists and "false" if not exists */
/******************************************************/
bool InArray(double &array[], double value)
{
	int index = ArrayBsearch(array, value);
	return (index > -1 && array[index] == value);
}
bool InArray(float &array[], float value)
{
	int index = ArrayBsearch(array, value);
	return (index > -1 && array[index] == value);
}
bool InArray(long &array[], long value)
{
	int index = ArrayBsearch(array, value);
	return (index > -1 && array[index] == value);
}
bool InArray(int &array[], int value)
{
	int index = ArrayBsearch(array, value);
	return (index > -1 && array[index] == value);
}
bool InArray(short &array[], short value)
{
	int index = ArrayBsearch(array, value);
	return (index > -1 && array[index] == value);
}
bool InArray(char &array[], char value)
{
	int index = ArrayBsearch(array, value);
	return (index > -1 && array[index] == value);
}
bool InArray(string &array[], string value)
{
	int index = -1;
   int size = ArraySize(array);

   if (size > 0)
   {
      for (int i=0; i<size; i++)
      {
         if (array[i] == value) {
            return true;
         }
      }
   }
	return false;
}

bool IsOrderTypeSell() {
   if (
   OrderType()==OP_SELL
   ||
   OrderType()==OP_SELLSTOP
   ||
   OrderType()==OP_SELLLIMIT
   ) {return(true);}
   return(false);
}

bool ModifyOrder(
   int ticket,
   double op,
   double sll=0,
   double tpl=0,
   double slp=0,
   double tpp=0,
   datetime exp=0,
   color clr=CLR_NONE,
   bool ontrade_event=true)
{
//-----------------------------------------------------------------------
   int bs=1;
   if (
         OrderType()==OP_SELL
      || OrderType()==OP_SELLSTOP
      || OrderType()==OP_SELLLIMIT
      )
   {bs=-1;} // Positive when Buy, negative when Sell

   while(true)
   {
      uint time0 = GetTickCount();
      
      WaitTradeContextIfBusy();
      
      if (!OrderSelect(ticket,SELECT_BY_TICKET)) {
         return(false);
      }
      
      string symbol      = OrderSymbol();
      int type           = OrderType();
      double ask         = SymbolInfoDouble(symbol, SYMBOL_ASK);
      double bid         = SymbolInfoDouble(symbol, SYMBOL_BID);
      int digits         = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
      double point       = SymbolInfoDouble(symbol, SYMBOL_POINT);
		double stoplevel   = point * SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL);
		double freezelevel = point * SymbolInfoInteger(symbol, SYMBOL_TRADE_FREEZE_LEVEL);
      
      if (OrderType()<2) {op=OrderOpenPrice();} else {op=NormalizeDouble(op,digits);}
      
		sll = NormalizeDouble(sll, digits);
      tpl = NormalizeDouble(tpl, digits);
      
		if (op<0 || op>=EMPTY_VALUE)  {break;}
      if (sll<0) {break;}
      if (slp<0) {break;}
      if (tpl<0) {break;}
      if (tpp<0) {break;}
		
		//-- OP -----------------------------------------------------------
		// https://book.mql4.com/appendix/limits
		if (type == OP_BUYLIMIT) {
			if (ask - op < stoplevel) {op = ask - stoplevel;}
			if (ask - op <= freezelevel) {op = ask - freezelevel - point;}
		}
		else if (type == OP_BUYSTOP) {
			if (op - ask < stoplevel) {op = ask + stoplevel;}
			if (op - ask <= freezelevel) {op = ask + freezelevel + point;}
		}
		else if (type == OP_SELLLIMIT) {
			if (op - bid < stoplevel) {op = bid + stoplevel;}
			if (op - bid <= freezelevel) {op = bid + freezelevel + point;}
		}
		else if (type == OP_SELLSTOP) {
			if (bid - op < stoplevel) {op = bid - stoplevel;}
			if (bid - op < freezelevel) {op = bid - freezelevel - point;}
		}
		op = NormalizeDouble(op, digits);
      
      //-- SL and TP ----------------------------------------------------
      double sl=0, tp=0, vsl=0, vtp=0;
      sl = AlignStopLoss(symbol, type, op, attrStopLoss(), sll, slp);
      if (sl < 0) {break;}
      tp = AlignTakeProfit(symbol, type, op, attrTakeProfit(), tpl, tpp);
      if (tp < 0) {break;}
      
      if (USE_VIRTUAL_STOPS)
      {
         //-- virtual SL and TP --------------------------------------------
         vsl = sl;
         vtp = tp;
         sl = 0;
			tp = 0;
      
         double askbid = ask;
         if (bs < 0) {askbid = bid;}
         
         if (vsl>0 || USE_EMERGENCY_STOPS=="always") {
            if (EMERGENCY_STOPS_REL>0 || EMERGENCY_STOPS_ADD>0)
            {
               sl=vsl-EMERGENCY_STOPS_REL*MathAbs(askbid-vsl)*bs;
               if (sl<=0) {sl=askbid;}
               sl=sl-toDigits(EMERGENCY_STOPS_ADD,symbol)*bs;
            }
         }
         if (vtp>0 || USE_EMERGENCY_STOPS=="always") {
            if (EMERGENCY_STOPS_REL>0 || EMERGENCY_STOPS_ADD>0)
            {
               tp=vtp+EMERGENCY_STOPS_REL*MathAbs(vtp-askbid)*bs;
               if (tp<=0) {tp=askbid;}
               tp=tp+toDigits(EMERGENCY_STOPS_ADD,symbol)*bs;
            }
         }
         vsl=NormalizeDouble(vsl,digits);
         vtp=NormalizeDouble(vtp,digits);
      }
      sl=NormalizeDouble(sl,digits);
      tp=NormalizeDouble(tp,digits);

      //-- modify -------------------------------------------------------
		ResetLastError();
		
      if (USE_VIRTUAL_STOPS) {
         if (vsl!=attrStopLoss() || vtp!=attrTakeProfit()) {
            VirtualStopsDriver("set", ticket, vsl, vtp, toPips(MathAbs(op-vsl), symbol), toPips(MathAbs(vtp-op), symbol));
         }
      }

      bool success=false;
      
      if (
            (OrderType()>1 && op!=NormalizeDouble(OrderOpenPrice(),digits))
         || sl!=NormalizeDouble(OrderStopLoss(),digits)
         || tp!=NormalizeDouble(OrderTakeProfit(),digits)
         || exp!=OrderExpiration()
      ) {
         success=OrderModify(ticket,op,sl,tp,exp,clr);
      }
         
      //-- error check --------------------------------------------------
      int erraction=CheckForTradingError(GetLastError(), "Modify error");
      switch(erraction)
      {
         case 0: break;    // no error
         case 1: continue; // overcomable error
         case 2: break;    // fatal error
      }
      
      //-- finish work --------------------------------------------------
      if (success==true) {
         if (!IsTesting() && !IsVisualMode()) Print("Operation details: Speed "+(string)(GetTickCount()-time0)+" ms");
         if (ontrade_event == true)
         {
            OrderModified(ticket);
            RegisterEvent("trade");
         }
         if (OrderSelect(ticket,SELECT_BY_TICKET)) {}
         return(true);
      }
      
      break;
   }

   return(false);
}

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);
}

int OrderCreate(
   string symbol="",
   int    type=OP_BUY,
   double lots=0,
   double op=0,
   double sll=0, // SL level
   double tpl=0, // TO level
   double slp=0, // SL adjust in points
   double tpp=0, // TP adjust in points
   double slippage=0,
   int    magic=0,
   string comment="",
   color  arrowcolor=CLR_NONE,
   datetime expiration=0,
   bool oco = false
   )
{
   uint time0=GetTickCount();
   
   int ticket=-1;
   int bs=1;
   if (
         type==OP_SELL
      || type==OP_SELLSTOP
      || type==OP_SELLLIMIT
      ) {bs=-1;} // Positive when Buy, negative when Sell
   
   if (symbol=="") {symbol=Symbol();}

   lots=AlignLots(lots);
   
   int digits = 0;
   double ask=0, bid=0, point=0, ticksize=0;
   double sl=0, tp=0;
	double vsl=0, vtp=0;

   //-- attempt to send trade/order -------------------------------------
   while(!IsStopped())
   {
      //Print(sll+" "+tpl+" "+slp+" "+tpp);
      WaitTradeContextIfBusy();
      
      static bool not_allowed_message = false;
      if (!MQLInfoInteger(MQL_TESTER) && !MarketInfo(symbol, MODE_TRADEALLOWED)) {
         if (not_allowed_message == false) {
            Print("Market ("+symbol+") is closed");
         }
         not_allowed_message = true;
         return(false);
      }
      not_allowed_message = false;
      
      digits  = (int)MarketInfo(symbol,MODE_DIGITS);
      ask     = MarketInfo(symbol,MODE_ASK);
      bid     = MarketInfo(symbol,MODE_BID);
      point   = MarketInfo(symbol,MODE_POINT);
      ticksize= MarketInfo(symbol, MODE_TICKSIZE);
      
      //- not enough money check: fix maximum possible lot by margin required, or quit
      if (type==OP_BUY || type==OP_SELL)
      {
         double LotStep          = MarketInfo(symbol,MODE_LOTSTEP);
         double MinLots          = MarketInfo(symbol,MODE_MINLOT);
         double margin_required  = MarketInfo(symbol,MODE_MARGINREQUIRED);
         static bool not_enough_message = false;
         
         if (margin_required != 0)
         {
            double max_size_by_margin = AccountFreeMargin()/margin_required;
         
            if (lots > max_size_by_margin) {
               double size_old = lots;
               lots = max_size_by_margin;
               if (lots<MinLots)
               {
                  if (not_enough_message==false) {
                     Print("Not enough money to trade :( The robot is still working, waiting for some funds to appear...");
                  }
                  not_enough_message = true;
                  return(false);
               }
               else
               {
                  lots = MathFloor(lots/LotStep)*LotStep;
                  Print("Not enough money to trade "+DoubleToString(size_old, 2)+", the volume to trade will be the maximum possible of "+DoubleToString(lots, 2));
               }
            }
         }
         not_enough_message = false;
      }
      
      //- expiration for trades
      if (type==OP_BUY || type==OP_SELL)
      {
         if (expiration > 0)
         {
            //- convert UNIX to seconds
            if (expiration > TimeCurrent()-100) {
               expiration = expiration - TimeCurrent();
            }
            
            //- bo broker?
            if (StringLen(symbol)>6 && StringSubstr(symbol, StringLen(symbol)-2)=="bo") {
               comment = "BO exp:"+(string)expiration;
            }
            else {
               string expiration_str   = "[exp:"+IntegerToString(expiration)+"]";
               int expiration_len      = StringLen(expiration_str);
               int comment_len         = StringLen(comment);
               if (comment_len > (27-expiration_len))
               {
                  comment = StringSubstr(comment, 0, (27-expiration_len));
               }
               comment = comment + expiration_str;
            }
         }
      }

      if (type==OP_BUY || type==OP_SELL)
      {
         op=ask;
         if (bs<0) {
           op=bid;
         }
      }
      
      op    = NormalizeDouble(op, digits);
      sll   = NormalizeDouble(sll,digits);
      tpl   = NormalizeDouble(tpl,digits);
      if (op<0 || op>=EMPTY_VALUE)  {break;}
      if (sll<0 || slp<0 || tpl<0 || tpp<0) {break;}

      //-- SL and TP ----------------------------------------------------
      vsl=0; vtp=0;
      
      sl=AlignStopLoss(symbol, type, op, 0, NormalizeDouble(sll,digits), slp);
      if (sl<0) {break;}
      tp=AlignTakeProfit(symbol, type, op, 0, NormalizeDouble(tpl,digits), tpp);
      if (tp<0) {break;}
      
      if (USE_VIRTUAL_STOPS)
      {
         //-- virtual SL and TP --------------------------------------------
         vsl=sl;
         vtp=tp;
         sl=0; tp=0;
         
         double askbid=ask;
         if (bs<0) {askbid=bid;}
         
         if (vsl>0 || USE_EMERGENCY_STOPS=="always") {
            if (EMERGENCY_STOPS_REL>0 || EMERGENCY_STOPS_ADD>0)
            {
               sl=vsl-EMERGENCY_STOPS_REL*MathAbs(askbid-vsl)*bs;
               if (sl<=0) {sl=askbid;}
               sl=sl-toDigits(EMERGENCY_STOPS_ADD,symbol)*bs;
            }
         }
         if (vtp>0 || USE_EMERGENCY_STOPS=="always") {
            if (EMERGENCY_STOPS_REL>0 || EMERGENCY_STOPS_ADD>0)
            {
               tp=vtp+EMERGENCY_STOPS_REL*MathAbs(vtp-askbid)*bs;
               if (tp<=0) {tp=askbid;}
               tp=tp+toDigits(EMERGENCY_STOPS_ADD,symbol)*bs;
            }
         }
         vsl=NormalizeDouble(vsl,digits);
         vtp=NormalizeDouble(vtp,digits);
      }
      
      sl=NormalizeDouble(sl,digits);
      tp=NormalizeDouble(tp,digits);

      //-- fix expiration for pending orders ----------------------------
      if (expiration>0 && type>OP_SELL) {
         if ((expiration-TimeCurrent())<(11*60)) {
            Print("Expiration time cannot be less than 11 minutes, so it was automatically modified to 11 minutes.");
            expiration=TimeCurrent()+(11*60);
         }
      }
      
      //-- fix prices by ticksize
      op = MathRound(op/ticksize)*ticksize;
      sl = MathRound(sl/ticksize)*ticksize;
      tp = MathRound(tp/ticksize)*ticksize;

      //-- send ---------------------------------------------------------
      ResetLastError();
      ticket = OrderSend(symbol,type,lots,op,(int)(slippage*PipValue(symbol)),sl,tp,comment,magic,expiration,arrowcolor);

      //-- error check --------------------------------------------------
      string msg_prefix="New trade error";
      if (type>OP_SELL) {msg_prefix="New order error";}
      int erraction=CheckForTradingError(GetLastError(), msg_prefix);
      switch(erraction)
      {
         case 0: break;    // no error
         case 1: continue; // overcomable error
         case 2: break;    // fatal error
      }
      
      //-- finish work --------------------------------------------------
      if (ticket>0) {
         if (USE_VIRTUAL_STOPS) {
            VirtualStopsDriver("set", ticket, vsl, vtp, toPips(MathAbs(op-vsl), symbol), toPips(MathAbs(vtp-op), symbol));
         }
         
         //-- show some info
         double slip=0;
         if (OrderSelect(ticket,SELECT_BY_TICKET)) {
            if (!MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_VISUAL_MODE) &&!MQLInfoInteger(MQL_OPTIMIZATION))
				{
               slip=OrderOpenPrice()-op;
					string print = "";
               print = StringConcatenate(
                  "Operation details: Speed ",
                  (GetTickCount()-time0),
                  " ms | Slippage ",
                  DoubleToStr(toPips(slip, symbol),1),
                  " pips"
               );
					Print(print);
            }
         }
         
         //-- fix stops in case of slippage
         if (!MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_VISUAL_MODE) &&!MQLInfoInteger(MQL_OPTIMIZATION))
         {
            slip = NormalizeDouble(OrderOpenPrice(), digits) - NormalizeDouble(op, digits);
            if (slip != 0 && (OrderStopLoss()!=0 || OrderTakeProfit()!=0))
            {
               Print("Correcting stops because of slippage...");
               sl = OrderStopLoss();
               tp = OrderTakeProfit();
               if (sl != 0 || tp != 0)
               {
                  if (sl != 0) {sl = NormalizeDouble(OrderStopLoss()+slip, digits);}
                  if (tp != 0) {tp = NormalizeDouble(OrderTakeProfit()+slip, digits);}
                  ModifyOrder(ticket, OrderOpenPrice(), sl, tp, 0, 0, 0, CLR_NONE, false);
               }
            }
         }
         
         RegisterEvent("trade");
         break;
      }
      
      break;
   }
   
   if (oco == true && ticket > 0)
   {
      if (USE_VIRTUAL_STOPS) {
         sl = vsl;
         tp = vtp;
      }
      
      sl = (sl > 0) ? NormalizeDouble(MathAbs(op-sl), digits) : 0;
      tp = (tp > 0) ? NormalizeDouble(MathAbs(op-tp), digits) : 0;
      
      int typeoco = type;
      if (typeoco == OP_BUYSTOP) {
         typeoco = OP_SELLSTOP;
         op = bid - MathAbs(op-ask);
      }
      else if (typeoco == OP_BUYLIMIT) {
         typeoco = OP_SELLLIMIT;
         op = bid + MathAbs(op-ask);
      }
      else if (typeoco == OP_SELLSTOP) {
         typeoco = OP_BUYSTOP;
         op = ask + MathAbs(op-bid);
      }
      else if (typeoco == OP_SELLLIMIT) {
         typeoco = OP_BUYLIMIT;
         op = ask - MathAbs(op-bid);
      }
      
      if (typeoco == OP_BUYSTOP || typeoco == OP_BUYLIMIT)
      {
         sl = (sl > 0) ? op - sl : 0;
         tp = (tp > 0) ? op + tp : 0;
         arrowcolor = clrBlue;
      }
      else {
         sl = (sl > 0) ? op + sl : 0;
         tp = (tp > 0) ? op - tp : 0;
         arrowcolor = clrRed;
      }
         
      comment = "[oco:"+(string)ticket+"]";
      
      OrderCreate(symbol,typeoco,lots,op,sl,tp,0,0,slippage,magic,comment,arrowcolor,expiration,false);
   }
   
   return(ticket);
}

bool OrderModified(double id=-1, string action="set")
{
   static double memory[];
   
   if (id==-1) {
      id=OrderTicket();
      action="get";
   }
   else if (id>-1 && action!="clear") {
      action="set";
   }
   
   bool modified_status=InArray(memory,id);
   
   if (action=="set") {
   //- Set Trade ID
      ArrayValue(memory,id);
      return(true);
   }
   else if (action=="clear") {
   //- Unset Trade ID
      ArrayStrip(memory,id);
      return(true);
   }
   else if (action=="get") {
   //- Get State
      return(modified_status);
   }
   
   Print("Error: The second parameter of the function \"OrderModified\" should be \"set\", \"get\" or \"clear\"");
   return (false);
}

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;
}

int SellNow(
   string symbol,
   double lots,
   double sll,
   double tpl,
   double slp,
   double tpp,
   double slippage=0,
   int magic=0,
   string comment="",
   color arrowcolor=CLR_NONE,
   datetime expiration = 0
   )
{
   int ticket=OrderCreate(
      symbol,
      OP_SELL,
      lots,
      0,
      sll,
      tpl,
      slp,
      tpp,
      slippage,
      magic,
      comment,
      arrowcolor,
      expiration
      );
   return(ticket);
}

string SetSymbol(string symbol="")
{
	if (symbol=="") {symbol=Symbol();}
   GetSymbol(symbol); return(symbol);
}

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++;    
   }
}

template<typename T>
string StringImplode(string delimeter, T &array[])
{
   string retval = "";
   int size      = ArraySize(array);

   for (int i = 0; i < size; i++)
	{
      retval = StringConcatenate(retval, (string)array[i], delimeter);
   }
	
   return StringSubstr(retval, 0, (StringLen(retval) - StringLen(delimeter)));
}

string StringTrim(string text)
{
   static string t; t = text;
   t = StringTrimRight(t);
   t = StringTrimLeft(t);
	return(t);
}

double SymbolAsk(string symbol="")
{
   if (symbol=="") {symbol=GetSymbol();}
   return(MarketInfo(symbol,MODE_ASK));
}

double SymbolBid(string symbol="")
{
   if (symbol=="") {symbol=GetSymbol();}
   return(MarketInfo(symbol,MODE_BID));
}

int SymbolDigits(string symbol="")
{
   if (symbol=="") {symbol=GetSymbol();}
   return (int)MarketInfo(symbol,MODE_DIGITS);
}

int SymbolsList(string &symbols[], bool selected)
{
   static string memory[];
   static int symbols_count;
   static bool do_read=true;
   
   //-- read all symbols once, or read them every time if selected==true
   if (do_read==true || selected==true)
   {
      do_read=false;
      
      symbols_count = SymbolsTotal(selected);
      
      int s = 0;
      for(int i = 0; i < symbols_count; i++)
      {
         string symbol = SymbolName(i, selected);
         if (StringLen(symbol) > 0 /* add another condition here if needed */)
         {
            ArrayResize(memory, s+1);
            memory[s] = symbol;
            s++;
         }
      }
      symbols_count = s;
   }
   
   ArrayCopy(symbols,memory);
   return(symbols_count);
}

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);
}

int TicksPerSecond(bool get_max = false, bool set = false)
{
   static datetime time0 = 0;
   datetime time1 = TimeLocal();
   static int ticks, tps, tpsmax;
   
   if (set == true)
   {
      if (time1 > time0)
      {
         if (time1-time0 > 1)
         {
            tps = 0;
         }
         else
         {
            tps = ticks;
         }
         time0 = time1;
         ticks = 0;
      }
      
      ticks++;
      
      if (tps > tpsmax) {tpsmax = tps;}
   }
   
   if (get_max)
   {
      return tpsmax;
   }
   
   return tps;
}

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);
}

datetime TimeFromComponents(int time_src=0, int y=0, int m=0, int d=0, int h=0, int i=0, int s=0)
{
   MqlDateTime tm;
   if (time_src == 0) {TimeCurrent(tm);}
	else if (time_src == 1) {TimeLocal(tm);}
	else if (time_src == 2) {TimeGMT(tm);}

   if (y>0) {
      if (y<100) {y=2000+y;}
      tm.year = y;
   }
   if (m>0) {tm.mon = m;}
   if (d>0) {tm.day = d;}

   tm.hour  = h;
   tm.min   = i;
   tm.sec   = s;
   
   return StructToTime(tm);
}

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=0, 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 ulong mem_to_ti[]; // tickets
   static int mem_to[];    // timeouts
   static ulong 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 ulong 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(0, name)<0) {
                  error=GetLastError();
               }
               else {
                  clear=false;
                  ObjectDelete(0, name);
               }
               
               name = "#"+(string)OrderTicket()+" tp";
               if (ObjectFind(0, name)<0) {
                  clear=true;
                  error=GetLastError();
               }
               else {
                  clear=false;
                  ObjectDelete(0, 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 ulong 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 = ObjectGetDouble(0,name,OBJPROP_PRICE,0);
            name = "#"+(string)ticket+" tp";
            tp_lvl = ObjectGetDouble(0,name,OBJPROP_PRICE,0);
            
            // close trade/order
            if (OrderType()==OP_BUY)
            {
               bid = SymbolInfoDouble(symbol,SYMBOL_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((int)ticket, lots, cp, 0, clrNONE))
                  {
                     OnTradeListener(); // check this before deleting the lines
                     name = "#"+(string)OrderTicket()+" sl";
                     ObjectDelete(0, name);
                     name = "#"+(string)OrderTicket()+" tp";
                     ObjectDelete(0, 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 = SymbolInfoDouble(symbol,SYMBOL_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((int)ticket, lots, cp, 0, clrNONE))
                  {
                     OnTradeListener(); // check this before deleting the lines
                     name = "#"+(string)OrderTicket()+" sl";
                     ObjectDelete(0, name);
                     name = "#"+(string)OrderTicket()+" tp";
                     ObjectDelete(0, 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>0)
   {
      static string settext;
      // update record (add/modify)
      name = "#"+(string)ti+" sl";
      if (sl>0) {
         if (ObjectFind(0, name)==-1)
         {
            ObjectCreate(0,name,OBJ_HLINE,0,0,sl);
            ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
            ObjectSetInteger(0,name,OBJPROP_COLOR,DeepPink);
            ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
            settext = name+" (virtual)";
            ObjectSetString(0,name,OBJPROP_TEXT,settext);
            error=GetLastError();
         }
         else {
            ObjectSetDouble(0,name,OBJPROP_PRICE,0,sl);
         }
      } else {ObjectDelete(0, name);}
      
      name="#"+(string)ti+" tp";
      if (tp>0)
      {
         if (ObjectFind(0, name)==-1) {
            ObjectCreate(0,name,OBJ_HLINE,0,0,tp);
            ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
            ObjectSetInteger(0,name,OBJPROP_COLOR,DodgerBlue);
            ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
            settext = name+" (virtual)";
            ObjectSetString(0,name,OBJPROP_TEXT,settext);
            error=GetLastError();
         }
         else {
            ObjectSetDouble(0,name,OBJPROP_PRICE,0,tp);
         }
      }
      else {
         ObjectDelete(0, name);
      }
      
      // print message
      if (command=="set" || command=="modify") {
         print = command+" #"+(string)ti+": virtual sl "+DoubleToStr(sl,(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS))+" tp "+DoubleToStr(tp,(int)SymbolInfoInteger(Symbol(),SYMBOL_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(0, name) == -1) {error=GetLastError();return(0);} 
         return(ObjectGetDouble(0,name,OBJPROP_PRICE,0));
      }
      else if (command=="get tp")
      {
         name = "#"+(string)ti+" tp";
         if (ObjectFind(0, name) == -1) {error=GetLastError();return(0);}
         return(ObjectGetDouble(0,name,OBJPROP_PRICE,0));
      }
      return(0);
   }
   
   return(1);
}

void WaitTradeContextIfBusy()
{
	if(IsTradeContextBusy()) {
      while(true)
      {
         Sleep(1);
         if(!IsTradeContextBusy()) {
            RefreshRates();
            break;
         }
      }
   }
   return;
}

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]);}

double toDigits(double pips, string symbol="")
{
	if (symbol=="") {symbol=GetSymbol();}
   return(
      NormalizeDouble(
         pips*PipValue(symbol)*MarketInfo(symbol,MODE_POINT),
         (int)MarketInfo(symbol,MODE_DIGITS)
      )
   );
}

double toPips(double digits,string symbol="")
{
   if (symbol=="") {symbol=GetSymbol();}
   return(digits/(PipValue(symbol)*MarketInfo(symbol,MODE_POINT)));
}




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[];

int fxdBlocksLookupTableTranslateID(string id)
{
	return ArraySearch(fxdBlocksLookupTable, id);
}
string fxdBlocksLookupTableTranslateID(int id)
{
	return fxdBlocksLookupTable[id];
}

int fxdBlockToggle(int id)
{
	_blocks_[id].__disabled = !_blocks_[id].__disabled;
	return _blocks_[id].__disabled;
}

void fxdBlockTurnOn(int id)
{
	_blocks_[id].__disabled = false;
}
void fxdBlockTurnOff(int id)
{
	_blocks_[id].__disabled = true;
}

void fxdBlockRun(int id=0)
{
	_blocks_[id].run();
}

void fxdGetInboundBlocks(int block_id, int &list[])
{
	if (block_id > -1 && ArraySize(_blocks_) > block_id) {
		ArrayCopy(list, _blocks_[block_id].__inbound_blocks);
	}
}

void fxdGetInboundBlocks(string block_string_id, int &list[])
{
	// first we need to get the numeric id of the block
	int block_id = ArraySearch(fxdBlocksLookupTable, block_string_id);

	if (block_id > -1 && ArraySize(_blocks_) > block_id) {
		ArrayCopy(list, _blocks_[block_id].__inbound_blocks);
	}
}

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];
				_blocks_[block_to_run].run();
			}

			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:eNrtXVlz2zgS/isqzWuS4ilRypPPmdT6WsnJ1uyLiiIhGWuK4PJIrE35v283AJ6mEklDeewY8zCR2Y1udKO7gY8gSHc8Gn9Pxro+7nssDImXUhYm/Y/u2NDH3+lYg18mctjj/oKSwO9/TMbDcf/07Pzo88Ut/jUY9xOWxR7BP0COLi+mbrwkqbxo9T8+0rG+uzSrTdqQSzN2lzZ8Is2ADptcnLmjONmyRd6Iy7Pa5AHvp6svZ5PtbXW4MHt3W53WvomBGOxhq9Umz9C4vOHu3TPajLW5NGd3aXabNDEOo92ljVpdJzqna3v4zm71nS4E6jsHSqs4XQyFbuzRP61V4EAI3CcxBq0GG0KgtfuImG3jK/tn79E/vdVgUVf0fbKjtbIYorLowz0EGq09FNVAd/YQ2FoPDFmY90iS1iHWRUwb2s4x3S4OI+aRX58HzLsXM5OFM5Mu56hk7PC5K41ZwMn8mm6O+xc0Sa8X0/VqzilcytnnyefpKQhNYOLjzVwakhip1rh/S717/AkSfZq484BAv+cwCXJfkIeUxGGug/LLLXoeec+pX50OQRdNZl6WpGyVS3Rym4S16I91kpKV7MCK+SSYSTGgfUrSkyyOSZgKTUZOiNzYXRHoGihgLPa5qdq4L4VGFKb1wgwYjsRzAyKJIYtXbsAlQYuEoKyUxdxGbfxdeD5J3TQTnpcjykK2WPQ/CheA33w3dQutj5wlpanQYg5433t92fvepRvfg8m9BYt7Ifi0l48sVwaCuUOkMhAekEWKuoyBjeJANIvwb32koS7g+ObStDJOLEujLE2qMihcQw5d9JfIP7kAkOglnJsHrI6x5nr3y5hlof/eYwGLZTL8tliQuT/vCz8WFOlrXEMtIKDefyN0eZdW/aFbZYjOwOTYLR0MofHVjSkGW8XtOoTQHAaTxO+TdC08KaSJdc/ukQ9hf/LH+XNGvqEiv5vIt3UV+cUSfa+a/8yRb6rI7ybyh6rm9wsw2Rr5Q95/aHkJsXMKUemGYkllYRhEuYvlAJQ9N+DfCVp7la3mEEfVpsCvfdB0jaMC9E2V8UYKRfuAxRuP53Q5W8Yyap0697UcWp4WXJ5htHEUUiWXDs6eECQQWCJCxi5JSf6LiSy8Xc1Xq4N8feJQSE8vFdFyyJTVxWK44pu/nL7FEPVCYUvPrxjzw4Q1Na2WsIZlP3fCnp+fnIjQrSYseOe3Z81YW2XsATPWVhnbUcbalq0ytrgp+sIydpBnbALRFLzynB2onO0oZ4cjlbP9YuOhHRHq+b7WNbZMfgcbouotuZI09VhEpCXLgg3IAnQVZB6XeEX+Mf3z8vj6ooEGjrN1ch1PSSDgJxg7hyt9CRuxgBy7CbmJqSgD4EpPYCSZHBWqHLMyhUBAhKRkJv6R+VE125KBWCiANkfTf8geY/J+Oq2nxFMr4N+T68nk7OT20/VV6WwxPmjBxA2XzfoHJH65KDEmlq7mWsOUXOexy3cay0qoiT2IguOGJTTnOFA9GnaB0sHXV+yKuPF8PcnCkIbLQ5cis1KKdJ79hYvwDkl+vYsSJazrQY76BHA7GrnFbdo6AkBE8My16Wh0Mjo/3x6yVxzeeYFy9ilQxvMVKAy8/LeqUC+sQjmqQh2gQpmOqlCVCjVSSyhVoPYsUCNVoA5QoGzNVgWqLFD8iSu1hlIlavcSVTzbpmpUxzVqYKgaVa1R+gtfRb3hGmXLGtW4h/6yqpSuqtQBqtTQVlWqWqWMF76SertY71UUKUMVqQMUKcdpFin7TRepjU95cjPachCU3H66PDufHF2e1fa3Qc2NmySX7sMtXZGkXx3rjnOjk2c+wZ7r0CM3JD5241fysCc6GTvdi0jcm7s/3702mpPy4Nkn5blGDMf823evdetVhrqlQn3LUH+y1fB2Q91+laFuq1DfMtSbh1TecKgPXmWoD1SobxnqA0tV9X55svn1hfpQhfqWoT4cqqreL8/cv75Qd1Sobxnq
zkhV9X75NojWULd/tpm16Q7pFxZkK4J3Q6V1C/pA/Dp1Sv9XjJz2QffG46/8en52qeSa0OReBrit1YXkJPC68cHO75AKmjzlIMn52QsnJx9jOlR48oO8aK5f7eKTO806ujjIyIz/v+U+cx7AX3K6yU38yf1j7No5+mnippR9Dmmau8gSp0fyO44l0ykJRErId/MI+mp1ufwEzakbXLA0aXTAEQyXWZDSKFhfhxcsSar3/vHQRZ3jJmYLmtZ2MYeC58j3UUVNiOjHqMFQk1HrKj+S0iJhWCNv6oNuGoPNxpo5y4R8hQAuwtENxG8h45zO2UYZeHBjtTqFaobHNtKfOLbg26DQ4D26cOcs8+5ITDaLG9QZ8Qh6Hn36O+Od+c56Z78blL4uWTfZylVPyX9xF6VFoS7J5VBAaxNUDWqtW4fCkMRNqgd51n2OoEBf0BWtnpzC5lOow6i3tWhU6MUWCh4LzJ9FmV4UZ+hzNpHYtzeN9AeNfpAzdZLbhQ9+kNmoNepUa92kH+n1O9VbLUU/UIzLGPeeiEhpHdIaR+ugirHD4K4wimEV413xgYHDWrI928Ci3qhjvdsNLWr2O9a83eBClp09RPmgQm9/vz2RSxq4fuqua2Xc4Vf/YFmcNJ2KUmiYpaTWABd6QGkxSMsNSumq1Z58DYN9u5U8xXSi4coa5vTqW6jyh8U4IXVXkQxTTRuXgYXEEzf0AyJ2h6smIO3JQVB9VG2Evxa4dm5ObpyFrSIWQkT/SdzaJpzToF+CnXdVhkGDAfxeJTfl4wDU9nWLThYa+FA05/Aay5SAu/0ai1l6e3pHF2ntIQJLehYJaGAVM+DsXxC5dTVqtem/CLmvEc0KsRJv+pOWRdg1vCaUlrHXRhbGtpLvaYR98qVq0JjGmEGYHTBw04BGUXm4Nt8/uVyDI1flarNYoRzFMft2guv/42wtBR7zlOweIHbxCPeAP8hxxb4dGhvaG3by9fpOPr4vpdzhtzvd4R9yW3uhMHano3CWsTui1OqIUt8RUWqaoz09pivea4P/bcCV+BwOw5f2dYYtgTMmXuqGS/w7f0XgziDTUCBTgUwFMhXIVCBTgUwFMhXIVCBTgUwFMncAmYWPSpCJj9/LpJoQ/wAg0+jiDB5/H2oQvA2U6Qhjt4KZZuP5ccuwnxtmLhbaS4WZutrLVDBTwUwFMxXMVDBTwUwFMxXMVDBTwcxfcS/T0NVe5qH2Mu3GmQfLtNVeZgEyDbWXqUCmApkKZCqQqUCmApkKZCqQqUCmApm/5F6mofYyD7eXORj87TDzBe9lmmovU8FMBTMVzHxJMNPCCSgkm1BmWQsUtnzt2NKU2FK8pFShS4UuFbpU6PLX3sJ8ZnRpKnR5OHQ5HCl0uRldWmoTU6FLhS4VulToUqFLhS4VulToUqFLhS4Punf5zA/IWuoB2UM9IDtqfLTVstTLfiS2FHkBfktpuKzUarQFP2v1lcw2BTlKj2L2H5A2C2UVxZX4HfGXIKxn9qKEZD7rxSS8Z80W6br4FBl5gAVnmuecB/E8C6B/mVt8muvynxdWAfzcFPoUE1eURMNCXKUP32uj95p+C5URKrRhf7Ad+9+1JpAndLHetsnKXVJvBhEaiwhy+IdxDJy5aULBzbOAzmM3Xs+K76zVEhMrdM7Js3cTH0YlVK54luRVjY75Rjv2ImI0TGdxJkeVfyxLqwTnImarfGLEz47JWpKy2poBXxvc0mgoGjWbyU+YyYZGS0Mnb/iDlo+i/mQJLHJonGZuAM5kBTAIeQPhzgqVrx94hokSgF5EEQQctCSht24RgjwN+iwmQXO6bLK4fn26hB8AjMI0wUDJ4y7NQ31UUL21B0MKAUuZj93Uc3KIyTdLIohMf8ZrbkE2S7IseZIwxM/0udHdjMUUpLvFR+KgN7csumXHLOUzQTHdgIBQFKiBXFKVn/eV38wrZpUaUUxKUE8rieezbC7fyI1ZQhIvplHeBzlL2HJpV7mFIRFM9XN4
TdVNehfagWmYTx35faim3gqpC5VlCvHm+cO4LVrL53S7cfOwqhPx1gadAop1qdPJR65Na43YkV78WOLjxjkMmF1IqcoVSBq8g8FlyoXHFpPVVvX48f9USZgb
:fxdreema>*/