//=============================================================================
//                            LibOrderReliable.mq4
//
//         Copyright © 2006, Matthew Kennel  (mbkennelfx@gmail.com)
//         Copyright © 2007, Derk Wehler     (derkwehler@gmail.com)
//         Copyright © 2007, Jack Tomlinson  (jack.tomlinson@gmail.com)
//
//  In order to read this code most clearly in the Metaeditor, it is advised
//  that you set your tab settings to 4 (instead of the default 3): 
//  Tools->Options->General Tab, set Tab Size to 4, uncheck "Insert spaces"
//
//                        CURRENT REVISION STATUS (Inserted by SOS):
//                       |$Workfile:: LibOrderReliable.mq4                $|
//                       |$Revision:: 39                                  $|
//                       |$Author  :: Derk                                $|
//                       |$Date    :: 12/21/07 4:18p                      $|
//
// ***************************************************************************
// ***************************************************************************
//  LICENSING:  This is free, open source software, licensed under
//              Version 2 of the GNU General Public License (GPL).
//
//  In particular, this means that distribution of this software in a binary
//  format, e.g. as compiled in as part of a .ex4 format, must be accompanied
//  by the non-obfuscated source code of both this file, AND the .mq4 source
//  files which it is compiled with, or you must make such files available at
//  no charge to binary recipients.	If you do not agree with such terms you
//  must not use this code.  Detailed terms of the GPL are widely available
//  on the Internet.  The Library GPL (LGPL) was intentionally not used,
//  therefore the source code of files which link to this are subject to
//  terms of the GPL if binaries made from them are publicly distributed or
//  sold.
//
//  ANY USE OF THIS CODE NOT CONFORMING TO THIS LICENSE MUST FIRST RECEIVE 
//  PRIOR AUTHORIZATION FROM THE AUTHOR(S).  ANY COMMERCIAL USE MUST FIRST 
//  OBTAIN A COMMERCIAL LICENSE FROM THE AUTHOR(S).
//
//  Copyright (2006), Matthew Kennel, mbkennelfx@gmail.com
//  Copyright (2007), Derk Wehler, derkwehler@gmail.com
// ***************************************************************************
// ***************************************************************************
//
//  A library for MT4 expert advisors, intended to give more reliable
//  order handling.	This library only concerns the mechanics of sending
//  orders to the Metatrader server, dealing with transient connectivity
//  problems better than the standard order sending functions.  It is
//  essentially an error-checking wrapper around the existing transaction
//  functions. This library provides nothing to help actual trade strategies,
//  but ought to be valuable for nearly all expert advisors which trade 'live'.
//
//
//                             Instructions:
//
//  Put this file in the experts/libraries directory.  Put the header
//  file (LibOrderReliable.mqh) in the experts/include directory
//
//  Include the line:
//
//     #include <LibOrderReliable.mqh>
//
//  ...in the beginning of your EA with the question marks replaced by the
//  actual version number (in file name) of the header file.
//
//  YOU MUST EDIT THE EA MANUALLY IN ORDER TO USE THIS LIBRARY, BY BOTH
//  SPECIFYING THE INCLUDE FILE AND THEN MODIFYING THE EA CODE TO USE
//  THE FUNCTIONS.
//
//  In particular you must change, in the EA, OrderSend() commands to
//  OrderSendReliable() and OrderModify() commands to OrderModifyReliable(),
//  or any others which are appropriate.
//
//=============================================================================
//
//  Contents:
//
//		OrderSendReliable()
//			This is intended to be a drop-in replacement for OrderSend()
//			which, one hopes is more resistant to various forms of errors
//			prevalent with MetaTrader.
//
//		OrderSendReliableMKT()
//			This function is intended for immediate market-orders ONLY,
//			the principal difference that in its internal retry-loop,
//			it uses the new "Bid" and "Ask" real-time variables as opposed
//			to the OrderSendReliable() which uses only the price given upon
//			entry to the routine.  More likely to get off orders, and more
//			likely they are further from desired price.
//
//		OrderSendReliable2Step()
//			This function is intended to be used when brokers do not allow
//			initial stoploss and take-profit settings as part of the initial
//			market order. After successfully playing an order, it will then
//			Call OrderModifyReliable() to update the SL and TP settings.
//
//		OrderModifyReliable()
//			A replacement for OrderModify with more error handling.
//
//		OrderCloseReliable()
//			A replacement for OrderClose with more error handling.
//
//		OrderCloseReliableMKT()
//			This function is intended for closing orders ASAP; the
//			principal difference is that in its internal retry-loop,
//			it uses the new "Bid" and "Ask" real-time variables as opposed
//			to the OrderCloseReliable() which uses only the price given upon
//			entry to the routine.  More likely to get the order closed if 
//          price moves, but more likely to "slip"
//
//		OrderDeleteReliable()
//			A replacement for OrderDelete with more error handling.
//
//===========================================================================
/*
            ADDITIONAL TIPS FOR RELIABLE EXPERT ADVISORS
            (Matthew Kennel, 7-31-06)

	*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
	First bit of advice: In your metaeditor, open the options window 
	(Tools -> Options).  By default you will see that the tab level is 
	set to 3.  I'm not sure why those wacky Russians use 3-space tabs, 
	but 4 is much more common.  I advise you set to 4, and also that you 
	DISable the checkbox below ("Insert spaces").  Once you change to that, 
	all this code will look much more readable, and so will the code that 
	you write.
	*  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *

	Here are some "good practices" which some people have found by experience
	to be a good idea for EA programming.

	The first thing to remember is that programming EA's, which work in
	an unreliable client-server environment, is not like ordinary software
	development.  In "regular" programming you can be pretty sure that
	operations will work as long as the computer is functioning.   This is
	not the case with transactions over the network, and you have to be
	quite careful and almost "paranoid" in the sense of checking for
	anything which might go wrong.  You must assume a possibility that all
	transactions might fail, and that much information may become unavailable,
	often transiently so. These failure modes will NOT be visible on a
	back-test, and are significantly more severe in a live "real-money"
	account than even real-time demo accounts.

	The problem is that many EA's use the existence of orders (or nonexistence 
	thereof) as a trigger for additional events, like setting up additional
	trades, in same direction or not.   If you assume that trades will be
	successful, or otherwise instantly visible to OrderSelect(), for example,
	you may be in for a shock as this information may be unavailable during a
	trade, or during the interval the dealing desk is 'holding' the order.
	This may result in duplicate trades which shouldn't be there, in bad
	circumstances, a loop of a sequence of zillions of trades placed
	inappropriately.  If this is real money this could mean inappropriate
	risk to a catastrophe and the dealers will NOT bail you out as your
	bugs are your own problem.

	Other problem is that Metatrader is somewhat amateurish in its design,
	and a real "pro" level financial system would have transaction-oriented
	multiple-phase commits and event queues.  On the downside this would be
	significantly more difficult to program.

	Unfortunately if you want to use Metatrader EA's there is no real
	alternative to learning how to write software fairly well.  A little bit
	of knowledge and over-confidence (and a buggy EA) is a very quick way to
	go bankrupt.  If you have never or almost never written software before,
	please go learn on something which will not cost you money.

	Specific suggestions:

	*	Learn how to use the "MagicNumber" function in your EA's.  Remember
		that multiple EA's on the same symbol (perhaps even if different
		chart time frame's) will have ALL their orders visible to one another.
		This is not the case in back-testing.   Many EA's which work fine in
		backtesting will totally get fried when there are multiple EA's
		working at once. Hence, if you want to see if an order is "owned"
		by an EA---which is ESSENTIAL!!!---you must check the MagicNumber
		variable in addition to the OrderSymbol() to ensure it is one of
		"yours".

	*	Manage your existing orders with the *ticket number*, i.e. the value
		returned by OrderSend() (and OrderSendReliable()).   This has proven
		by people's experience to be more reliable, in terms of getting
		status of existing orders with OrderSelect(), i.e. SELECT_BY_TICKET.
		Sometimes people have found that orders have become invisible
		to SELECT_BY_POS for a short time, perhaps while the dealing desk has
		them, but remain visible to SELECT_BY_TICKET.  The ticket number is,
		by definition, the truly unique identifier of the order in the
		server's data base.  Use it.

	*	Often you may combine the use of ticket numbers and magic numbers.
		For instance, suppose you have potentially four different order kinds
		in your EA, two long orders (i.e. for scaling out), and two short
		orders. You would then define that each had a magic number *offset*
		from the magic number base.  E.g. you may have something like this:

			extern int MN = 123456;
			int Magic_long1, int Magic_long2, int Magic_short1, int Magic_short2
			int Ticket_long1, int Ticket_long2, int Ticket_short1, int Ticket_short2;

		and in the init() function:

			init() 
			{
				Magic_long1 = MN;
				Magic_long2 = MN+1;
				Magic_short1 = MN+2;
				Magic_short2 = MN+3;
			}

		And then when you send the order you use the appropriate magic number---and
		you save the ticket value in the appropriate variable.  When you see if an
		order is owned by you then you have to check the range of magic numbers.

		Of course this can be generalized to arrays of each.

	*	Put the magic number in the "Comment" field so you can see it on the
		Metatrader terminal.  This way you can figure out what is what as magic
		numbers are not available in the user-interface. For example:

			string overall_comment = "My_EA_name"

			void foo() 
			{
				yadda yadda
				int my_MN = MN + k;
				string cstr = overall_comment + " magic:"+my_mn;

				OrderSendReliable(  yadda yadda yadda,   my_MN, cstr,   yadda yadda )
			}


		If you are in a loop checking existing orders with an OrderSelect()
		and a SELECT_BY_POS,  if you delete an order or close an order, then
		all other entries may become invalid!  By deleting then you have
		totally changed the list, and you ought to start over with a new
		loop once you have executed one client-server transaction.  For
		instance the OrdersTotal() can change.

		Generally hence do loops with OrdersTotal() like this,

			int i;
			bool do_again = false;
			for (i = OrdersTotal()-1; !do_again && i >= 0; i--) 
			{
				if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES) 
				{
					if (condition) 
					{
						if {OrderDelete(OrderTicket()) 
							Print("Successful delete")
						else
							Print("Failed delete")
						do_again = true;
						break; // VERY IMPORTANT TO QUIT ENTIRE FOR LOOP IF ORDER IS DELETED
					}
					if (othercondition) 
					{
						OrderCloseReliable( yadda yadda ) 
						do_again = true;
						break;
					}
				}
			}

		and if "do_again == true" then there is a possibility that more may yet
		remain in the list for processing so you ought to restart the loop over.
		One way to do that is to have something like a "DeleteOneOrder()" function
		which has that core loop, and returns the "do_again" variable.  And then
		the calling function will keep on calling it until do_again is false---
		and of course you will also have a maximum loop count in case something
		gets screwed up.

		Not good:

			for (i=0; i < OrdersTotal(); i++) 
			{
				//
				// maybe delete an order in here
				//
			}

	*	Note also the error checking of the return value of OrderSelect().
		Most people forget to do that.  I am not sure what it means if
		an OrderSelect() fails, whether to continue or to abort.

	*	Put time-outs between the time you send an order, and you
		later query them to let it "settle".

	*	If you have a condition that you think specifies a new order, then
		ensure that it stays "OK" for a certain number of seconds/minutes.  You
		do this by saving in a static or global int variable, the "TimeCurrent()"
		when it last occurred (and setting that to zero if the condition is
		false) and then only if it has remained true until
		(TimeCurrent() - saved_time) >= some_interval of seconds.

	*	Try to query orders to check their status, using ticket numbers, if you
		have them before assuming they have executed.

	*	Orders can go from pending to active any time during the execution
		of your EA and you might not know it unless you check.

		*	Check to see that there are not an excessive number of 'open orders'
		'owned' by the EA, either active or pending.  THIS CAN BE A KEY
		SANITY CHECK WHICH PREVENTS A FINANCIAL MELTDOWN!!!

		And if there are, do not open more, there is probably
		a bug, and you do not want to send too many real-money orders.   Here
		you will probably do a SELECT_BY_POS and not SELECT_BY_TICKET because
		you have to account for the possibility that due to a bug (or restart
		of the EA!) you have "forgotten" some of the ticket numbers.

	*	Do not assume that in a real-money EA that a stop or take profit will actually
		be executed if the price has gone through that value.  Yes, they are
		unethical and mean.

	*	Assume that the EA could be stopped and restarted at any time due to
		external factors, like a power failure.  In other words, do not
		assume in the init() that there are no orders outstanding owned
		by the EA!!!     If your EA depends on this to be true, then
		check first, and if it isn't the case, put up a big Alert
		box or something and tell the human to delete them.

		Or, better, if you are able to 'pick up where you left off' then do so
		and write your EA with that possibility in mind.

	*	Write your EA's with a "SetNewOrders" type of boolean variable
		which, if false, means that the EA will not set new orders, but
		will continue to manage open orders and close them.  This variable
		may be changed "in flight" by the user to allow him to
		'safely' go flat.

	*	Use global variables---i.e. the ones you set with GlobalVariableSet()
		as these can stay persistent over restarts of the trading station,
		and maybe even upgrades of the Metatrader software version.
		Here you may want to store ticket numbers or other vital information
		to enable a "warm restart" of the strategy after an EA is stopped
		or started.

	*	In a more advanced usage, you can approximate some kinds of
		"semaphores" and lock-outs which are the computer-science
		ways of dealing with the multithreading problems.
		See GlobalVariableSetOnCondition() documentation.

		This OrderReliable library may preclude the need for *some* of that
		but don't necessarily count on it.


*/
//===========================================================================

