I'm generating a trade request that's causing a 10013 error when I do backtesting in MT5. I decided to log the trade request and it printed this:
2022.11.16 14:28:17.245 Core 01 2022.01.04 11:00:00 Action REMOVE, Symbol EURUSD, Volume 0.100000, Price 1.128980, Deviation 5, Type SELL STOP LIMIT, Position 9443944
My code is:
bool Strategy::DoEntryRules() {
// Iterate over all the entry rules we have registered...
for (int i = 0; i < m_entry_rules.Count(); i++) {
IEntryRule *rule;
m_entry_rules.TryGetValue(i, rule);
EntryResult result = rule.Enter();
if (result.Result == ENTRY_RESULT_CONTINUE) {
continue;
} else if (result.Result == ENTRY_RESULT_ERROR) {
PrintFormat("Entry rule %s failed with error code %d\n", rule.Name(), result.ErrorCode);
}
m_money_manager.AssignLot(result.Order);
uint errCode = SendOrder(result.Order, rule.Name());
if (errCode != 0) {
result.Result = ENTRY_RESULT_ERROR;
result.ErrorCode = errCode;
}
return true;
}
return false;
}
uint Strategy::SendOrder(const MqlTradeRequest &request, const string name) {
string action;
switch (request.action) {
case TRADE_ACTION_DEAL:
action = "DEAL";
case TRADE_ACTION_SLTP:
action = "SLTP";
case TRADE_ACTION_CLOSE_BY:
action = "CLOSE BY";
case TRADE_ACTION_MODIFY:
action = "MODIFY";
case TRADE_ACTION_PENDING:
action = "PENDING";
case TRADE_ACTION_REMOVE:
action = "REMOVE";
}
string type;
switch (request.type) {
case ORDER_TYPE_BUY:
type = "BUY";
case ORDER_TYPE_BUY_LIMIT:
type = "BUY LIMIT";
case ORDER_TYPE_BUY_STOP:
type = "BUY STOP";
case ORDER_TYPE_BUY_STOP_LIMIT:
type = "BUY STOP LIMIT";
case ORDER_TYPE_CLOSE_BY:
type = "CLOSE BY";
case ORDER_TYPE_SELL:
type = "SELL";
case ORDER_TYPE_SELL_LIMIT:
type = "SELL LIMIT";
case ORDER_TYPE_SELL_STOP:
type = "SELL STOP";
case ORDER_TYPE_SELL_STOP_LIMIT:
type = "SELL STOP LIMIT";
}
PrintFormat("Action %s, Symbol %s, Volume %f, Price %f, Deviation %d, Type %s, Position %d",
action, request.symbol, request.volume, request.price, request.deviation, type, request.position);
MqlTradeCheckResult checkResult;
if (!OrderCheck(request, checkResult)) {
PrintFormat("%s generated a bad order with error code %d: %s", name, checkResult.retcode, checkResult.comment);
return checkResult.retcode;
}
MqlTradeResult result;
if (!OrderSend(request, result) && result.retcode != TRADE_RETCODE_DONE) {
PrintFormat("%s generated an order that was not accepted with error code %d: %s", name, result.retcode, result.comment);
return result.retcode;
}
return 0;
}
where m_entry_rules
contains one IEntryRule
, which is an interface that has the following implementation:
EntryResult SentimentEntry::Enter() {
EntryResult result;
if (m_handle == 0) {
int errCode = InitATR();
if (errCode != 0) {
result.Result = ENTRY_RESULT_ENTER;
result.ErrorCode = errCode;
return result;
}
}
double values[];
if (CopyBuffer(m_handle, 0, 0, 1, values) < 0) {
result.Result = ENTRY_RESULT_ERROR;
result.ErrorCode = GetLastError();
ResetLastError();
return result;
}
ENUM_SENTIMENT sentiment = SentimentIndicator();
bool decision = GetRange() > 0.66 * values[0];
if (decision && sentiment != SENTIMENT_NEUTRAL) {
PrintFormat("%f > 0.66 * %f, %s", GetRange(), values[0], SentimentToString(sentiment));
result.Result = ENTRY_RESULT_ENTER;
result.Order = CreateOrder(m_magic);
if (sentiment == SENTIMENT_BULLISH) {
result.Order.price = SymbolInfoDouble(Symbol(), SYMBOL_ASK); // ENTERED HERE
result.Order.type = ORDER_TYPE_BUY;
} else if (sentiment == SENTIMENT_BEARISH) {
result.Order.price = SymbolInfoDouble(Symbol(), SYMBOL_BID);
result.Order.type = ORDER_TYPE_SELL;
}
} else {
result.Result = ENTRY_RESULT_CONTINUE;
}
return result;
}
and m_money_manager.AssignLot
is defined as:
void ConstantLotManager::AssignLot(MqlTradeRequest &request) {
request.volume = 0.1;
return;
}
Finally, the CreateOrder
function is defined as:
MqlTradeRequest CreateOrder(ulong magic) {
MqlTradeRequest request;
request.symbol = Symbol();
request.deviation = 5;
request.action = TRADE_ACTION_DEAL;
request.type_filling = ORDER_FILLING_FOK;
request.type_time = ORDER_TIME_GTC;
request.magic = magic;
return request;
}
From logging, I'm able to determine that the commented line was reached, so the trade request should have an action of TRADE_ACTION_DEAL
and a type of ORDER_TYPE_BUY
so I'm not sure why it's showing up as me attempting to remove a sell-short order. Does anyone know what's going on here?
I have determined the problem and fixed it. The core issue was that I wasn't converting the ENUM_TRADE_REQUEST_ACTIONS
and ENUM_ORDER_TYPE
enums to strings properly, giving me the impression of a malformed trade request. The logging statement should look like this:
string action;
switch (request.action) {
case TRADE_ACTION_DEAL:
action = "DEAL";
break;
case TRADE_ACTION_SLTP:
action = "SLTP";
break;
case TRADE_ACTION_CLOSE_BY:
action = "CLOSE BY";
break;
case TRADE_ACTION_MODIFY:
action = "MODIFY";
break;
case TRADE_ACTION_PENDING:
action = "PENDING";
break;
case TRADE_ACTION_REMOVE:
action = "REMOVE";
break;
}
string type;
switch (request.type) {
case ORDER_TYPE_BUY:
type = "BUY";
break;
case ORDER_TYPE_BUY_LIMIT:
type = "BUY LIMIT";
break;
case ORDER_TYPE_BUY_STOP:
type = "BUY STOP";
break;
case ORDER_TYPE_BUY_STOP_LIMIT:
type = "BUY STOP LIMIT";
break;
case ORDER_TYPE_CLOSE_BY:
type = "CLOSE BY";
break;
case ORDER_TYPE_SELL:
type = "SELL";
break;
case ORDER_TYPE_SELL_LIMIT:
type = "SELL LIMIT";
break;
case ORDER_TYPE_SELL_STOP:
type = "SELL STOP";
break;
case ORDER_TYPE_SELL_STOP_LIMIT:
type = "SELL STOP LIMIT";
break;
}
PrintFormat("Action %s, Symbol %s, Volume %f, Price %f, Deviation %d, Type %s, Position %d",
action, request.symbol, request.volume, request.price, request.deviation, type, request.position);
This was my punishment for not remembering that I have to break switch statements in C++. :(