c++mql5

TradeRequest fields improperly set when backtesting in MT5


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?


Solution

  • 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++. :(