#property copyright "Copyright © 2006, Derk Wehler"
#property link      "derkwehler@gmail.com"
#property library

#include <stdlib.mqh>
#include <stderror.mqh>

string 	OrderReliableVersion = "v3.1";
string 	OrderReliable_Fname = "OrderReliable fname unset";

int 	retry_attempts 		= 10;
double 	sleep_time 			= 8.0;
double 	sleep_maximum 		= 25.0;

int 	ErrorLevel 			= 3;

bool	UseLimitToMarket 	= false;
bool	UseForTesting 		= false;


//=============================================================================
//							 OrderSendReliable()
//
//  This is intended to be a drop-in replacement for OrderSend() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//	RETURN VALUE:
//     Ticket number or -1 under some error conditions.  
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Automatic normalization of Digits
//
//     * Automatically makes sure that stop levels are more than
//       the minimum stop distance, as given by the server. If they
//       are too close, they are adjusted.
//
//     * Automatically converts stop orders to market orders
//       when the stop orders are rejected by the server for
//       being to close to market.  NOTE: This intentionally
//       applies only to OP_BUYSTOP and OP_SELLSTOP,
//       OP_BUYLIMIT and OP_SELLLIMIT are not converted to market
//       orders and so for prices which are too close to current
//       this function is likely to loop a few times and return
//       with the "invalid stops" error message.
//       Note, the commentary in previous versions erroneously said
//       that limit orders would be converted.  Note also
//       that entering a BUYSTOP or SELLSTOP new order is distinct
//       from setting a stoploss on an outstanding order; use
//       OrderModifyReliable() for that.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Matt Kennel, 2006-05-28
//
//=============================================================================
int OrderSendReliable(string symbol, int cmd, double volume, double price,
                      int slippage, double stoploss, double takeprofit,
                      string comment, int magic, datetime expiration = 0,
                      color arrow_color = CLR_NONE)
{
	int ticket = -1;
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss,
			                   takeprofit, comment, magic, expiration, arrow_color);
			return(ticket);
		}
	}
	// ========================================================================
	
	OrderReliable_Fname = "OrderSendReliable";
	OrderReliablePrint("");
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("Attempted " + OrderType2String(cmd) + " " + volume +
	                   " lots @" + price + " sl:" + stoploss + " tp:" + takeprofit);

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	double realPoint = MarketInfo(symbol, MODE_POINT);
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(0, symbol, cmd, digits, point, sl, tp, bid, ask, false);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	

	// Normalize all price / stoploss / takeprofit to the proper # of digits.
	price = NormalizeDouble(price, digits);
	stoploss = NormalizeDouble(stoploss, digits);
	takeprofit = NormalizeDouble(takeprofit, digits);

	// Check stop levels, adjust if necessary
	EnsureValidStops(symbol, cmd, price, stoploss, takeprofit);

	int cnt;
	int err = GetLastError(); // clear the global variable.
	err = 0;
	bool exit_loop = false;
	bool limit_to_market = false;

	// limit/stop order.
	bool fixed_invalid_price = false;
	if (cmd == OP_BUYSTOP  ||  cmd == OP_SELLSTOP  ||  cmd == OP_BUYLIMIT  ||  cmd == OP_SELLLIMIT)
	{
		cnt = 0;
		while (!exit_loop)
		{
			ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss,
			                   takeprofit, comment, magic, expiration, arrow_color);
			err = GetLastError();

			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					break;

				// retryable errors
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_TRADE_CONTEXT_BUSY:
				case ERR_TRADE_TIMEOUT:
					cnt++;
					break;

				case ERR_PRICE_CHANGED:
				case ERR_REQUOTE:
					RefreshRates();
					continue;	// we can apparently retry immediately according to MT docs.

				case ERR_INVALID_PRICE:
				case ERR_INVALID_STOPS:
					cnt++;
					double servers_min_stop = MarketInfo(symbol, MODE_STOPLEVEL) * realPoint;
					double old_price;
					if (cmd == OP_BUYSTOP || cmd == OP_BUYLIMIT)
					{
						// If we are too close to put in a limit/stop order so go to market.
						if (MathAbs(ask - price) <= servers_min_stop)
						{
							if (UseLimitToMarket)
							{
								limit_to_market = true;
								exit_loop = true;
							}
							else
							{
								if (fixed_invalid_price)
								{
									if (cmd == OP_BUYSTOP)
									{
										price += point;
										if (stoploss > 0)   stoploss += point;
										if (takeprofit > 0) takeprofit += point;
										OrderReliablePrint("Pending BuyStop Order still has ERR_INVALID_STOPS, adding 1 pip; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
									else if (cmd == OP_BUYLIMIT)
									{
										price -= point;
										if (stoploss > 0)   stoploss -= point;
										if (takeprofit > 0) takeprofit -= point;
										OrderReliablePrint("Pending BuyLimit Order still has ERR_INVALID_STOPS, subtracting 1 pip; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
								}
								else
								{
									if (cmd == OP_BUYLIMIT)
									{
										old_price = price;
										price = ask - servers_min_stop;
										if (stoploss > 0)   stoploss += (price - old_price);
										if (takeprofit > 0) takeprofit += (price - old_price);
										OrderReliablePrint("Pending BuyLimit has ERR_INVALID_STOPS; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
									else if (cmd == OP_BUYSTOP)
									{
										old_price = price;
										price = ask + servers_min_stop;
										if (stoploss > 0)   stoploss += (price - old_price);
										if (takeprofit > 0) takeprofit += (price - old_price);
										OrderReliablePrint("Pending BuyStop has ERR_INVALID_STOPS; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
									fixed_invalid_price = true;
								}
								EnsureValidStops(symbol, cmd, price, stoploss, takeprofit);
							}
						}
					}
					else if (cmd == OP_SELLSTOP || cmd == OP_SELLLIMIT)
					{
						// If we are too close to put in a limit/stop order so go to market.
						if (MathAbs(bid - price) <= servers_min_stop)
						{
							if (UseLimitToMarket)
							{
								limit_to_market = true;
								exit_loop = true;
							}
							else
							{
								if (fixed_invalid_price)
								{
									if (cmd == OP_SELLSTOP)
									{
										price -= point;
										if (stoploss > 0)   stoploss -= point;
										if (takeprofit > 0) takeprofit -= point;
										OrderReliablePrint("Pending SellStop Order still has ERR_INVALID_STOPS, subtracting 1 pip; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
									else if (cmd == OP_SELLLIMIT)
									{
										price += point;
										if (stoploss > 0)   stoploss += point;
										if (takeprofit > 0) takeprofit += point;
										OrderReliablePrint("Pending SellLimit Order still has ERR_INVALID_STOPS, adding 1 pip; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
								}
								else
								{
									if (cmd == OP_SELLSTOP)
									{
										old_price = price;
										price = bid - servers_min_stop;
										if (stoploss > 0)   stoploss -= (old_price - price);
										if (takeprofit > 0) takeprofit -= (old_price - price);
										OrderReliablePrint("Pending SellStop has ERR_INVALID_STOPS; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
									else if (cmd == OP_SELLLIMIT)
									{
										old_price = price;
										price = bid + servers_min_stop;
										if (stoploss > 0)   stoploss -= (old_price - price);
										if (takeprofit > 0) takeprofit -= (old_price - price);
										OrderReliablePrint("Pending SellLimit has ERR_INVALID_STOPS; new price = " + DoubleToStr(price, digits));
										if (stoploss > 0  ||  takeprofit > 0)
											OrderReliablePrint("NOTE: SL (now " + DoubleToStr(stoploss, digits) + ") & TP (now " + DoubleToStr(takeprofit, digits) + ") were adjusted proportionately");
									}
									fixed_invalid_price = true;
								}
								EnsureValidStops(symbol, cmd, price, stoploss, takeprofit);
							}
						}
					}
					break;

				case ERR_INVALID_TRADE_PARAMETERS:
				default:
					// an apparently serious error.
					exit_loop = true;
					break;

			}  // end switch

			if (cnt > retry_attempts)
				exit_loop = true;

			if (exit_loop)
			{
				if (err != ERR_NO_ERROR  &&  err != ERR_NO_RESULT)
				{
					OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
				}
				if (cnt > retry_attempts)
				{
					OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
				}
			}
			else
			{
				OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
				OrderReliablePrint("~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~");
				SleepRandomTime(sleep_time, sleep_maximum);
				RefreshRates();
			}
		}

		// We have now exited from loop.
		if (err == ERR_NO_ERROR  ||  err == ERR_NO_RESULT)
		{
			OrderReliablePrint("Ticket #" + ticket + ": Successful " + OrderType2String(cmd) + " order placed, details follow.");
			OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
			OrderPrint();
			OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
			OrderReliablePrint("");
			return(ticket); // SUCCESS!
		}
		if (!limit_to_market)
		{
			OrderReliablePrint("Failed to execute stop or limit order after " + retry_attempts + " retries");
			OrderReliablePrint("Failed trade: " + OrderType2String(cmd) + " " + DoubleToStr(volume, 2) + " lots  " + symbol +
			                   "@" + price + " tp@" + takeprofit + " sl@" + stoploss);
			OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
			OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
			OrderReliablePrint("");
			return(-1);
		}
	}  // end

	if (limit_to_market)
	{
		OrderReliablePrint("Going from stop/limit order to market order because market is too close.");
		if ((cmd == OP_BUYSTOP) || (cmd == OP_BUYLIMIT))
		{
			cmd = OP_BUY;
			price = ask;
		}
		else if ((cmd == OP_SELLSTOP) || (cmd == OP_SELLLIMIT))
		{
			cmd = OP_SELL;
			price = bid;
		}
	}

	// we now have a market order.
	err = GetLastError(); // so we clear the global variable.
	err = 0;
	ticket = -1;
	exit_loop = false;

	if ((cmd == OP_BUY) || (cmd == OP_SELL))
	{
		cnt = 0;
		while (!exit_loop)
		{
			ticket = OrderSend(symbol, cmd, volume, price, slippage,
			                   stoploss, takeprofit, comment, magic,
			                   expiration, arrow_color);
			err = GetLastError();

			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					break;

				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_INVALID_PRICE:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_TRADE_CONTEXT_BUSY:
				case ERR_TRADE_TIMEOUT:
					cnt++; // a retryable error
					break;

				case ERR_PRICE_CHANGED:
				case ERR_REQUOTE:
					RefreshRates();
					continue; // we can apparently retry immediately according to MT docs.

				default:
					// an apparently serious, unretryable error.
					exit_loop = true;
					break;
			}  

			if (cnt > retry_attempts)
				exit_loop = true;

			if (!exit_loop)
			{
				OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
				OrderReliablePrint("~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~");
				SleepRandomTime(sleep_time, sleep_maximum);
				RefreshRates();
			}
			else
			{
				if (err != ERR_NO_ERROR  &&  err != ERR_NO_RESULT)
				{
					OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
				}
				if (cnt > retry_attempts)
				{
					OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
				}
			}
		}

		// we have now exited from loop.
		if (err == ERR_NO_ERROR  ||  err == ERR_NO_RESULT)
		{
			OrderReliablePrint("Ticket #" + ticket + ": Successful " + OrderType2String(cmd) + " order placed, details follow.");
			OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
			OrderPrint();
			OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
			OrderReliablePrint("");
			return(ticket); // SUCCESS!
		}
		OrderReliablePrint("Failed to execute OP_BUY/OP_SELL, after " + retry_attempts + " retries");
		OrderReliablePrint("Failed trade: " + OrderType2String(cmd) + " " + DoubleToStr(volume, 2) + " lots  " + symbol +
		                   "@" + price + " tp@" + takeprofit + " sl@" + stoploss);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
		OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
		OrderReliablePrint("");
		return(-1);
	}
}


//=============================================================================
//							 OrderSendReliableMKT()
//
//  This is intended to be an alternative for OrderSendReliable() which
//  will update market-orders in the retry loop with the current Bid or Ask.
//  Hence with market orders there is a greater likelihood that the trade will
//  be executed versus OrderSendReliable(), and a greater likelihood it will
//  be executed at a price worse than the entry price due to price movement.
//
//  RETURN VALUE:
//     Ticket number or -1 under some error conditions.  Check
//     final error returned by Metatrader with OrderReliableLastErr().
//     This will reset the value from GetLastError(), so in that sense it cannot
//     be a total drop-in replacement due to Metatrader flaw.
//
//  FEATURES:
//     * Most features of OrderSendReliable() but for market orders only.
//       Command must be OP_BUY or OP_SELL, and specify Bid or Ask at
//       the time of the call.
//
//     * If price moves in an unfavorable direction during the loop,
//       e.g. from requotes, then the slippage variable it uses in
//       the real attempt to the server will be decremented from the passed
//       value by that amount, down to a minimum of zero.   If the current
//       price is too far from the entry value minus slippage then it
//       will not attempt an order, and it will signal, manually,
//       an ERR_INVALID_PRICE (displayed to log as usual) and will continue
//       to loop the usual number of times.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//	   Matt Kennel, 2006-08-16
//
//=============================================================================
int OrderSendReliableMKT(string symbol, int cmd, double volume, double price,
                         int slippage, double stoploss, double takeprofit,
                         string comment, int magic, datetime expiration = 0,
                         color arrow_color = CLR_NONE)
{
	int ticket = -1;
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss,
			                   takeprofit, comment, magic, expiration, arrow_color);
			return(ticket);
		}
	}
	// ========================================================================
	
	// Cannot use this function for pending orders
	if (cmd > OP_SELL)
	{
		ticket = OrderSendReliable(symbol, cmd, volume, price, slippage, 0, 0, 
								   comment, magic, expiration, arrow_color);
		return(ticket);
	}
	
	OrderReliable_Fname = "OrderSendReliableMKT";
	OrderReliablePrint("");
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("Attempted " + OrderType2String(cmd) + " " + volume +
	                   " lots @" + price + " sl:" + stoploss + " tp:" + takeprofit);

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(0, symbol, cmd, digits, point, sl, tp, bid, ask, false);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	
	price = NormalizeDouble(price, digits);
	stoploss = NormalizeDouble(stoploss, digits);
	takeprofit = NormalizeDouble(takeprofit, digits);
	EnsureValidStops(symbol, cmd, price, stoploss, takeprofit);

	int cnt;
	int err = GetLastError(); // clear the global variable.
	err = 0;
	bool exit_loop = false;

	if ((cmd == OP_BUY) || (cmd == OP_SELL))
	{
		cnt = 0;
		while (!exit_loop)
		{
			double pnow = price;
			int slippagenow = slippage;
			if (cmd == OP_BUY)
			{
				// modification by Paul Hampton-Smith to replace RefreshRates()
				pnow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
				if (pnow > price)
				{
					slippagenow = slippage - (pnow - price) / point;
				}
			}
			else if (cmd == OP_SELL)
			{
				// modification by Paul Hampton-Smith to replace RefreshRates()
				pnow = NormalizeDouble(MarketInfo(symbol, MODE_BID), MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
				if (pnow < price)
				{
					// moved in an unfavorable direction
					slippagenow = slippage - (price - pnow) / point;
				}
			}
			if (slippagenow > slippage) slippagenow = slippage;
			if (slippagenow >= 0)
			{

				ticket = OrderSend(symbol, cmd, volume, pnow, slippagenow,
				                   stoploss, takeprofit, comment, magic,
				                   expiration, arrow_color);
				err = GetLastError();
			}
			else
			{
				// too far away, manually signal ERR_INVALID_PRICE, which
				// will result in a sleep and a retry.
				err = ERR_INVALID_PRICE;
			}

			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					break;

				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_INVALID_PRICE:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_TRADE_CONTEXT_BUSY:
				case ERR_TRADE_TIMEOUT:
					cnt++; // a retryable error
					break;

				case ERR_PRICE_CHANGED:
				case ERR_REQUOTE:
					// Paul Hampton-Smith removed RefreshRates() here and used MarketInfo() above instead
					continue; // we can apparently retry immediately according to MT docs.

				default:
					// an apparently serious, unretryable error.
					exit_loop = true;
					break;

			}  // end switch

			if (cnt > retry_attempts)
				exit_loop = true;

			if (!exit_loop)
			{
				OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
				OrderReliablePrint("~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~");
				SleepRandomTime(sleep_time, sleep_maximum);
			}
			else
			{
				if (err != ERR_NO_ERROR  &&  err != ERR_NO_RESULT)
				{
					OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
				}
				if (cnt > retry_attempts)
				{
					OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
				}
			}
		}

		// we have now exited from loop.
		if (err == ERR_NO_ERROR  ||  err == ERR_NO_RESULT)
		{
			OrderReliablePrint("Ticket #" + ticket + ": Successful " + OrderType2String(cmd) + " order placed, details follow.");
			OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
			OrderPrint();
			OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
			OrderReliablePrint("");
			return(ticket); // SUCCESS!
		}
		OrderReliablePrint("Failed to execute OP_BUY/OP_SELL, after " + retry_attempts + " retries");
		OrderReliablePrint("Failed trade: " + OrderType2String(cmd) + " " + DoubleToStr(volume, 2) + " lots  " + symbol +
		                   "@" + price + " tp@" + takeprofit + " sl@" + stoploss);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
		OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
		OrderReliablePrint("");
		return(-1);
	}
}


//=============================================================================
//							 OrderSendReliable2Step()
//
//  Some brokers don't allow the SL and TP settings as part of the initial
//  market order (Water House Capital).  Therefore, this routine will first
//  place the market order with no stop-loss and take-profit but later
//  update the order accordingly
//
//	RETURN VALUE:
//     Same as OrderSendReliable; the ticket number
//
//  NOTES:
//     Order will not be updated if an error continues during
//     OrderSendReliableMKT.  No additional information will be logged
//     since OrderSendReliableMKT would have already logged the error
//     condition
//
//  ORIGINAL AUTHOR AND DATE:
//     Jack Tomlinson, 2007-05-29
//
//=============================================================================
int OrderSendReliable2Step(string symbol, int cmd, double volume, double price,
                           int slippage, double stoploss, double takeprofit,
                           string comment, int magic, datetime expiration = 0,
                           color arrow_color = CLR_NONE)
{
	int ticket = -1;
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			ticket = OrderSend(symbol, cmd, volume, price, slippage, 0, 0, 
							   comment, magic, expiration, arrow_color);

			OrderModify(ticket, 0, stoploss, takeprofit, 0, arrow_color);
			
			return(ticket);
		}
	}
	// ========================================================================
	
	OrderReliable_Fname = "OrderSendReliable2Step";
	OrderReliablePrint("");
	OrderReliablePrint("Doing OrderSendReliable, followed by OrderModifyReliable:");

	ticket = OrderSendReliable(symbol, cmd, volume, price, slippage,
	                                   0, 0, comment, magic, expiration, arrow_color);

	if (stoploss != 0 || takeprofit != 0)
	{
		if (ticket >= 0)
			OrderModifyReliable(ticket, price,	stoploss, takeprofit, 0, arrow_color);
	}
	else
		OrderReliablePrint("Skipping OrderModifyReliable because no SL or TP specified.");

	return(ticket);
}


//=============================================================================
//							 OrderModifyReliable()
//
//  This is intended to be a drop-in replacement for OrderModify() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//
//  ORIGINAL AUTHOR AND DATE:
//     Matt Kennel, 2006-05-28
//
//=============================================================================
bool OrderModifyReliable(int ticket, double price, double stoploss,
                         double takeprofit, datetime expiration,
                         color arrow_color = CLR_NONE)
{
	bool result = false;
	bool non_retryable_error = false;

	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderModify(ticket, price, stoploss,
			                     takeprofit, expiration, arrow_color);
			return(result);
		}
	}
	// ========================================================================
	
	OrderReliable_Fname = "OrderModifyReliable";
	OrderReliablePrint("");
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("Attempted modify of #" + ticket + " price:" + price +
	                   " sl:" + stoploss + " tp:" + takeprofit);

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		
	// Below, we call "EnsureValidStops".  If the order we are modifying 
	// is a pending order, then we should use the price passed in.  But 
	// if it's an open order, the price passed in is irrelevant; we need 
	// to use the appropriate bid or ask, so get those...
	double prc = price;
	if (type == OP_BUY)			prc = bid;
	else if (type == OP_SELL)	prc = ask;

	// With the requisite info, we can do error checking on SL & TP
	prc = NormalizeDouble(prc, digits);
	price = NormalizeDouble(price, digits);
	stoploss = NormalizeDouble(stoploss, digits);
	takeprofit = NormalizeDouble(takeprofit, digits);
	
	// If SL/TP are not changing then send in zeroes to EnsureValidStops(),
	// so that it does not bother to try to change them
	double newSL = stoploss;
	double newTP = takeprofit;
	if (stoploss == sl)		newSL = 0;
	if (takeprofit == tp)	newTP = 0;
	EnsureValidStops(symbol, type, prc, newSL, newTP, false);
	if (stoploss != sl)		stoploss = newSL;
	if (takeprofit != tp)	takeprofit = newTP;


	int cnt = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;

	while (!exit_loop)
	{
		result = OrderModify(ticket, price, stoploss,
		                     takeprofit, expiration, arrow_color);
		err = GetLastError();

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderClose() returned false; exiting");
					break;

				case ERR_NO_RESULT:
					// Modification to same value as before
					// See below for reported result
					exit_loop = true;
					break;

				// Shouldn't be any reason stops are invalid (and yet I've seen it); try again
				case ERR_INVALID_STOPS:	
					OrderReliablePrint("OrderModifyReliable, ERR_INVALID_STOPS, should not happen; stops already adjusted");
				//	EnsureValidStops(symbol, price, stoploss, takeprofit);
				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for modify this is a retryable error, I hope.
				case ERR_INVALID_PRICE:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_TOO_MANY_REQUESTS:
				case ERR_TRADE_CONTEXT_BUSY:
					cnt++; 	// a retryable error
					break;

				case ERR_TRADE_MODIFY_DENIED:
					// This one may be important; have to Ensure Valid Stops AND valid price (for pends)
					break;
				
				case ERR_PRICE_CHANGED:
				case ERR_REQUOTE:
					RefreshRates();
					continue; 	// we can apparently retry immediately according to MT docs.

				default:
					// an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (!exit_loop)
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
			OrderReliablePrint("~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~");
			SleepRandomTime(sleep_time, sleep_maximum);
			RefreshRates();
		}
		else
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: "  + OrderReliableErrTxt(err));
		}
	}

	// we have now exited from loop.
	if (err == ERR_NO_RESULT)
	{
		OrderReliablePrint("Server reported modify order did not actually change parameters.");
		OrderReliablePrint("Redundant modification: " + ticket + " " + symbol +
		                   "@" + price + " tp@" + takeprofit + " sl@" + stoploss);
		OrderReliablePrint("Suggest modifying code logic to avoid.");
	}
	
	if (result)
	{
		OrderReliablePrint("Ticket #" + ticket + ": Modification successful, updated trade details follow.");
		OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
		OrderPrint();
	}
	else
	{
		OrderReliablePrint("Failed to execute modify after " + retry_attempts + " retries");
		OrderReliablePrint("Failed modification: "  + ticket + " " + symbol +
	                   	"@" + price + " tp@" + takeprofit + " sl@" + stoploss);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("");
	return(result);
}


//=============================================================================
//                            OrderCloseReliable()
//
//  This is intended to be a drop-in replacement for OrderClose() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Derk Wehler, 2006-07-19
//
//=============================================================================
bool OrderCloseReliable(int ticket, double volume, double price,
                        int slippage, color arrow_color = CLR_NONE)
{
	bool result = false;
	bool non_retryable_error = false;
	
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderClose(ticket, volume, price, slippage, arrow_color);
			return(result);
		}
	}
	// ========================================================================
	
	OrderReliable_Fname = "OrderCloseReliable";
	OrderReliablePrint("");
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("Attempted close of #" + ticket + " price:" + price +
	                   " lots:" + volume + " slippage:" + slippage);


	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	if (type != OP_BUY && type != OP_SELL)
	{
		OrderReliablePrint("Error: Trying to close ticket #" + ticket + ", which is " + OrderType2String(type) + ", not OP_BUY or OP_SELL");
		return(false);
	}


	int cnt = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;

	while (!exit_loop)
	{
		result = OrderClose(ticket, volume, price, slippage, arrow_color);
		err = GetLastError();

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderClose() returned false; exiting");
					break;

				case ERR_NO_RESULT:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_RESULT received, but OrderClose() returned false; exiting");
					break;

				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for close this is a retryable error, I hope.
				case ERR_TRADE_DISABLED:
				case ERR_PRICE_CHANGED:
				case ERR_INVALID_PRICE:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_REQUOTE:
				case ERR_TOO_MANY_REQUESTS:	
				case ERR_TRADE_CONTEXT_BUSY:
					cnt++; 	// a retryable error
					break;

				default:
					// Any other error is an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (!exit_loop)
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
			OrderReliablePrint("~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~");
			SleepRandomTime(sleep_time, sleep_maximum);
		}

		if (exit_loop)
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
		}
	}

	// we have now exited from loop.
	if (result)
	{
/*		if (OrderStillOpen(ticket))
		{
			OrderReliablePrint("Close result reported success, but order remains!  Must re-try close from EA logic!");
			OrderReliablePrint("Close Failed: Ticket #" + ticket + ", Price: " +
		                   		price + ", Slippage: " + slippage);
			OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
			result = false;
		}
		else
*/
			OrderReliablePrint("Successful close of Ticket #" + ticket + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
	}
	else
	{
		OrderReliablePrint("Failed to execute close after " + retry_attempts + " retries");
		OrderReliablePrint("Failed close: Ticket #" + ticket + ", Price: " +
	                   		price + ", Slippage: " + slippage);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("");
	return(result);
}


//=============================================================================
//                           OrderCloseReliableMKT()
//
//	This function is intended for closing orders ASAP; the principal 
//  difference is that in its internal retry-loop, it uses the new "Bid" 
//  and "Ask" real-time variables as opposed to the OrderCloseReliable(), 
//  which uses only the price given upon entry to the routine.  More likely 
//  to get the order closed if price moves, but more likely to "slip"
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Derk Wehler, 2009-04-03
//
//=============================================================================
bool OrderCloseReliableMKT(int ticket, double volume, double price,
						   int slippage, color arrow_color = CLR_NONE)
{
	bool result = false;
	bool non_retryable_error = false;
	
	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderClose(ticket, volume, price, slippage, arrow_color);
			return(result);
		}
	}
	// ========================================================================
	
	OrderReliable_Fname = "OrderCloseReliableMKT";
	OrderReliablePrint("");
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("Attempted close of #" + ticket + " initial price:" + price +
	                   " lots:" + volume + " slippage:" + slippage);


	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	if (type != OP_BUY && type != OP_SELL)
	{
		OrderReliablePrint("Error: Trying to close ticket #" + ticket + ", which is " + OrderType2String(type) + ", not OP_BUY or OP_SELL");
		return(false);
	}


	int cnt = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;
	double pnow;
	int slippagenow;

	while (!exit_loop)
	{
		if (type == OP_BUY)
		{
			pnow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
			if (pnow > price)
			{
				// Do not allow slippage to go negative; will cause error
				slippagenow = MathMax(0, slippage - (pnow - price) / point);
			}
		}
		else if (type == OP_SELL)
		{
			pnow = NormalizeDouble(MarketInfo(symbol, MODE_BID), MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask
			if (pnow < price)
			{
				// Do not allow slippage to go negative; will cause error
				slippagenow = MathMax(0, slippage - (price - pnow) / point);
			}
		}

		result = OrderClose(ticket, volume, pnow, slippagenow, arrow_color);
		err = GetLastError();

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderClose() returned false; exiting");
					break;

				case ERR_NO_RESULT:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_RESULT received, but OrderClose() returned false; exiting");
					break;

				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for close this is a retryable error, I hope.
				case ERR_TRADE_DISABLED:
				case ERR_PRICE_CHANGED:
				case ERR_INVALID_PRICE:
				case ERR_OFF_QUOTES:
				case ERR_BROKER_BUSY:
				case ERR_REQUOTE:
				case ERR_TOO_MANY_REQUESTS:	
				case ERR_TRADE_CONTEXT_BUSY:
					cnt++; 	// a retryable error
					break;

				default:
					// Any other error is an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (!exit_loop)
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
			OrderReliablePrint("~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~");
			SleepRandomTime(sleep_time, sleep_maximum);
		}

		if (exit_loop)
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
		}
	}

	// we have now exited from loop.
	if (result)
	{
		OrderReliablePrint("Successful close of Ticket #" + ticket + " @ " + pnow + "     [ Last error: " + OrderReliableErrTxt(err) + " ]");
	}
	else
	{
		OrderReliablePrint("Failed to execute close after " + retry_attempts + " retries");
		OrderReliablePrint("Failed close: Ticket #" + ticket + " @ Price: " +
	                   		pnow + " (Initial Price: " + price + "), Slippage: " + 
	                   		slippagenow + " (Initial Slippage: " + slippage + ")");
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("");
	return(result);
}


