Filling mode errors in MQL5 (SetTypeFillingBySymbol) explained

Published 2026-08-28 · Ranar Algo

Filling mode errors are one of the few MetaTrader 5 problems that look like a broker issue but are almost always a code issue. The order gets rejected with retcode 10030, the journal mutters "Unsupported filling mode", and the EA sits there doing nothing. This post covers what actually goes wrong, why the widely-copied SetTypeFillingBySymbol helper is often the culprit, and what to write instead.

The symptom

Your OrderSend comes back with result.retcode == 10030 (TRADE_RETCODE_INVALID_FILL) and a comment along the lines of "Unsupported filling mode". Sometimes you get 10013 (TRADE_RETCODE_INVALID) instead, which is the same underlying problem wearing a different label.

The infuriating part is the inconsistency. It works on one broker and not another. It works on EURUSD and not on GBPUSD. It works in the Strategy Tester and fails the moment you go live. Once you understand what SYMBOL_FILLING_MODE actually returns, the pattern stops looking random.

What the symbol is actually telling you

This is where most implementations go wrong:

int modes = (int)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);

That returns a bitmask, not a filling mode. There are two flags:

A symbol that supports both returns 3. A symbol that supports only IOC returns 2. Note that the flag for IOC is truthy on its own — which matters in a moment.

Separately, the values you assign to request.type_filling come from a *different* enumeration: ORDER_FILLING_FOK is 0, ORDER_FILLING_IOC is 1, ORDER_FILLING_RETURN is 2. The two sets of numbers do not line up. Treating them as interchangeable is a reliable way to send the wrong mode.

Root cause 1: type_filling left at zero

The single most common cause. You declare your request like this:

MqlTradeRequest request = {};

Zero-initialising the struct sets type_filling to 0, and 0 is not "auto" — it is ORDER_FILLING_FOK. If the symbol doesn't support FOK, the server rejects the request outright. Nothing was executed; the order never existed.

Root cause 2: the broken bitmask test

The helper that circulates in forum posts usually looks like this:

void SetTypeFillingBySymbol(const string symbol)
{
   if(SymbolInfoInteger(symbol, SYMBOL_FILLING_FOK))
      request.type_filling = ORDER_FILLING_FOK;
   else
      request.type_filling = ORDER_FILLING_IOC;
}

Three bugs in five lines. First, if(2) is true, so on an IOC-only symbol this picks FOK — exactly the mode that isn't supported. Second, the else branch assumes IOC exists; it never checks. Third, the function is void, so the caller has no way to know whether a valid mode was chosen. It also never considers ORDER_FILLING_RETURN, which some symbols need.

Root cause 3: stale or missing symbol data

SymbolInfoInteger reads the terminal's local cache. If the symbol has never been added to Market Watch, that cache may be empty and SYMBOL_FILLING_MODE returns 0. Your AND tests then both fail, and you fall through to whatever default you wrote. Call SymbolSelect(symbol, true) first.

There is also a subtler issue: the bitmask describes the symbol, not the order type. A mode the server accepts for a market order is not guaranteed to be accepted for a pending order on the same symbol. Don't assume one value covers both.

The corrected helper

//+------------------------------------------------------------------+
//| Picks a filling mode the symbol advertises. Returns false only   |
//| if the symbol could not be selected at all.                      |
//+------------------------------------------------------------------+
bool SetTypeFillingBySymbol(MqlTradeRequest &request, const string symbol)
{
   if(!SymbolSelect(symbol, true))
   {
      PrintFormat("SetTypeFillingBySymbol: cannot select %s", symbol);
      return false;
   }

   const int modes = (int)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);

   if((modes & SYMBOL_FILLING_FOK) != 0)
   {
      request.type_filling = ORDER_FILLING_FOK;
      return true;
   }
   if((modes & SYMBOL_FILLING_IOC) != 0)
   {
      request.type_filling = ORDER_FILLING_IOC;
      return true;
   }

   // Nothing advertised. Some servers accept only RETURN here.
   request.type_filling = ORDER_FILLING_RETURN;
   return true;
}

The bitwise AND is the fix. The order of preference matters too: FOK first, because all-or-nothing is the safer default when your position sizing assumes the full volume.

Retrying on 10030

Even the corrected helper can lose against a server that contradicts its own symbol metadata. Since 10030 guarantees nothing was executed, resending is safe:

bool SendOrderWithFillingFallback(MqlTradeRequest &request, MqlTradeResult &result)
{
   static const ENUM_ORDER_TYPE_FILLING fallbacks[] =
   {
      ORDER_FILLING_FOK,
      ORDER_FILLING_IOC,
      ORDER_FILLING_RETURN
   };

   for(int i = 0; i < ArraySize(fallbacks); i++)
   {
      request.type_filling = fallbacks[i];

      if(!OrderSend(request, result))
      {
         PrintFormat("OrderSend failed, error %d", GetLastError());
         return false;
      }

      if(result.retcode == TRADE_RETCODE_DONE ||
         result.retcode == TRADE_RETCODE_PLACED ||
         result.retcode == TRADE_RETCODE_DONE_PARTIAL)
         return true;

      if(result.retcode != TRADE_RETCODE_INVALID_FILL)
      {
         PrintFormat("Rejected: retcode %d, comment %s",
                     result.retcode, result.comment);
         return false;
      }

      PrintFormat("Filling %d rejected on %s, trying next",
                  (int)fallbacks[i], request.symbol);
   }
   return false;
}

Check result.retcode even when OrderSend returns true — a request can be sent successfully and still rejected by the server. That distinction catches a lot of silent failures.

Caveats before you ship it

Switching from FOK to IOC changes behaviour, not just error handling. IOC permits partial fills, so a request for 1.00 lots might come back as 0.37. If your stop-loss distance or risk calculation assumes full size, you now need to handle TRADE_RETCODE_DONE_PARTIAL explicitly — and decide whether to cancel the remainder or track a smaller position.

The Strategy Tester emulates filling modes rather than asking a server. A backtest that runs clean tells you nothing about whether your filling logic survives contact with a real broker. If you want a concrete sample to test against, [see a real backtest](https://mql.ranartech.com/r/hhMdF4k_p3Sc) — a GBPUSD H1 EA with +$43 across 171 trades and a 46% win rate. The sample is small, but it's the kind of run where a single rejected order at the wrong moment skews the whole result.

Fix the bitmask test, never leave type_filling at zero, and keep the fallback loop. Filling mode errors stop being mysterious once you know the symbol is handing you flags, not modes.

Build & backtest your own EA free →

Ranar Algo turns a plain-English strategy into a compiled MQL5 Expert Advisor with a real MetaTrader 5 backtest. You keep your IP. Past performance does not guarantee future results.