//============================================================================= // OrderReliable.mqh // // Copyright ? 2006, Derk Wehler ([email protected]) // // This file is simply LibOrderReliable as a header file instead of a library // // 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" // // *************************************************************************** // OrderReliable library MIT license // // Copyright (c) 2006 Derk Wehler // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // LICENSE LIMITATION // This MIT license is limited only for use of OrderReliable within // strategies generated by StrategyQuant. Any method from this library // that will be used outside of this source code will be governed // by GPL license. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // *************************************************************************** // *************************************************************************** // // 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'. // // //============================================================================= // // 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. // // 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. // //=========================================================================== // CHANGE LOG BEGUN 28 March, 2014 // Prior to this, Source OffSite was used to save changes // Start with revision 32, which is what SOS had as last change // // v32, 28 Mar 14: // Small bug fixes for Build 600 changes // // v33, 25 Apr 16: // Tiny adjustment made to GetOrderDetails() for non-forex pairs // // v34, 21 Jun 16: // Changed SleepRandomTime() to just sleep 200ms // // v35, 20 Jul 16: (important) // Added MySymbolConst2Val(), MySymbolVal2String(), necessary for correct // functioning of GetOrderDetails() // // v36, 23 Apr 19: (Mark Fric, SQ) // Added separate retry_attempts_bad_price variable that can configure repeat // attempts for ERR_INVALID_PRICE and ERR_INVALID_STOPS errors // //=========================================================================== //============================================================================= // 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=0, datetime expiration=0, color arrow_color=CLR_NONE) { OrderReliable_Fname = "OrderSendReliable"; int ticket = -1; price = NormalizeDouble(price, Digits); takeprofit = NormalizeDouble(takeprofit, Digits); stoploss = NormalizeDouble(stoploss, Digits); // ======================================================================== // 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); } } // ======================================================================== // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Get information about this order double realPoint = MarketInfo(symbol, MODE_POINT); double adjPoint = realPoint; if (adjPoint == 0.00001 || adjPoint == 0.001) adjPoint *= 10; int digits; double point, M=0; double bid, ask; double sl, tp; double priceNow=0; double hasSlippedBy=0; GetOrderDetails(0, symbol, cmd, digits, point, sl, tp, bid, ask, false); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OrderReliablePrint("Attempted " + OrderTypeToString(cmd) + " " + symbol + ": " + DoubleToStr(volume, 3) + " lots @" + DoubleToStr(price, digits+1) + " sl:" + DoubleToStr(stoploss, digits+1) + " tp:" + DoubleToStr(takeprofit, digits+1)); // 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, cnt_bad_price; GetLastError(); // clear the global variable. int err = 0; bool exit_loop = false; bool limit_to_market = false; bool fixed_invalid_price = false; // Concatenate to comment if enabled double symSpr = MarketInfo(symbol, MODE_ASK) - MarketInfo(symbol, MODE_BID); if (AddSpreadToComment) comment = comment + " (Spr: " + DoubleToStr(symSpr / adjPoint, 1) + ")"; // Limit/Stop order............................................................... if (cmd > OP_SELL) { cnt = 0; cnt_bad_price = 0; while (!exit_loop) { // = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = // Calculating our own slippage internally should not need to be done for pending orders; see market orders below // = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = OrderReliablePrint("About to call OrderSend(), comment = " + comment); 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: case ERR_TRADE_DISABLED: case ERR_PRICE_CHANGED: case ERR_REQUOTE: cnt++; break; case ERR_INVALID_PRICE: case ERR_INVALID_STOPS: cnt++; cnt_bad_price++; 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 (cnt_bad_price > retry_attempts_bad_price) exit_loop = true; if (exit_loop) { if (!limit_to_market) { if (err != ERR_NO_ERROR && err != ERR_NO_RESULT) OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err)); else if (cnt > retry_attempts) OrderReliablePrint("Retry attempts maxed at " + (string)retry_attempts +"("+DoubleToStr(retry_attempts_bad_price)+")"); } } else { OrderReliablePrint("Result of attempt " + (string)cnt + " of " + (string)retry_attempts+"("+DoubleToStr(retry_attempts_bad_price)+")" + ": Retryable error: " + OrderReliableErrTxt(err)); SleepRandomTime(sleep_time, sleep_maximum); RefreshRates(); } } // We have now exited from loop. if (err == ERR_NO_ERROR || err == ERR_NO_RESULT) { OrderReliablePrint("Ticket #" + (string)ticket + ": Successful " + OrderTypeToString(cmd) + " order placed with comment = " + comment + ", details follow."); if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) OrderReliablePrint("Could Not Select Ticket #" + (string)ticket); sqOrderPrint(); return(ticket); // SUCCESS! } if (!limit_to_market) { OrderReliablePrint("Failed to execute stop or limit order after " + (string)retry_attempts + " retries"); OrderReliablePrint("Failed trade: " + OrderTypeToString(cmd) + " " + DoubleToStr(volume, 2) + " lots " + symbol + "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(stoploss)); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); OrderReliablePrint(""); return(-1); } } // end if (limit_to_market) { OrderReliablePrint("Going from stop/limit order to market order because market is too close."); cmd %= 2; if (cmd == OP_BUY) price = ask; else price = bid; } // We now have a market order. err = GetLastError(); // so we clear the global variable. err = 0; ticket = -1; exit_loop = false; // Market order.......................................................... if (cmd == OP_BUY || cmd == OP_SELL) { cnt = 0; cnt_bad_price = 0; while (!exit_loop) { // = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = // Get current price and calculate slippage RefreshRates(); if (cmd == OP_BUY) { M = 1.0; priceNow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), (int)MarketInfo(symbol, MODE_DIGITS)); // Open @ Ask hasSlippedBy = (priceNow - price) / point; // (Adjusted Point) } else if (cmd == OP_SELL) { M = -1.0; priceNow = NormalizeDouble(MarketInfo(symbol, MODE_BID), (int)MarketInfo(symbol, MODE_DIGITS)); // Open @ Bid hasSlippedBy = (price - priceNow) / point; // (Adjusted Point) } // Check if slippage is more than caller's maximum allowed if (priceNow != price && hasSlippedBy > slippage) { // Actual price has slipped against us more than user allowed // Log error message, sleep, and try again OrderReliablePrint("Actual Price (Ask for buy, Bid for sell) = " + DoubleToStr(priceNow, Digits+1) + "; Slippage from Requested Price = " + DoubleToStr(hasSlippedBy, 1) + " pips. Retrying..."); err = ERR_PRICE_CHANGED; } else { if (priceNow != price) { // If the price has slipped "acceptably" (either negative or within // "Slippage" param), then we need to adjust the SL and TP accordingly if (stoploss != 0) stoploss += M * hasSlippedBy; if (takeprofit != 0) takeprofit += M * hasSlippedBy; OrderReliablePrint("Actual Price (Ask for buy, Bid for sell) = " + DoubleToStr(priceNow, Digits+1) + "; Requested Price = " + DoubleToStr(price, Digits) + "; Slippage from Requested Price = " + DoubleToStr(hasSlippedBy, 1) + " pips (\'positive slippage\'). Attempting order at market"); } OrderReliablePrint("About to call OrderSend(), comment = " + comment); ticket = OrderSend(symbol, cmd, volume, priceNow, (slippage - (int)hasSlippedBy), stoploss, takeprofit, comment, magic, expiration, arrow_color); err = GetLastError(); } // = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = switch (err) { case ERR_NO_ERROR: exit_loop = true; break; case ERR_INVALID_PRICE: if (cmd == OP_BUY) OrderReliablePrint("INVALID PRICE ERROR - Requested Price: " + DoubleToStr(price, Digits) + "; Ask = " + DoubleToStr(MarketInfo(symbol, MODE_ASK), Digits)); else OrderReliablePrint("INVALID PRICE ERROR - Requested Price: " + DoubleToStr(price, Digits) + "; Bid = " + DoubleToStr(MarketInfo(symbol, MODE_BID), Digits)); cnt++; // a retryable error cnt_bad_price++; break; case ERR_INVALID_STOPS: OrderReliablePrint("INVALID STOPS on attempted " + OrderTypeToString(cmd) + " : " + DoubleToStr(volume, 2) + " lots " + " @ " + DoubleToStr(price, Digits) + ", SL = " + DoubleToStr(stoploss, Digits) + ", TP = " + DoubleToStr(takeprofit, Digits)); cnt++; // a retryable error cnt_bad_price++; break; 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: case ERR_TRADE_DISABLED: case ERR_PRICE_CHANGED: case ERR_REQUOTE: cnt++; // a retryable error break; default: // an apparently serious, unretryable error. exit_loop = true; break; } if (cnt > retry_attempts) exit_loop = true; if (cnt_bad_price > retry_attempts_bad_price) 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 " + (string)retry_attempts +"("+DoubleToStr(retry_attempts_bad_price)+")"); } else { OrderReliablePrint("Result of attempt " + (string)cnt + " of " + (string)retry_attempts +"("+DoubleToStr(retry_attempts_bad_price)+")" + ": Retryable error: " + OrderReliableErrTxt(err)); SleepRandomTime(sleep_time, sleep_maximum); RefreshRates(); } } // We have now exited from loop; if successful, return ticket # if (err == ERR_NO_ERROR || err == ERR_NO_RESULT) { OrderReliablePrint("Ticket #" + (string)ticket + ": Successful " + OrderTypeToString(cmd) + " order placed with comment = " + comment + ", details follow."); if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) OrderReliablePrint("Could Not Select Ticket #" + (string)ticket); sqOrderPrint(); return(ticket); // SUCCESS! } // If not successful, log and return -1 OrderReliablePrint("Failed to execute OP_BUY/OP_SELL, after " + (string)retry_attempts + " retries"); OrderReliablePrint("Failed trade: " + OrderTypeToString(cmd) + " " + DoubleToStr(volume, 2) + " lots " + symbol + "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(stoploss)); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); } 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=0, datetime expiration=0, color arrow_color=CLR_NONE) { OrderReliable_Fname = "OrderSendReliable2Step"; int ticket = -1; double slipped = 0; price = NormalizeDouble(price, Digits); takeprofit = NormalizeDouble(takeprofit, Digits); stoploss = NormalizeDouble(stoploss, Digits); // ======================================================================== // 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, 0, arrow_color); if (!OrderModify(ticket, price, stoploss, takeprofit, expiration, arrow_color)) OrderReliablePrint("Order Modify of Ticket #" + (string)ticket + " FAILED"); return(ticket); } } // ======================================================================== 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) { double theOpenPrice = price; if (OrderSelect(ticket, SELECT_BY_TICKET)) { slipped = OrderOpenPrice() - price; theOpenPrice = OrderOpenPrice(); } else OrderReliablePrint("Failed to select ticket #" + (string)ticket + " after successful 2step placement; cannot recalculate SL & TP"); if (slipped > 0) { OrderReliablePrint("2step order slipped by: " + DoubleToStr(slipped, Digits) + "; SL & TP modified by same amount"); if (takeprofit != 0) takeprofit += slipped; if (stoploss != 0) stoploss += slipped; } OrderModifyReliable(ticket, theOpenPrice, stoploss, takeprofit, expiration, 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) { OrderReliable_Fname = "OrderModifyReliable"; 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); } } // ======================================================================== // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 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); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OrderReliablePrint("Attempted modify of #" + (string)ticket + " price:" + DoubleToStr(price, digits+1) + " sl:" + DoubleToStr(stoploss, digits+1) + " tp:" + DoubleToStr(takeprofit, digits+1) + " exp:" + TimeToStr(expiration)); // 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 cnt_bad_price = 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_PRICE: case ERR_INVALID_STOPS: OrderReliablePrint("OrderModifyReliable, ERR_INVALID_STOPS, Broker\'s Min Stop Level (in pips) = " + DoubleToStr(MarketInfo(symbol, MODE_STOPLEVEL) * Point / AdjPoint(symbol), 1)); // EnsureValidStops(symbol, price, stoploss, takeprofit); cnt_bad_price++; 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_OFF_QUOTES: case ERR_BROKER_BUSY: case ERR_TOO_MANY_REQUESTS: case ERR_TRADE_CONTEXT_BUSY: case ERR_TRADE_DISABLED: 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 (cnt_bad_price > retry_attempts_bad_price) exit_loop = true; if (!exit_loop) { OrderReliablePrint("Result of attempt " + (string)cnt + " of " + (string)retry_attempts+"("+DoubleToStr(retry_attempts_bad_price)+")" + ": Retryable error: " + OrderReliableErrTxt(err)); SleepRandomTime(sleep_time, sleep_maximum); RefreshRates(); } else { if (cnt > retry_attempts) OrderReliablePrint("Retry attempts maxed at " + (string)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: " + (string)ticket + " " + symbol + "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(stoploss)); OrderReliablePrint("Suggest modifying code logic to avoid."); } if (result) { OrderReliablePrint("Ticket #" + (string)ticket + ": Modification successful, updated trade details follow."); if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) OrderReliablePrint("Could Not Select Ticket #" + (string)ticket); sqOrderPrint(); } else { OrderReliablePrint("Failed to execute modify after " + (string)retry_attempts + " retries"); OrderReliablePrint("Failed modification: " + (string)ticket + " " + symbol + "@" + DoubleToStr(price) + " tp@" + DoubleToStr(takeprofit) + " sl@" + DoubleToStr(stoploss)); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); } 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) { OrderReliable_Fname = "OrderCloseReliable"; 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); } } // ======================================================================== // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 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); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OrderReliablePrint("Attempted close of #" + (string)ticket + " initial price:" + DoubleToStr(price, digits+1) + " lots:" + DoubleToStr(volume, 3) + " slippage:" + (string)slippage); if (type != OP_BUY && type != OP_SELL) { OrderReliablePrint("Error: Trying to close ticket #" + (string)ticket + ", which is " + OrderTypeToString(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 priceNow=0; double hasSlippedBy=0; while (!exit_loop) { // = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = : = // Get current price and calculate slippage RefreshRates(); if (type == OP_BUY) { priceNow = NormalizeDouble(MarketInfo(symbol, MODE_BID), (int)MarketInfo(symbol, MODE_DIGITS)); // Close @ Bid hasSlippedBy = (price - priceNow) / point; // (Adjusted Point) } else if (type == OP_SELL) { priceNow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), (int)MarketInfo(symbol, MODE_DIGITS)); // Close @ Ask hasSlippedBy = (priceNow - price) / point; // (Adjusted Point) } // Check if slippage is more than caller's maximum allowed if (priceNow != price && hasSlippedBy > slippage) { // Actual price has slipped against us more than user allowed // Log error message, sleep, and try again OrderReliablePrint("Actual Price (Bid for buy, Ask for sell) Value = " + DoubleToStr(priceNow, Digits) + "; Slippage from Requested Price = " + DoubleToStr(hasSlippedBy, 1) + " pips. Retrying..."); result = false; err = ERR_PRICE_CHANGED; } else { result = OrderClose(ticket, volume, priceNow, (slippage - (int)hasSlippedBy), 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"); OrderReliablePrint("If order did not actually close, error code is apparently wrong"); break; case ERR_NO_RESULT: exit_loop = true; OrderReliablePrint("ERR_NO_RESULT received, but OrderClose() returned false; exiting"); OrderReliablePrint("If order did not actually close, error code is apparently wrong"); break; case ERR_INVALID_PRICE: OrderReliablePrint("ERR_INVALID_PRICE received, but should not occur since we are refreshing rates"); cnt++; // a retryable error break; case ERR_PRICE_CHANGED: 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_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) { if (cnt > retry_attempts) OrderReliablePrint("Retry attempts maxed at " + (string)retry_attempts); else if (non_retryable_error) OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err)); } else { OrderReliablePrint("Result of attempt " + (string)cnt + " of " + (string)retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err)); SleepRandomTime(sleep_time, sleep_maximum); } } // We have now exited from loop if (result || err == ERR_NO_RESULT || err == ERR_NO_ERROR) { if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) OrderReliablePrint("Successful close of Ticket #" + (string)ticket + " [ Last error: " + OrderReliableErrTxt(err) + " ]"); else if (OrderCloseTime() > 0) // Then it closed ok OrderReliablePrint("Successful close of Ticket #" + (string)ticket + " [ Last error: " + OrderReliableErrTxt(err) + " ]"); else { OrderReliablePrint("Close result reported success (or failure, but w/ERR_NO_ERROR); yet order remains! Must re-try close from EA logic!"); OrderReliablePrint("Close Failed: Ticket #" + (string)ticket + ", Price: " + DoubleToStr(price) + ", Slippage: " + (string)slippage); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); result = false; } } else { OrderReliablePrint("Failed to execute close after " + (string)(cnt-1) + " retries"); OrderReliablePrint("Failed close: Ticket #" + (string)ticket + " @ Price: " + DoubleToStr(priceNow) + " (Requested Price: " + DoubleToStr(price) + "), Slippage: " + (string)slippage); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); } 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) { OrderReliable_Fname = "OrderCloseReliableMKT"; 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); } } // ======================================================================== // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 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); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OrderReliablePrint("Attempted close of #" + (string)ticket + " initial price:" + DoubleToStr(price, digits+1) + " lots:" + DoubleToStr(price, 3) + " slippage:" + (string)slippage); if (type != OP_BUY && type != OP_SELL) { OrderReliablePrint("Error: Trying to close ticket #" + (string)ticket + ", which is " + OrderTypeToString(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=0; int slippagenow=0; while (!exit_loop) { if (type == OP_BUY) { pnow = NormalizeDouble(MarketInfo(symbol, MODE_ASK), (int)MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask if (pnow > price) { // Do not allow slippage to go negative; will cause error slippagenow = (int)MathMax(0, slippage - (pnow - price) / point); } } else if (type == OP_SELL) { pnow = NormalizeDouble(MarketInfo(symbol, MODE_BID), (int)MarketInfo(symbol, MODE_DIGITS)); // we are buying at Ask if (pnow < price) { // Do not allow slippage to go negative; will cause error slippagenow = (int)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 " + (string)cnt + " of " + (string)retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err)); SleepRandomTime(sleep_time, sleep_maximum); } if (exit_loop) { if (cnt > retry_attempts) OrderReliablePrint("Retry attempts maxed at " + (string)retry_attempts); else if (non_retryable_error) OrderReliablePrint("Non-retryable error: " + OrderReliableErrTxt(err)); } } // we have now exited from loop. if (result) { if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) OrderReliablePrint("Successful close of Ticket #" + (string)ticket + " [ Last error: " + OrderReliableErrTxt(err) + " ]"); else if (OrderCloseTime() > 0) // Then it closed ok OrderReliablePrint("Successful close of Ticket #" + (string)ticket + " [ Last error: " + OrderReliableErrTxt(err) + " ]"); else { OrderReliablePrint("Close result reported success, but order remains! Must re-try close from EA logic!"); OrderReliablePrint("Close Failed: Ticket #" + (string)ticket + ", Price: " + DoubleToStr(price) + ", Slippage: " + (string)slippage); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); result = false; } } else { OrderReliablePrint("Failed to execute close after " + (string)retry_attempts + " retries"); OrderReliablePrint("Failed close: Ticket #" + (string)ticket + " @ Price: " + DoubleToStr(pnow) + " (Initial Price: " + DoubleToStr(price) + "), Slippage: " + (string)slippagenow + " (Initial Slippage: " + (string)slippage + ")"); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); } 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, color clr=CLR_NONE) { OrderReliable_Fname = "OrderDeleteReliable"; 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, clr); return(result); } } // ======================================================================== OrderReliablePrint("Attempted deletion of pending order, #" + (string)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 #" + (string)ticket + ", which is " + OrderTypeToString(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, clr); 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 " + (string)cnt + " of " + (string)retry_attempts + ": Retryable error: " + OrderReliableErrTxt(err)); SleepRandomTime(sleep_time, sleep_maximum); } else { if (cnt > retry_attempts) OrderReliablePrint("Retry attempts maxed at " + (string)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 #" + (string)ticket); return(true); // SUCCESS! } else { OrderReliablePrint("Failed to execute delete after " + (string)retry_attempts + " retries"); OrderReliablePrint("Failed deletion: Ticket #" + (string)ticket); OrderReliablePrint("Last error: " + OrderReliableErrTxt(err)); } return(result); }
发表评论