//=============================================================================
//                            OrderDeleteReliable()
//
//  This is intended to be a drop-in replacement for OrderDelete() which,
//  one hopes, is more resistant to various forms of errors prevalent
//  with MetaTrader.
//
//  RETURN VALUE:
//     TRUE if successful, FALSE otherwise
//
//
//  FEATURES:
//     * Re-trying under some error conditions, sleeping a random
//       time defined by an exponential probability distribution.
//
//     * Displays various error messages on the log for debugging.
//
//  ORIGINAL AUTHOR AND DATE:
//     Derk Wehler, 2006-12-21
//
//=============================================================================
bool OrderDeleteReliable(int ticket)
{
	bool result = false;
	bool non_retryable_error = false;

	// ========================================================================
	// If testing or optimizing, there is no need to use this lib, as the 
	// orders are not real-world, and always get placed optimally.  By 
	// refactoring this option to be in this library, one no longer needs 
	// to create similar code in each EA.
	if (!UseForTesting)
	{
		if (IsOptimization()  ||  IsTesting())
		{
			result = OrderDelete(ticket);
			return(result);
		}
	}
	// ========================================================================
	
	OrderReliable_Fname = "OrderDeleteReliable";
	OrderReliablePrint("");
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("Attempted deletion of pending order, #" + ticket);
	

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// Get information about this order
	string symbol = "ALLOCATE";		// This is so it has memory space allocated
	int type;
	int digits;
	double point;
	double bid, ask;
	double sl, tp;
	GetOrderDetails(ticket, symbol, type, digits, point, sl, tp, bid, ask);
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		
	if (type == OP_BUY || type == OP_SELL)
	{
		OrderReliablePrint("error: Trying to close ticket #" + ticket +
		                   ", which is " + OrderType2String(type) +
		                   ", not OP_BUYSTOP, OP_SELLSTOP, OP_BUYLIMIT, or OP_SELLLIMIT");
		return(false);
	}


	int cnt = 0;
	int err = GetLastError(); // so we clear the global variable.
	err = 0;
	bool exit_loop = false;

	while (!exit_loop)
	{
		result = OrderDelete(ticket);
		err = GetLastError();

		if (result == true)
			exit_loop = true;
		else
		{
			switch (err)
			{
				case ERR_NO_ERROR:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_ERROR received, but OrderDelete() returned false; exiting");
					break;

				case ERR_NO_RESULT:
					exit_loop = true;
					OrderReliablePrint("ERR_NO_RESULT received, but OrderDelete() returned false; exiting");
					break;

				case ERR_COMMON_ERROR:
				case ERR_SERVER_BUSY:
				case ERR_NO_CONNECTION:
				case ERR_TOO_FREQUENT_REQUESTS:
				case ERR_TRADE_TIMEOUT:		// for delete this is a retryable error, I hope.
				case ERR_TRADE_DISABLED:
				case ERR_OFF_QUOTES:
				case ERR_PRICE_CHANGED:
				case ERR_BROKER_BUSY:
				case ERR_REQUOTE:
				case ERR_TOO_MANY_REQUESTS:
				case ERR_TRADE_CONTEXT_BUSY:
					cnt++; 	// a retryable error
					break;

				default:	// Any other error is an apparently serious, unretryable error.
					exit_loop = true;
					non_retryable_error = true;
					break;

			}  // end switch
		}

		if (cnt > retry_attempts)
			exit_loop = true;

		if (!exit_loop)
		{
			OrderReliablePrint("Result of attempt " + cnt + " of " + retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err));
			OrderReliablePrint("~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~  ~");
			SleepRandomTime(sleep_time, sleep_maximum);
		}
		else
		{
			if (cnt > retry_attempts)
				OrderReliablePrint("Retry attempts maxed at " + retry_attempts);
			else if (non_retryable_error)
				OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err));
		}
	}

	// we have now exited from loop.
	if (result)
	{
		OrderReliablePrint("Successful deletion of Ticket #" + ticket);
		return(true); // SUCCESS!
	}
	else
	{
		OrderReliablePrint("Failed to execute delete after " + retry_attempts + " retries");
		OrderReliablePrint("Failed deletion: Ticket #" + ticket);
		OrderReliablePrint("Last error: " + OrderReliableErrTxt(err));
	}
	OrderReliablePrint("•  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •  •");
	OrderReliablePrint("");
	return(result);
}



