//===========================================================================
//        OrderReliable.mqh
//
//   A library for MT4 expert advisors,  intended to give more
//   reliable order handling.   This is under development.
//
//                       Instructions:
//
//  Put this file in the experts/include directory.
//  Include the line
//     #include <OrderReliable_V?_?_?.mqh>
// 
// in the beginning of your EA with the question marks replaced by
// the actual version number and file name of the library, i.e.
// this 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.
//
// DO NOT COMPILE THIS FILE ON ITS OWN.  It is meaningless.  You only
// compile your "main" EA. 
//
//===========================================================================
//  Version:   0.2.4
//  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.
//
//      OrderModifyReliable
//            A replacement for OrderModify with more error handling, similar to
//            OrderSendReliable()
//
//      OrderModifyReliableSymbol()
//            Adds a "symbol" field to OrderModifyReliable (not drop in any more)
//            so that it can fix problems with stops/take profits which are
//            too close to market, as well as normalization problems.
// 
//      OrderReliableLastErr()
//            Returns the last error seen by an Order*Reliable() call.
//            NOTE: GetLastError()  WILL NOT WORK to return the error
//            after a call.  This is a flaw in Metatrader design, in that
//            GetLastError() also clears it.  Hence in this way
//            this library cannot be a total drop-in replacement.
// 
//===========================================================================
//  History:
//   2006-07-14:  ERR_TRADE_TIMEOUT now a retryable error for modify   0.2.4
//                only.  Unclear about what to do for send. 
//                Adds OrderReliableLastErr()
//
//   2006-06-07:  Version number now in log comments.  Docs updated.   0.2.3
//                OP_BUYLIMIT/OP_SELLLIMIT added. Increase retry time  
//   2006-06-07:  Fixed int/bool type mismatch compiler ignored        0.2.2
//   2006-06-06:  Returns success if modification is redundant         0.2.1
//   2006-06-06:  Added OrderModifyReliable                            0.2.0
//   2006-06-05:  Fixed idiotic typographical bug.                     0.1.2
//   2006-06-05:  Added ERR_TRADE_CONTEXT_BUSY to retryable errors.    0.1.1
//   2006-05-29:  Created.  Only OrderSendReliable() implemented       0.1
//       
// 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, 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. 
//  
// Copyright (2006), Matthew Kennel     
//===========================================================================


//===========================================================================
//                         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.
           
syntax:
         
    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) //    

returns: 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.
       * automatically converts limit orders to market orders 
         when the limit orders are rejected by the server for being
         to close to market.
       * displays various error messages on the log for debugging.
 
  Matt Kennel, mbkennel@gmail.com  2006-05-28
 */
  
//===========================================================================
#include <stdlib.mqh>
#include <stderror.mqh> 

string OrderReliableVersion = "V0_2_3"; 

int retry_attempts = 10; 
double sleep_time = 4.0, sleep_maximum = 25.0;  // in seconds

string OrderReliable_Fname = "OrderReliable fname unset";

