OrderSendReliable mq4
OrderSendReliable mq4
// 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:
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.
//===========================================================================
#include <stdlib.mqh>
#include <stderror.mqh>
string OrderReliableVersion = "V0_2_3";
//
// 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);
}
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;
}
}
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
return(OrderModifyReliable(ticket,price,stoploss,takeprofit,expiration,arrow_color)
);
int OrderReliableLastErr() {
return (_OR_err);
}
//===========================================================================
//===========================================================================
// Utility Functions
//===========================================================================
void OrderReliablePrint(string s) {
// Print to log prepended with stuff;
Print(OrderReliable_Fname+" "+OrderReliableVersion+":"+s);
}
double servers_min_stop =
MarketInfo(symbol,MODE_STOPLEVEL)*MarketInfo(symbol,MODE_POINT);
}
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 [email protected].
//
if (IsTesting()) return; // return immediately if backtesting.
//=============================================================================
// 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, [email protected] 2006-07-19
//
//=============================================================================
bool OrderCloseReliable(int ticket, double lots, double price,
int slippage, color arrow_color = CLR_NONE)
{
OrderReliable_Fname = "OrderCloseReliable";
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);
}
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 (!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));
return(false);
}
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.
}