//=============================================================================
//=============================================================================
//								Utility Functions
//=============================================================================
//=============================================================================

string OrderReliableErrTxt(int err)
{
	return (err + "  ::  " + ErrorDescription(err));
}


// Defaut level is 3
// Use level = 1 to Print all but "Retry" messages
// Use level = 0 to Print nothing
void OrderReliableSetErrorLevel(int level)
{
	ErrorLevel = level;
}


void OrderReliablePrint(string s)
{
	// Print to log prepended with stuff;
	if (ErrorLevel >= 99 || (!(IsTesting() || IsOptimization())))
	{
		if (ErrorLevel > 0)
			Print(OrderReliable_Fname + " " + OrderReliableVersion + ":     " + s);
	}
}


string OrderType2String(int type)
{
	if (type == OP_BUY) 		return("BUY");
	if (type == OP_SELL) 		return("SELL");
	if (type == OP_BUYSTOP) 	return("BUY STOP");
	if (type == OP_SELLSTOP)	return("SELL STOP");
	if (type == OP_BUYLIMIT) 	return("BUY LIMIT");
	if (type == OP_SELLLIMIT)	return("SELL LIMIT");
	return("None (" + type + ")");
}


//=============================================================================
//                        EnsureValidStops()
//
//  Most MQ4 brokers have a minimum stop distance, which is the number of 
//  pips from price where a pending order can be placed or where a SL & TP 
//  can be placed.  THe purpose of this function is to detect when the 
//  requested SL or TP is too close, and to move it out automatically, so 
//  that we do not get ERR_INVALID_STOPS errors.
//
//  FUNCTION COMPLETELY OVERHAULED:
//     Derk Wehler, 2008-11-08
//
//=============================================================================
void EnsureValidStops(string symbol, int cmd, double price, double& sl, double& tp, bool isNewOrder=true)
{
	string prevName = OrderReliable_Fname;
	OrderReliable_Fname = "EnsureValidStops";
	
	double point = MarketInfo(symbol, MODE_POINT);
	
	// We only use point for StopLevel, and StopLevel is reported as 10 times
	// what you expect on a 5-digit broker, so leave it as is.
	//if (point == 0.001  ||  point == 0.00001)
	//	point *= 10;
		
	double 	orig_sl = sl;
	double 	orig_tp = tp;
	double 	new_sl, new_tp;
	int 	min_stop_level = MarketInfo(symbol, MODE_STOPLEVEL);
	double 	servers_min_stop = min_stop_level * point;
	double 	spread = MarketInfo(symbol, MODE_ASK) - MarketInfo(symbol, MODE_BID);
	//Print("        EnsureValidStops: Symbol = " + symbol + ",  servers_min_stop = " + servers_min_stop); 

	// Skip if no S/L (zero)
	if (sl != 0)
	{
		if (cmd % 2 == 0)	// we are long
		{
			// for pending orders, sl/tp can bracket price by servers_min_stop
			new_sl = price - servers_min_stop;
			//Print("        EnsureValidStops: new_sl [", new_sl, "] = price [", price, "] - servers_min_stop [", servers_min_stop, "]"); 
			
			// for market order, sl/tp must bracket bid/ask
			if (cmd == OP_BUY  &&  isNewOrder)
			{
				new_sl -= spread;	
				//Print("        EnsureValidStops: Minus spread [", spread, "]"); 
			}
			sl = MathMin(sl, new_sl);
		}
		else	// we are short
		{
			new_sl = price + servers_min_stop;	// we are short
			//Print("        EnsureValidStops: new_sl [", new_sl, "] = price [", price, "] + servers_min_stop [", servers_min_stop, "]"); 
			
			// for market order, sl/tp must bracket bid/ask
			if (cmd == OP_SELL  &&  isNewOrder)
			{
				new_sl += spread;	
				//Print("        EnsureValidStops: Plus spread [", spread, "]"); 
			}

			sl = MathMax(sl, new_sl);
		}
		sl = NormalizeDouble(sl, MarketInfo(symbol, MODE_DIGITS));
	}


	// Skip if no T/P (zero)
	if (tp != 0)
	{
		// check if we have to adjust the stop
		if (MathAbs(price - tp) <= servers_min_stop)
		{
			if (cmd % 2 == 0)	// we are long
			{
				new_tp = price + servers_min_stop;	// we are long
				tp = MathMax(tp, new_tp);
			}
			else	// we are short
			{
				new_tp = price - servers_min_stop;	// we are short
				tp = MathMin(tp, new_tp);
			}
			tp = NormalizeDouble(tp, MarketInfo(symbol, MODE_DIGITS));
		}
	}
	
	// notify if changed
	if (sl != orig_sl)
		OrderReliablePrint("SL was too close to brokers min distance (" + min_stop_level + "); moved SL to: " + sl);
	if (tp != orig_tp)
		OrderReliablePrint("TP was too close to brokers min distance (" + min_stop_level + "); moved TP to: " + tp);
		
	OrderReliable_Fname = prevName;
}