static int _OR_err = 0; // 
//Matt's O-R stuff
int 	         O_R_Setting_max_retries 	= 10;
double 	      O_R_Setting_sleep_time 		= 4.0; /* seconds */
double 	      O_R_Setting_sleep_max 		= 15.0; /* seconds */

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) {

//
// check basic conditions see if trade is possible. 
//
   OrderReliable_Fname = "OrderSendReliable";
   OrderReliablePrint(" attempted "+OrderReliable_CommandString(cmd)+" "+volume+" lots @"+price+" sl:"+stoploss+" tp:"+takeprofit); 
   if (!IsConnected()) {
      OrderReliablePrint("error: IsConnected() == false");
      _OR_err = ERR_NO_CONNECTION; 
      return(-1);
   }
   if (IsStopped()) {
      OrderReliablePrint("error: IsStopped() == true");
      _OR_err = ERR_COMMON_ERROR; 
      return(-1);
   }
   int cnt = 0;
   while(!IsTradeAllowed() && cnt < retry_attempts) {
      OrderReliable_SleepRandomTime(sleep_time,sleep_maximum); 
      cnt++;
   }
   if (!IsTradeAllowed()) {
      OrderReliablePrint("error: no operation possible because IsTradeAllowed()==false, even after retries.");
      _OR_err = ERR_TRADE_CONTEXT_BUSY; 

      return(-1);  
   }

   // let's normalize all price/stoploss/takeprofit to the proper # of digits.
   int digits = MarketInfo(symbol,MODE_DIGITS);
   if (digits > 0) {
      price = NormalizeDouble(price,digits);
      stoploss = NormalizeDouble(stoploss,digits);
      takeprofit = NormalizeDouble(takeprofit,digits); 
   }
   if (stoploss != 0) OrderReliable_EnsureValidStop(symbol,price,stoploss); 

   int err = GetLastError(); // so we clear the global variable.  
   err = 0; 
   _OR_err = 0; 
   bool exit_loop = false;
   bool limit_to_market = false; 
   // limit/stop order. 
   int ticket=-1;

   if ((cmd == OP_BUYSTOP) || (cmd == OP_SELLSTOP) || (cmd == OP_BUYLIMIT) || (cmd == OP_SELLLIMIT)) {
       cnt = 0;
       while (!exit_loop) {
          if (IsTradeAllowed()) {
             ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss, takeprofit, comment, magic, expiration, arrow_color);
             err = GetLastError();
             _OR_err = err; 
          } else {
            cnt++;
          } 
          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: 
               cnt++; // a retryable error
               break;
            case ERR_PRICE_CHANGED:
            case ERR_REQUOTE:
               RefreshRates();
               continue; // we can apparently retry immediately according to MT docs.
            case ERR_INVALID_STOPS:
               double servers_min_stop = MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT); 
               if (cmd == OP_BUYSTOP) {
                 if (MathAbs(Ask - price) <= servers_min_stop)
                  // we are too close to put in a limit/stop order so go to market.
                    limit_to_market = true; 
               } else if (cmd == OP_SELLSTOP) {
                 if (MathAbs(Bid - price) <= servers_min_stop)
                    limit_to_market = true; 
               }
               exit_loop = true; 
               break; 
            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) {
               OrderReliablePrint("non-retryable error: " +OrderReliableErrTxt(err)); 
            }
            if (cnt > retry_attempts) {
               OrderReliablePrint("retry attempts maxed at "+retry_attempts); 
            }
          }
          if (!exit_loop) {
              OrderReliablePrint("retryable error ("+cnt+"/"+retry_attempts+"): " + OrderReliableErrTxt(err)); 
              OrderReliable_SleepRandomTime(sleep_time,sleep_maximum); 
              RefreshRates(); 
          }
       }
      // we have now exited from loop. 
      if (err == ERR_NO_ERROR) {
         OrderReliablePrint("apparently successful OP_BUYSTOP or OP_SELLSTOP order placed, details follow.");
         OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES); 
         OrderPrint(); 
         return(ticket); // SUCCESS! 
      } 
      if (!limit_to_market) {
         OrderReliablePrint("failed to execute stop or limit order after "+cnt+" retries");
         OrderReliablePrint("failed trade: " + OrderReliable_CommandString(cmd)+" "+symbol+"@"+price+" tp@"+takeprofit+" sl@"+stoploss); 
         OrderReliablePrint("last error: "+OrderReliableErrTxt(err)); 
         return(-1); 
      }
   }  // end     
  
   if (limit_to_market) {
      OrderReliablePrint("going from 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; 
   _OR_err = 0; 
   ticket=-1;

   if ((cmd == OP_BUY) || (cmd == OP_SELL)) {
       cnt = 0;
       while (!exit_loop) {
          if (IsTradeAllowed()) {
             ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss, takeprofit, comment, magic, expiration, arrow_color);
             err = GetLastError();
             _OR_err = err; 
          } else {
            cnt++;
          } 
          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: 
               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; 
          }  // end switch 

          if (cnt > retry_attempts) exit_loop = true; 
          if (!exit_loop) {
              OrderReliablePrint("retryable error ("+cnt+"/"+retry_attempts+"): " + OrderReliableErrTxt(err)); 
              OrderReliable_SleepRandomTime(sleep_time,sleep_maximum); 
              RefreshRates(); 
          }
          if (exit_loop) {
            if (err != ERR_NO_ERROR) {
               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) {
         OrderReliablePrint("apparently successful OP_BUY or OP_SELL order placed, details follow.");
          O_R_CheckForHistory(ticket);
         OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES); 
         OrderPrint(); 
         return(ticket); // SUCCESS! 
      } 
      OrderReliablePrint("failed to execute OP_BUY/OP_SELL, after "+cnt+" retries");
      OrderReliablePrint("failed trade: " + OrderReliable_CommandString(cmd)+" "+symbol+"@"+price+" tp@"+takeprofit+" sl@"+stoploss); 
      OrderReliablePrint("last error: "+OrderReliableErrTxt(err)); 
      return(-1); 
   }
}  // end     
   
   
bool OrderModifyReliable(int ticket, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=CLR_NONE) {
// Replacement for OrderModifyReliable 
    OrderReliable_Fname = "OrderModifyReliable";
  
   OrderReliablePrint(" attempted modify of #"+ticket+" price:"+price+" sl:"+stoploss+" tp:"+takeprofit); 
  
    if (!IsConnected()) {
      OrderReliablePrint("error: IsConnected() == false");
      _OR_err = ERR_NO_CONNECTION; 
      return(false);
   }
   if (IsStopped()) {
      OrderReliablePrint("error: IsStopped() == true");
      return(false);
   }
   int cnt = 0;
   while(!IsTradeAllowed() && cnt < retry_attempts) {
      OrderReliable_SleepRandomTime(sleep_time,sleep_maximum); 
      cnt++;
   }
   if (!IsTradeAllowed()) {
      OrderReliablePrint("error: no operation possible because IsTradeAllowed()==false, even after retries.");
      _OR_err = ERR_TRADE_CONTEXT_BUSY; 
      return(false);  
   }

   
   if (false) {
       // This section is 'nulled out', because
       // it would have to involve an 'OrderSelect()' to obtain
       // the symbol string, and that would change the global context of the
       // existing OrderSelect, and hence would not be a drop-in replacement
       // for OrderModify().
       //
       // See OrderModifyReliableSymbol() where the user passes in the Symbol 
       // manually.
       
       OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES);
       string symbol = OrderSymbol();
       int digits = MarketInfo(symbol,MODE_DIGITS);
       if (digits > 0) {
          price = NormalizeDouble(price,digits);
          stoploss = NormalizeDouble(stoploss,digits);
          takeprofit = NormalizeDouble(takeprofit,digits); 
       }
       if (stoploss != 0) OrderReliable_EnsureValidStop(symbol,price,stoploss); 
   }
   int err = GetLastError(); // so we clear the global variable.  
   err = 0; 
   _OR_err = 0; 
   bool exit_loop = false;
   cnt = 0;
   bool result = false;
   
    while (!exit_loop) {
      if (IsTradeAllowed()) {
        result = OrderModify(ticket,price, stoploss, takeprofit, expiration, arrow_color);
        err = GetLastError();
        _OR_err = err; 
      } else {
         cnt++;
      } 
      if (result == true) {
         exit_loop = true;
      } 
      switch (err) {
        case ERR_NO_ERROR:
           exit_loop = true;
           break;
        case ERR_NO_RESULT:
           // modification without changing a parameter. 
           // if you get this then you may want to change the code.
           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:   // for modify this is a retryable error, I hope. 
           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; 
      }  // end switch 

      if (cnt > retry_attempts) exit_loop = true; 
      if (!exit_loop) {
          OrderReliablePrint("retryable error ("+cnt+"/"+retry_attempts+"): " + OrderReliableErrTxt(err)); 
          OrderReliable_SleepRandomTime(sleep_time,sleep_maximum); 
              RefreshRates(); 
      }
      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); 
        }
      }
    }  
     // we have now exited from loop. 
    if ((result == true) || (err == ERR_NO_ERROR)) {
       OrderReliablePrint("apparently successful modification order, updated trade details follow.");
       OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES); 
       OrderPrint(); 
       return(true); // SUCCESS! 
    } 
    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."); 
       return(true);
    }
    OrderReliablePrint("failed to execute modify after "+cnt+" retries");
    OrderReliablePrint("failed modification: " +ticket+" "+symbol+"@"+price+" tp@"+takeprofit+" sl@"+stoploss); 
    OrderReliablePrint("last error: "+OrderReliableErrTxt(err)); 
    return(false); 
 
 
 }  
 
 
