MQL5 Error 10030 Unsupported Filling Mode: How to Fix It (EA Code + Real Backtest)
To fix MQL5 error 10030 ("Unsupported filling mode", TRADE_RETCODE_INVALID_FILL), stop hard-coding type_filling. Read the symbol's allowed policies with SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE), test the SYMBOL_FILLING_FOK and SYMBOL_FILLING_IOC bit flags, and only use ORDER_FILLING_RETURN when the symbol is not in Market Execution mode. Set the result in MqlTradeRequest.type_filling before every OrderSend().
Why the error happens
Every MT5 symbol tells the terminal which volume filling policies the broker accepts. When your EA sends a market order with a policy the server does not allow, the request is rejected with retcode 10030 and no position is opened. It is one of the most common reasons an EA that "works on my demo" does nothing on another broker: the code was written for one account and copied to another with a different execution model.
Two details from the MQL5 documentation cause most of the confusion:
SYMBOL_FILLING_MODEis a combination of flags, not a single value.SYMBOL_FILLING_FOKis 1,SYMBOL_FILLING_IOCis 2,SYMBOL_FILLING_BOCis 4. A symbol can allow FOK and IOC at the same time, so you test with a bitwise AND, not with==.ORDER_FILLING_RETURNis allowed in every execution mode except Market Execution (SYMBOL_TRADE_EXECUTION_MARKET). It is also what the server assumes if you leavetype_fillingempty, which is why "I never set it and it broke" is a real bug report.
The filling policies themselves mean:
- FOK (Fill or Kill): the whole volume fills, or nothing does.
- IOC (Immediate or Cancel): fill what is available now, cancel the rest.
- RETURN: a partial fill keeps the remaining volume working as an order.
- BOC (Book or Cancel): only for passive limit and stop-limit orders, never for market deals, so ignore it here.
The fix: choose the policy from the symbol
This helper follows the documented rules. It prefers FOK, falls back to IOC, then RETURN when the execution mode allows it:
ENUM_ORDER_TYPE_FILLING PickFilling(const string sym)
{
long modes=SymbolInfoInteger(sym,SYMBOL_FILLING_MODE);
long exe =SymbolInfoInteger(sym,SYMBOL_TRADE_EXEMODE);
if((modes&SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK) return ORDER_FILLING_FOK;
if((modes&SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC) return ORDER_FILLING_IOC;
if(exe!=SYMBOL_TRADE_EXECUTION_MARKET) return ORDER_FILLING_RETURN;
return ORDER_FILLING_IOC;
}
Call it per request (q.type_filling = PickFilling(_Symbol);). Do not cache it once in OnInit() for every symbol if your EA trades several instruments, because each symbol can have different settings.
If you use the standard library instead of raw OrderSend(), CTrade already has this: call trade.SetTypeFillingBySymbol(_Symbol) after creating the object. Per the docs, when both FOK and IOC are allowed it picks FOK, same as the helper above. That is the lazy, correct option for most EAs.
Full working EA
Here is a complete EMA crossover EA that uses the helper, trades only on a new bar, reads closed bars from CopyBuffer() (shift 1, so signals do not repaint), and logs the retcode plus the filling it tried when a request fails. That log line is what you want when a user on another broker says "it doesn't trade".
#property version "1.00"
input int FastMA = 10;
input int SlowMA = 30;
input double Lots = 0.10;
input int SL_Points= 300;
input int TP_Points= 600;
int hFast=INVALID_HANDLE, hSlow=INVALID_HANDLE;
ENUM_ORDER_TYPE_FILLING PickFilling(const string sym)
{
long modes=SymbolInfoInteger(sym,SYMBOL_FILLING_MODE);
long exe =SymbolInfoInteger(sym,SYMBOL_TRADE_EXEMODE);
if((modes&SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK) return ORDER_FILLING_FOK;
if((modes&SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC) return ORDER_FILLING_IOC;
if(exe!=SYMBOL_TRADE_EXECUTION_MARKET) return ORDER_FILLING_RETURN;
return ORDER_FILLING_IOC;
}
bool Open(const ENUM_ORDER_TYPE type)
{
MqlTradeRequest q; MqlTradeResult r; ZeroMemory(q); ZeroMemory(r);
MqlTick t; if(!SymbolInfoTick(_Symbol,t)) return false;
double price=(type==ORDER_TYPE_BUY)?t.ask:t.bid;
double dir =(type==ORDER_TYPE_BUY)?1.0:-1.0;
q.action =TRADE_ACTION_DEAL;
q.symbol =_Symbol;
q.volume =Lots;
q.type =type;
q.price =price;
q.sl =NormalizeDouble(price-dir*SL_Points*_Point,_Digits);
q.tp =NormalizeDouble(price+dir*TP_Points*_Point,_Digits);
q.deviation =10;
q.type_filling=PickFilling(_Symbol);
if(!OrderSend(q,r))
{
PrintFormat("OrderSend failed: retcode=%u filling=%s",r.retcode,EnumToString(q.type_filling));
return false;
}
return r.retcode==TRADE_RETCODE_DONE || r.retcode==TRADE_RETCODE_PLACED;
}
int OnInit()
{
hFast=iMA(_Symbol,_Period,FastMA,0,MODE_EMA,PRICE_CLOSE);
hSlow=iMA(_Symbol,_Period,SlowMA,0,MODE_EMA,PRICE_CLOSE);
if(hFast==INVALID_HANDLE || hSlow==INVALID_HANDLE) return INIT_FAILED;
PrintFormat("Filling chosen for %s: %s",_Symbol,EnumToString(PickFilling(_Symbol)));
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason){ IndicatorRelease(hFast); IndicatorRelease(hSlow); }
void OnTick()
{
static datetime last=0;
datetime bar=iTime(_Symbol,_Period,0);
if(bar==last) return;
last=bar;
double f[],s[];
if(CopyBuffer(hFast,0,1,2,f)<2 || CopyBuffer(hSlow,0,1,2,s)<2) return;
if(PositionSelect(_Symbol)) return;
if(f[0]<=s[0] && f[1]>s[1]) Open(ORDER_TYPE_BUY);
else if(f[0]>=s[0] && f[1]<s[1]) Open(ORDER_TYPE_SELL);
}
Note the return check: OrderSend() returning true only means the request was accepted for processing. The actual outcome is in MqlTradeResult.retcode, so check for TRADE_RETCODE_DONE (or TRADE_RETCODE_PLACED) and log anything else.
Real backtest of this exact EA
We compiled the EA above and ran it in the MetaTrader 5 Strategy Tester (MetaQuotes-Demo data). The point of the test was to confirm the filling logic places trades without a single 10030 rejection. It is not a profitable strategy and is not presented as one:
- Symbol / timeframe: EURUSD, H1
- Period: 2024.01.01 to 2025.01.01, 10,000 deposit, 0.10 lots
- Trades: 116
- Net profit: -$436
- Win rate: 29%
- Profit factor: 0.82
- Max drawdown: 5.7%
116 orders filled with no filling errors, which is what the article is about. A plain 10/30 EMA cross with a fixed 300/600 point SL/TP lost money over this period, a useful reminder that order plumbing and strategy edge are separate problems. Past performance does not guarantee future results.
Quick checklist when you see 10030
- Print
SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE)andSYMBOL_TRADE_EXEMODEinOnInit()on the failing account. - Remove any hard-coded
ORDER_FILLING_FOKorORDER_FILLING_RETURN. - Use the helper above, or
CTrade::SetTypeFillingBySymbol(). - For pending orders, the docs say to use
ORDER_FILLING_RETURNregardless of execution mode. - Log
r.retcodeon every failed request so the next error is obvious.
Generating EAs that get this right
Ranar Algo, the tool behind this blog, turns a plain-English strategy description into MQL5 source, compiles it, and runs a real MT5 backtest, so issues like a wrong filling mode show up before you ever attach the EA to a chart. You get the source code and keep the IP. It is still being improved, and feedback on what it gets wrong is welcome.
Summary
Error 10030 is not a broker bug. It means your EA asked for a filling policy the symbol does not allow. Read SYMBOL_FILLING_MODE as bit flags, respect the Market Execution rule for RETURN, set type_filling on every request, and check retcode after OrderSend(). Your EA will then work across brokers without edits.