//=============================================================================
//                              SleepRandomTime()
//
//  This sleeps a random amount of time defined by an exponential
//  probability distribution. The mean time, in Seconds is given
//  in 'mean_time'.
//
//  This is the back-off strategy used by Ethernet.  This will
//  quantize in fiftieths of seconds, so don't call this with a too
//  small a number.  This returns immediately if we are backtesting
//  and does not sleep.
//
//=============================================================================
void SleepRandomTime(double mean_time, double max_time)
{
	if (IsTesting())
		return; 	// return immediately if backtesting.

	double tenths = MathCeil(mean_time / 0.1);		// 40
	if (tenths <= 0)
		return;

	int maxtenths = MathRound(max_time / 0.1);		// 250
	double p = 1.0 - 1.0 / tenths;					// 0.975

	// Always sleep at least one-hundredth second
	Sleep(20);

	// Now loop through and sleep 1/10th second a max of 
	// (10 * mean_time) times.  But break out "randomly".
	for (int i = 0; i < maxtenths; i++)
	{
		if (MathRand() > p*32768)
			break;

		// MathRand() returns in 0..32767
		Sleep(20);
	}
}


//=============================================================================
//                                LimitToMarket()
//
//  Setting to toggle what OrderSendReliable does with Stop or Limit orders
//  that are requested to be placed too close to the current price.  
//
//  When set True, it will turn any such conundrum from a stop/limit order 
//  into a simple market order
//
//  When set False, the library will alter the price of the Stop/Limit order\
//  just far enough to be able to place the order as a pending order.
//
//=============================================================================
void LimitToMarket(bool limit2market)
{
	UseLimitToMarket = limit2market;
}