bool OrderModifyReliableSymbol(string symbol, int ticket, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=CLR_NONE) {
// This has the same calling sequence as OrderModify() except that the user must provide the symbol.
// This function will then be able to ensure proper normalization and stop levels.

   int digits = MarketInfo(symbol,MODE_DIGITS);
   if (digits > 0) {
      price = NormalizeDouble(price,digits);
      stoploss = NormalizeDouble(stoploss,digits);
      takeprofit = NormalizeDouble(takeprofit,digits); 
   }
   if (stoploss != 0) OrderReliable_EnsureValidStop(symbol,price,stoploss); 
   return(OrderModifyReliable(ticket,price,stoploss,takeprofit,expiration,arrow_color)); 
   
}
 
int OrderReliableLastErr() {
   return (_OR_err); 
}
//===========================================================================


//===========================================================================
//                          Utility Functions
//===========================================================================

string OrderReliableErrTxt(int err) {
   return (""+err+":"+ErrorDescription(err)); 
}

void OrderReliablePrint(string s) {
   // Print to log prepended with stuff;
   Print(OrderReliable_Fname+" "+OrderReliableVersion+":"+s);
}

void OrderReliable_EnsureValidStop(string symbol, double price, double& sl) {
// adjust stop loss so that it is legal.
   if (sl == 0) return;
   
   double servers_min_stop = MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT); 
   
   if (MathAbs(price-sl) <= servers_min_stop) {
      // we have to adjust the stop.
      if (price > sl) { // we are buying
         sl = price - servers_min_stop;
      } else if (price < sl) {
         sl = price + servers_min_stop;
      } else {
         OrderReliablePrint("EnsureValidStop: error, passed in price == sl, cannot adjust"); 
      }
      sl = NormalizeDouble(sl,MarketInfo(symbol,MODE_DIGITS)); 
   }
}

