MqlTradeRequest by hand: filling modes, deviation, retcodes, and closing what you opened.
Intermediate12 min readBeginner → AdvancedLesson 11 / 17
Try it in MetaEditor
Raw OrderSend with a filling-mode fallback chain
Save as MQL5/Experts/Lesson11RawOrder.mq5. DEMO ONLY. It builds the request manually, tries FOK then IOC then RETURN, and prints the full result — the same logic CTrade hides.
//+------------------------------------------------------------------+
//| Lesson 11 - Raw OrderSend, filling modes, retcodes |
//| WARNING: sends real market orders. Use a DEMO account. |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version "1.00"
#property strict
input double Lots = 0.10;
input int StopPoints = 200;
input int Deviation = 10;
input ulong MagicNumber = 11111111;
datetime lastBarTime = 0;
int OnInit()
{
Print("Filling mode supported by ", _Symbol, ": ",
EnumToString((ENUM_ORDER_TYPE_FILLING)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE)));
return(INIT_SUCCEEDED);
}
void OnTick()
{
datetime t = iTime(_Symbol, _Period, 0);
if(t == lastBarTime) return;
lastBarTime = t;
if(PositionSelect(_Symbol))
{
Print("Position already open - skipping.");
return;
}
OpenBuy(Lots, StopPoints);
}
//+------------------------------------------------------------------+
//| Market buy built by hand, with a filling-mode fallback chain |
//+------------------------------------------------------------------+
void OpenBuy(const double lots, const double stopPoints)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = NormalizeDouble(ask - stopPoints * _Point, _Digits);
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = NormalizeDouble(lots, 2);
req.type = ORDER_TYPE_BUY;
req.price = ask;
req.sl = sl;
req.deviation = Deviation;
req.magic = MagicNumber;
req.comment = "lesson11";
// Ask the symbol first; fall back through the rest if the server refuses.
long supported = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
ENUM_ORDER_TYPE_FILLING modes[3];
if((supported & SYMBOL_FILLING_FOK) != 0)
{ modes[0] = ORDER_FILLING_FOK; modes[1] = ORDER_FILLING_IOC; modes[2] = ORDER_FILLING_RETURN; }
else if((supported & SYMBOL_FILLING_IOC) != 0)
{ modes[0] = ORDER_FILLING_IOC; modes[1] = ORDER_FILLING_RETURN; modes[2] = ORDER_FILLING_FOK; }
else
{ modes[0] = ORDER_FILLING_RETURN; modes[1] = ORDER_FILLING_FOK; modes[2] = ORDER_FILLING_IOC; }
for(int i = 0; i < 3; i++)
{
req.type_filling = modes[i];
ResetLastError();
if(!OrderSend(req, res))
{
Print("OrderSend failed with ", EnumToString(modes[i]),
". GetLastError=", GetLastError());
continue;
}
Print("Sent with ", EnumToString(modes[i]),
" -> retcode=", res.retcode,
" deal=", res.deal,
" volume=", DoubleToString(res.volume, 2),
" price=", DoubleToString(res.price, _Digits));
if(res.retcode == TRADE_RETCODE_DONE || res.retcode == TRADE_RETCODE_PLACED)
{
Print("SUCCESS: order accepted.");
return;
}
// Retcode-specific hints - do not blindly retry these.
if(res.retcode == TRADE_RETCODE_INVALID_STOPS)
{
Print("Invalid stops: your SL/TP is too close. Broker minimum is ",
SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL), " points.");
return;
}
if(res.retcode == TRADE_RETCODE_NO_MONEY)
{
Print("Not enough margin. Free margin: ",
DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_FREE), 2));
return;
}
Print("Rejected with retcode ", res.retcode, " - check the retcode table.");
return;
}
}
To run it
1. Open MetaEditor from MT5 (press F4), and pick the folder this program belongs in — Experts, Indicators, or Scripts.
2. Create a new file, paste this over it, then press F7 to compile. Fix anything the Errors tab reports.
3. Back in MT5, drag it onto a chart — or open the Strategy Tester if it is an Expert Advisor.
Under CTrade: the raw request
CTrade is convenient, but you should see the machinery once. Every order is a MqlTradeRequest struct passed to OrderSend, which fills a MqlTradeResult.
type — ORDER_TYPE_BUY, ORDER_TYPE_SELL, plus the pending variants.
volume — normalised to the symbol's step.
price — ask for buys, bid for sells. For market orders you may pass 0 and let the server fill it.
sl / tp — absolute prices, normalised to digits.
deviation — slippage tolerance in points.
type_filling — ORDER_FILLING_FOK, ORDER_FILLING_IOC, or ORDER_FILLING_RETURN.
magic, comment — your identity and a note for the log.
Filling mode is the classic rejection
Send the wrong type_filling and you get unsupported filling mode — usually on exactly the broker you most want to trade with. Do not hardcode it. Query SYMBOL_FILLING_MODE, or let CTrade::SetTypeFillingBySymbol() do it. This lesson shows the manual fallback chain so you can see what that helper is doing.
Retcodes are your error messages
TRADE_RETCODE_DONE means filled. TRADE_RETCODE_PLACED means a pending order was accepted. Anything else is a specific, documented failure: requote, invalid stops, no money, market closed, disabled trading. Print the code and look it up — do not just retry blindly, because retrying a rejection caused by a too-tight stop will fail forever.
Closing and modifying
Use TRADE_ACTION_REMOVE for pending orders and CTrade::PositionClose for open positions. On hedging accounts you close a specific ticket; on netting accounts you close the symbol's single net position.
What you just did
Lesson 11 of 17 in MetaTrader 5 MQL5 for CFD Traders. When you have run the examples or read the section, tick it off and move to the next lesson.