//=============================================================================
//                        OrderReliableUseForTesting()
//
//  Setting to toggle whether this OrderReliable library is used in testing 
//  and optimization.  By default, it is set to false, and will thus just pass 
//  orders straight through.
//
//  When set true, it will use the full functions as normally all the time,
//  including testing / optimization.
//
//=============================================================================
void OrderReliableUseForTesting(bool use)
{
	UseForTesting = use;
}


//=============================================================================
//                              GetOrderDetails()
//
//  For some OrderReliable functions (such as Modify), we need to know some
//  things about the order (such as direction and symbol).  To do this, we 
//  need to select the order.  However, the caller may already have an order 
//  selected so we need to be responsible and put it back when done.
//
//  Return false if there is a problem, true otherwise.
//
//=============================================================================
bool GetOrderDetails(int ticket, string& symb, int& type, int& digits, 
					 double& point, double& sl, double& tp, double& bid, 
					 double& ask, bool exists=true)
{
	// If this is existing order, select it and get symbol and type
	if (exists)
	{
		int lastTicket = OrderTicket();
		if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
		{
			OrderReliablePrint("OrderSelect() error: " + ErrorDescription(GetLastError()));
			return(false);
		}
		symb = OrderSymbol();
		type = OrderType();
		tp = OrderTakeProfit();
		sl = OrderStopLoss();
		
		// Select back the prior ticket num in case caller was using it.
		if (lastTicket >= 0)
			OrderSelect(lastTicket, SELECT_BY_TICKET, MODE_TRADES);
	}
	
	// Get bid, ask, point & digits
	bid = NormalizeDouble(MarketInfo(symb, MODE_BID), MarketInfo(symb, MODE_DIGITS));
	ask = NormalizeDouble(MarketInfo(symb, MODE_ASK), MarketInfo(symb, MODE_DIGITS));
	point = MarketInfo(symb, MODE_POINT);
	if (point == 0.001  ||  point == 0.00001)
		point *= 10;
		
	digits = MarketInfo(symb, MODE_DIGITS);
	
	if (digits == 0)
	{
		string prevName = OrderReliable_Fname;
		OrderReliable_Fname = "GetDigits";
		OrderReliablePrint("error: MarketInfo(symbol, MODE_DIGITS) == 0");
		OrderReliable_Fname = prevName;
		return(false);
	}
	else if (exists)
	{
		tp = NormalizeDouble(tp, digits);
		sl = NormalizeDouble(sl, digits);
		bid = NormalizeDouble(bid, digits);
		ask = NormalizeDouble(ask, digits);
	}
	
	return(true);
}