string OrderReliable_CommandString(int cmd) {
   if (cmd == OP_BUY) {
      return("OP_BUY");
   }
   if (cmd == OP_SELL) {
      return("OP_SELL");
   }
   if (cmd == OP_BUYSTOP) {
      return("OP_BUYSTOP");
   }
   if (cmd == OP_SELLSTOP) {
      return("OP_SELLSTOP");
   }
   if (cmd == OP_BUYLIMIT) {
      return("OP_BUYLIMIT");
   }
   if (cmd == OP_SELLLIMIT) {
      return("OP_SELLLIMIT");
   }
   return("(CMD=="+cmd+")"); 

}
void OrderReliable_SleepRandomTime(double mean_time, double max_time) {
// 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 tenths of seconds, so don't call this with a too 
// small a number.  This returns immediately if we are backtesting
// and does not sleep.
//
// Matt Kennel mbkennel@gmail.com.
//
   if (IsTesting()) return; // return immediately if backtesting.

   double tenths = MathCeil(mean_time / 0.1);
   if (tenths <= 0) return; 
    
   int maxtenths = MathRound(max_time/0.1); 
   double p = 1.0 - 1.0/tenths; 
     
   Sleep(1000); // one tenth of a second
   for(int i=0; i<maxtenths; i++)  {
      if (MathRand() > p*32768) break; 
      // MathRand() returns in 0..32767
      Sleep(1000); 
   }
}  


