I've implemented a prototype of an Expert Advisor for Metatrader 4, initially using the MT4 ADX indicator together with the MT4 Stochastic indicator for signal filtering. As part of the Expert Advisor, I've implemented a sort of automated trailing stop.
An example of the code is included below. For purpose of testing, I've extracted this and updated from the initial implementation in the Expert Advisor. It's failing similarly under live trading in a FOREX demo account.
// simplified prototype for automated trailing stop
// License: BSD License
// https://spdx.org/licenses/BSD-2-Clause.html
#property strict
#include <stdlib.mqh>
extern const double ts_spread_frac = 1.5; // Fraction of spread for Trailing Stop
// ^ testing with 0.6
extern const double tp_spread_frac = 4.0; // Fraction of spread for Take Profit
static const long __tsl_lots__ = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
static const double __tsl_price__ = NormalizeDouble(__tsl_lots__ * _Point, Digits);
static bool opened_trailing_stop = false;
int nextOrder(int &start, const string symbol = NULL, const int pool = MODE_TRADES)
{
const string s = symbol == NULL ? _Symbol : symbol;
for (int n = start; n < OrdersTotal(); n++)
{
if (!OrderSelect(n, SELECT_BY_POS, pool))
{
continue;
}
else if (OrderSymbol() == s)
{
return OrderTicket();
start = n;
}
}
return EMPTY;
}
void handleError(const string message) {
const int errno = GetLastError();
printf("Error [%d] %s : " + message, errno, ErrorDescription(errno));
ExpertRemove();
}
void OnTick()
{
if (ts_spread_frac <= 0)
{
printf("Unsupported fraction for trailing stop: %f", ts_spread_frac);
ExpertRemove();
}
int start = 0;
const int ticket = nextOrder(start, _Symbol);
if (ticket == EMPTY)
{
return;
}
if (!OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
{
handleError(StringFormat("Unable to select order %d", ticket));
}
/*
const bool selected = OrderSelect(opened_order, SELECT_BY_TICKET, MODE_TRADES);
if (selected && OrderCloseTime() != 0) {
printf("Order %d was closed externally," opened_order);
return;
}
*/
const double opened_price = OrderOpenPrice();
const double opened_lots = OrderLots();
const bool opened_sell = OrderType() == OP_SELL;
const double opened_sl = OrderStopLoss();
const double opened_tp = OrderTakeProfit();
const double spread_price = Ask - Bid;
const double spreadoff = (ts_spread_frac * spread_price) + __tsl_price__;
const double new_sl = NormalizeDouble((opened_sell ? (Ask + spreadoff) : (Bid - spreadoff)), Digits);
const bool update_stop = (opened_trailing_stop ? (opened_sell ? new_sl < opened_sl : new_sl > opened_sl) : (opened_sell ? new_sl < opened_price : new_sl > opened_price));
if (update_stop)
{
const double tp_spreadoff = (spread_price * tp_spread_frac);
const double new_tp = NormalizeDouble(opened_sell ? Bid - tp_spreadoff : Ask + tp_spreadoff, Digits);
printf("Updating order (trailing stop) %d : SL %f, TP %f", ticket, new_sl, new_tp);
printf("Ask %f, Bid %f, Order opened price %f", Ask, Bid, opened_price);
bool modified = OrderModify(ticket, opened_price, new_sl, new_tp, 0, clrNONE);
if (modified)
{
opened_trailing_stop = true;
}
else
{
handleError(StringFormat("Unable to update order %d (trailing stop)", ticket));
}
}
}
When implemented together with an EA, a similar implementation of this function has worked out under testing with the MT4 strategy tester.
When testing the version above under a demo account however, I see the following in the Expert Advisor log:
0 21:05:59.223 tstop EURJPY,M1 inputs: ts_spread_frac=1.2; tp_spread_frac=4.0;
0 21:16:45.756 tstop EURJPY,M1: Updating order (trailing stop) 11264399 : SL 156.163000, TP 155.986000
0 21:16:45.756 tstop EURJPY,M1: Ask 156.121000, Bid 156.094000, Order opened price 156.167000
0 21:16:45.756 tstop EURJPY,M1: Error [4112] unknown error : Unable to update order 11264399 (trailing stop)
2 21:16:45.756 tstop EURJPY,M1: ExpertRemove function called
2 21:16:45.756 tstop EURJPY,M1: ExpertRemove function called
0 21:16:45.756 tstop EURJPY,M1: uninit reason 0
This unknown error
message has been difficult to debug for its ostensible cause. It's the same message as I was seeing with mostly the same trailing stop function in the EA code.
I was able to modify the order manually in the MT4 terminal's order editor, using those same values (SL/TP) at a time very close to the time and the relative market price at when the error occurred.
I'm not able to guess why this isn't working under any live trading in a demo account. The same code (earlier edition) has worked out as part of a broader EA implementation, when testing in the MT4 strategy tester. There, it generally has the trailing stop following the advancing market price, up to some point after an adverse reversal in the market price, at which point the order then closes with the last advanced stop loss. When running it for trailing stop under the demo account, it fails with the same obscure message.
Candidly, this specific implementation may not be just right, even for purpose of strategy testing. It can leave a trailing stop very close to the initial order price. As a prototype, it might at least be simpler than implementing an algorithmic stop of some kind - assuming it actually works under live trading.
I understand that there's an MQL forum. I thought I'd try asking here first, in case anyone might have an idea about why it's not working out under live trading in the demo account. The stops are apparently valid.
The error is only "Unknown" because you have not programmed a description of it in to your EA. Check the documentation.
Error 4112
is ERR_TRADE_EXPERT_DISABLED_BY_SERVER
(Automated trading by Expert Advisors/Scripts disabled by trade server)
If all your settings are correct (auto trading enabled, etc.) you need to discuss this with your broker, as it is the brokers server which is disabling your EA and stopping it performing your functions.
Maybe you are trying to move the stops too quickly. Change your coding to only move the stop loss for every 1 pips of positive movement. If that doesn't work, try 2, etc.