//=============================================================================
//							 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.
//
//
//	Derk Wehler, ashwoods155@yahoo.com  	2006-07-19
//
//=============================================================================
bool OrderCloseReliable(int ticket, double lots, double price, 
						int slippage, color arrow_color = CLR_NONE) 
{
	OrderReliable_Fname = "OrderCloseReliable";

	OrderReliablePrint(" attempted close of #" + ticket + " price:" + price + 
						" lots:" + lots + " slippage:" + slippage); 

	if (!IsConnected()) 
	{
		OrderReliablePrint("error: IsConnected() == false");
		_OR_err = ERR_NO_CONNECTION; 
		return(false);
	}
	
	if (IsStopped()) 
	{
		OrderReliablePrint("error: IsStopped() == true");
		return(false);
	}
	
	int cnt = 0;
	while(!IsTradeAllowed() && cnt < retry_attempts) 
	{
		OrderReliable_SleepRandomTime(sleep_time,sleep_maximum); 
		cnt++;
	}
	if (!IsTradeAllowed()) 
	{
		OrderReliablePrint("error: no operation possible because IsTradeAllowed()==false, even after retries.");
		_OR_err = ERR_TRADE_CONTEXT_BUSY; 
		return(false);  
	}


	int err = GetLastError(); // so we clear the global variable.  
	err = 0; 
	_OR_err = 0; 
	bool exit_loop = false;
	cnt = 0;
	bool result = false;
	
	while (!exit_loop) 
	{
		if (IsTradeAllowed()) 
		{
			result = OrderClose(ticket, lots, price, slippage, arrow_color);
			err = GetLastError();
			_OR_err = err; 
		} 
		else 
			cnt++;

		if (result == true) 
			exit_loop = true;

		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:		// for modify this is a retryable error, I hope. 
				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; 
				
		}  // end switch 

		if (cnt > retry_attempts) 
			exit_loop = true; 
			
		if (!exit_loop) 
		{
			OrderReliablePrint("retryable error (" + cnt + "/" + retry_attempts + 
								"): "  +  OrderReliableErrTxt(err)); 
			OrderReliable_SleepRandomTime(sleep_time,sleep_maximum); 
			RefreshRates(); 
		}
		
		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); 
		}
	}  
	
	// we have now exited from loop. 
	if ((result == true) || (err == ERR_NO_ERROR)) 
	{
		OrderReliablePrint("apparently successful modification order, updated trade details follow.");
		OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES); 
		OrderPrint(); 
		return(true); // SUCCESS! 
	} 
	
	OrderReliablePrint("failed to execute close after " + cnt + " retries");
	OrderReliablePrint("failed close: Ticket #" + ticket + ", Price: " + 
						price + ", Slippage: " + slippage); 
	OrderReliablePrint("last error: " + OrderReliableErrTxt(err)); 
	
	return(false);  
}
 
bool O_R_CheckForHistory(int ticket)
{
   //My thanks to Matt for this code. He also has the undying gratitude of all users of my trading robots
   
   int lastTicket = OrderTicket();

   int cnt = 0;
   int err = GetLastError(); // so we clear the global variable.
   err = 0;
   bool exit_loop = false;
   bool success=false;

   while (!exit_loop) {
      /* loop through open trades */
      int total=OrdersTotal();
      for(int c = 0; c < total; c++) {
         if(OrderSelect(c,SELECT_BY_POS,MODE_TRADES) == true) {
            if (OrderTicket() == ticket) {
               success = true;
               exit_loop = true;
            }
         }
      }
      if (cnt > 3) {
         /* look through history too, as order may have opened and closed immediately */
         total=OrdersHistoryTotal();
         for(c = 0; c < total; c++) {
            if(OrderSelect(c,SELECT_BY_POS,MODE_HISTORY) == true) {
               if (OrderTicket() == ticket) {
                  success = true;
                  exit_loop = true;
               }
            }
         }
      }

      cnt = cnt+1;
      if (cnt > O_R_Setting_max_retries) {
         exit_loop = true;
      }
      if (!(success || exit_loop)) {
         Print("Did not find #"+ticket+" in history, sleeping, then doing retry #"+cnt);
         O_R_Sleep(O_R_Setting_sleep_time, O_R_Setting_sleep_max);
      }
   }
   // Select back the prior ticket num in case caller was using it.
   if (lastTicket >= 0) {
      OrderSelect(lastTicket, SELECT_BY_TICKET, MODE_TRADES);
   }
   if (!success) {
      Print("Never found #"+ticket+" in history! crap!");
   }
   return(success);
}//End bool O_R_CheckForHistory(int ticket)

//=============================================================================
//                              O_R_Sleep()
//
//  This sleeps a random amount of time defined by an exponential
//  probability distribution. The mean time, in Seconds is given
//  in 'mean_time'.
//  This returns immediately if we are backtesting
//  and does not sleep.
//
//=============================================================================
void O_R_Sleep(double mean_time, double max_time)
{
   if (IsTesting()) {
      return;   // return immediately if backtesting.
   }

   double p = (MathRand()+1) / 32768.0;
   double t = -MathLog(p)*mean_time;
   t = MathMin(t,max_time);
   int ms = t*1000;
   if (ms < 10) {
      ms=10;
   }
   Sleep(ms);
}//End void O_R_Sleep(double mean_time, double max_time)