MQL5 Slippage Control for EA OrderSend: Deviation, Spread Checks, and Forward Testing

目次

Key Takeaway

The purpose of designing slippage control in MQL5 is to make it easier to limit the risk that an EA order is filled far away from the expected price.
Do not rely only on MqlTradeRequest.deviation. Manage execution by combining spread checks, price refreshes, OrderCheck, filling mode checks, and post-fill result validation.
Slippage cannot be removed completely. Its conditions change depending on sharp market moves, liquidity, broker specifications, and the VPS environment.
Because backtest results do not guarantee future execution quality, forward testing must verify fill differences and behavior when spreads widen.

1. Why This Design Is Necessary

Conclusion:
Slippage control in MQL5 is execution management that keeps the gap between the order price and the actual fill price within a defined range.
In an EA, even when the trading signal is correct, a worse fill price changes profit and loss, stop-loss distance, and risk-reward.

Slippage is the difference between the price expected when an order is placed and the price where the order is actually filled. With market orders, price keeps moving, so the price at the time of sending may not match the price processed by the server.

In an MQL5 EA, slippage control should be treated as an execution quality issue, including the following points.

  • Whether the spread is not too wide
  • Whether Bid and Ask are refreshed immediately before the order
  • Whether the allowed deviation is set according to the symbol’s digits
  • Whether margin and trading conditions are checked with OrderCheck
  • Whether the fill result and return code are checked after OrderSend
  • Whether the difference between netting and hedging accounts is considered

Slippage control in MQL5 is not enough if you only set the allowed deviation at order time. As part of EA execution design, it must be checked in three stages: before the order, during order submission, and after the order.

1.1 How Slippage Affects EA Performance

Large slippage changes the practical balance between the entry price, stop-loss price, and take-profit price. In short-term trading, scalping, and high-frequency entries, even a small fill difference can affect performance.

However, if slippage is restricted too tightly, orders are more likely to be rejected. You need to decide whether fill rate or fill quality has priority based on the EA’s design purpose.

1.2 Think of It as Control, Not Complete Avoidance

Slippage is not determined only by communication speed. Market movement, liquidity, filling mode, server processing, symbol specifications, and trading hours all affect it.

In EA design, the goal is not to make slippage zero. The goal is to define an acceptable range and avoid orders outside that range.

2. Overall EA Design Philosophy

Conclusion:
Slippage control should be designed as an execution control module separated from signal judgment.
When trade conditions, risk checks, pre-order checks, order submission, and post-fill management are separated, the EA becomes easier to test.

The EA process is easier to implement when organized in the following order.

Market analysis
↓
Filter judgment
↓
Signal judgment
↓
Risk check
↓
Pre-order check
↓
Order submission
↓
Post-fill management
↓
Exit and stop judgment

Slippage control mainly relates to “pre-order check,” “order submission,” and “post-fill management.” If it is mixed with signal judgment, it becomes harder to separate an entry condition problem from an execution quality problem.

In an MQL5 EA, OnInit performs initial setup, OnTick makes decisions on each price update, and OnTradeTransaction can be used when needed to track orders, deals, and position changes.

2.1 Items to Check Before an Order

Before an order, check at least the following items.

  • Whether the current spread is within the allowed range
  • Whether the symbol is tradable
  • Whether the lot size matches the minimum lot, maximum lot, and lot step
  • Whether the stop-loss price violates the stop level
  • Whether margin is sufficient
  • How existing positions on the same symbol will be handled
  • Whether the current price is not based on an old tick

Slippage control is not only about the fill difference. It is also a mechanism for deciding whether the market condition is suitable for placing an order.

2.2 Items to Check After an Order

After an order, check the contents of MqlTradeResult. Log whether the order was sent, whether it was filled, the fill price, and the return code.

Recording the post-fill price difference makes it easier to compare backtesting and forward testing.

3. Basic Structure

Conclusion:
MQL5 slippage control should be built around four basic elements: allowed deviation, spread limit, pre-order checks, and fill result logs.
Separating these four elements makes root cause analysis and parameter adjustment easier.

The basic structure can be divided as follows.

Design ElementRoleMain CheckNote
Allowed deviationSpecifies the allowed shift from the order priceMqlTradeRequest.deviationMatch the symbol’s digits
Spread limitAvoids orders in unfavorable marketsDifference between Bid and AskOften widens around news releases
Pre-order checkChecks trading conditionsOrderCheck, margin, lotsSuccess does not guarantee a fill
Post-fill logRecords the actual fill differenceFill price, return codeImportant for forward testing
MQL5 OrderSend slippage control flow with MqlTradeRequest.deviation, OrderCheck, chart prices, and retcode logs

3.1 How to Think About deviation

MqlTradeRequest.deviation specifies the allowed price difference for a market order in points. A point depends on the symbol’s minimum price unit.

For example, on a 5-digit symbol, 1 point is a small price unit. If pips and points are confused, the allowed range may become much narrower or wider than intended.

3.2 Difference From a Spread Limit

A spread limit is a condition used to judge market conditions before placing an order. deviation is a condition used to allow a price difference after the order is sent.

Spread control and slippage control have different roles. Using both makes it easier to avoid orders under poor conditions.

4. Roles of the Main Modules

Conclusion:
To stabilize slippage control, separate price retrieval, spread judgment, lot normalization, pre-order checks, order execution, and result logging into different functions.
Splitting functions makes it easier to isolate problems during backtesting and forward testing.

Design the main modules as follows.

ModuleRoleAction on Failure
Price retrievalGets Bid, Ask, Point, and DigitsDo not place an order
Spread judgmentChecks whether the current spread is within the limitSkip the entry
Lot adjustmentRounds lots according to symbol specificationsDo not order if outside the minimum or maximum range
Pre-order checkChecks conditions with OrderCheckLog the error details
Order submissionPlaces the order with OrderSendCheck the return code
Post-fill recordRecords the difference between fill price and expected priceUse it for forward testing

4.1 Price Retrieval Module

In MQL5, buy orders use Ask as the reference price, and sell orders use Bid. If the price is not refreshed immediately before the order, the EA may place the order using an old price.

In an EA, get Bid, Ask, and Point with SymbolInfoDouble. If retrieval fails, stop the order.

4.2 Lot Adjustment Module

Lots must be adjusted to the minimum lot, maximum lot, and lot step. Even when risk-percentage lot calculation is used, the final lot size must be rounded to the symbol specifications.

If lot restrictions are ignored, pre-order checks and order submission are more likely to fail.

5. Implementation Patterns

Conclusion:
Implementation patterns can be divided into four types: fixed allowed range, spread-linked control, volatility-linked control, and time-based control.
Beginners should start with a fixed allowed range and a spread limit because the behavior is easier to test.

Select a slippage control design pattern according to the EA’s trading frequency and timeframe.

MethodAdvantageDisadvantageBest Use Case
Fixed allowed rangeEasy to implementDoes not adapt well to market movementInitial implementation, low-frequency EAs
Spread-linked controlEasier to avoid poor conditionsMay reduce trade opportunitiesShort-term trading, scalping tests
Volatility-linked controlCan reflect market movementCan become highly parameter-dependentEAs that use ATR or similar measures
Time-based controlEasier to avoid news periods and thin liquidityMay have low generalitySymbols strongly affected by trading hours

5.1 Fixed Allowed Range

A fixed allowed range is a method that keeps deviation at a constant value. Its advantage is that it is easy to implement and easy to compare in test results.

However, a fixed value alone may be insufficient during periods of sharp market movement or on symbols where spreads widen.

5.2 Spread-Linked Control

Spread-linked control places orders only when the current spread is within a defined range. This makes it easier to avoid entries when the spread is wide.

If the spread limit is too strict, fill quality may improve, but the number of trades may decrease.

5.3 Volatility-Linked Control

Volatility-linked control adjusts allowed conditions using a range measure such as ATR. When using ATR in MQL5, the usual flow is to create the indicator handle in OnInit and get values from CopyBuffer in OnTick.

This method is flexible, but adding too many parameters can easily lead to over-optimization.

6. Sample Code

Conclusion:
The sample code implements a spread limit, lot restrictions, OrderCheck, OrderSend, and result logging in one flow.
This code is a basic form for testing. In live use, it must be adjusted for symbol specifications and account type.

#property strict

input double InpLots = 0.10;
input int    InpMaxSpreadPoints = 25;
input int    InpDeviationPoints = 10;
input ulong  InpMagicNumber = 20260518;

bool GetPrices(double &bid, double &ask, double &point)
{
   if(!SymbolInfoDouble(_Symbol, SYMBOL_BID, bid))
   {
      Print("Failed to get bid price");
      return false;
   }

   if(!SymbolInfoDouble(_Symbol, SYMBOL_ASK, ask))
   {
      Print("Failed to get ask price");
      return false;
   }

   if(!SymbolInfoDouble(_Symbol, SYMBOL_POINT, point))
   {
      Print("Failed to get point size");
      return false;
   }

   return true;
}

bool IsSpreadAcceptable()
{
   double bid, ask, point;

   if(!GetPrices(bid, ask, point))
      return false;

   int spread_points = (int)MathRound((ask - bid) / point);

   if(spread_points > InpMaxSpreadPoints)
   {
      Print("Spread is too wide. spread_points=", spread_points);
      return false;
   }

   return true;
}

double NormalizeLots(double lots)
{
   double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double step    = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   if(step <= 0.0)
      return 0.0;

   lots = MathMax(min_lot, MathMin(max_lot, lots));
   lots = MathFloor(lots / step) * step;

   return NormalizeDouble(lots, 2);
}

bool SendBuyOrder()
{
   if(!IsSpreadAcceptable())
      return false;

   double bid, ask, point;

   if(!GetPrices(bid, ask, point))
      return false;

   double lots = NormalizeLots(InpLots);

   if(lots <= 0.0)
   {
      Print("Invalid lot size after normalization");
      return false;
   }

   MqlTradeRequest request;
   MqlTradeCheckResult check;
   MqlTradeResult result;

   ZeroMemory(request);
   ZeroMemory(check);
   ZeroMemory(result);

   request.action       = TRADE_ACTION_DEAL;
   request.symbol       = _Symbol;
   request.volume       = lots;
   request.type         = ORDER_TYPE_BUY;
   request.price        = ask;
   request.deviation    = InpDeviationPoints;
   request.magic        = InpMagicNumber;
   request.type_filling = ORDER_FILLING_FOK;
   request.comment      = "slippage-control-test";

   if(!OrderCheck(request, check))
   {
      Print("OrderCheck failed. retcode=", check.retcode);
      return false;
   }

   if(!OrderSend(request, result))
   {
      Print("OrderSend failed. retcode=", result.retcode);
      return false;
   }

   if(result.retcode != TRADE_RETCODE_DONE &&
      result.retcode != TRADE_RETCODE_PLACED)
   {
      Print("Order was not completed as expected. retcode=", result.retcode);
      return false;
   }

   double slippage_points = MathAbs(result.price - request.price) / point;

   Print("OrderSend result. retcode=", result.retcode,
         " request_price=", request.price,
         " result_price=", result.price,
         " slippage_points=", slippage_points);

   return true;
}

int OnInit()
{
   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
}

void OnTick()
{
   static datetime last_bar_time = 0;
   datetime current_bar_time = iTime(_Symbol, _Period, 0);

   if(current_bar_time == last_bar_time)
      return;

   last_bar_time = current_bar_time;

   bool sample_signal = false;

   if(sample_signal)
   {
      SendBuyOrder();
   }
}

6.1 How to Read the Code

This code separates signal judgment as sample_signal. In a real EA, connect the result of moving average, RSI, breakout, or other conditions to this part.

Before the order, the code checks the spread, refreshes prices, and adjusts the lot size to the symbol specifications. After that, it checks order conditions with OrderCheck and then runs OrderSend.

6.2 Notes on type_filling

The available request.type_filling method differs depending on the symbol and broker specifications. For symbols that do not support ORDER_FILLING_FOK, another filling mode may be required.

In live operation, the EA must check the filling modes supported by the symbol and keep logs when orders fail.

7. Design Pattern Comparison

Conclusion:
The design of slippage control changes depending on whether fill rate or fill quality has priority.
Short-term EAs often need stricter control, while excessive restrictions in long-term EAs may create opportunity loss.

Design PolicyAdvantageDisadvantageBest Use CaseLive Operation Note
Fill rate priorityOrders are more likely to passMore likely to accept unfavorable fillsLong-term holding, low-frequency EAsWatch for worse P/L during sharp moves
Fill quality priorityEasier to avoid unfavorable fillsOrder rejections may increaseShort-term trading, narrow-profit EAsCheck whether trade count decreases
Spread priorityEasier to avoid trades under poor conditionsMay stop often around news releasesEAs strongly affected by spreadCheck period dependency
Volatility priorityEasier to adapt to market movementDesign can become complexATR-linked EAsWatch for over-optimization

7.1 Trade-Off Between Fill Rate and Fill Quality

Widening the allowed deviation makes orders more likely to pass, but it may also cause fills at unfavorable prices. Narrowing the allowed deviation makes unfavorable fills easier to avoid, but the probability of order rejection increases.

In EA design, test both the risk that an order does not pass and the risk that it fills at an unfavorable price.

7.2 Difference Between Netting and Hedging Accounts

In a netting account, positions on the same symbol are generally consolidated into one position. In a hedging account, it may be possible to hold multiple positions on the same symbol.

The basic idea of slippage control is the same, but post-order position management changes depending on the account type. It is important to check which position was updated after the fill.

8. Items to Check in Backtesting

Conclusion:
In backtesting, check how slippage control changes not only profit and loss, but also trade count, maximum drawdown, consecutive losses, and spread conditions.
If you only look at total profit and loss, you may miss deterioration in execution quality.

Check the following items in backtesting.

  • Total profit and loss
  • Maximum drawdown
  • Win rate
  • Profit/loss ratio
  • Number of trades
  • Consecutive losses
  • Spread conditions
  • Period dependency
  • Parameter dependency
  • Conditions where order rejection is expected

8.1 Change in Number of Trades

If the spread limit or allowed deviation is made stricter, the number of trades may decrease. A backtest with an extremely small number of trades is more likely to depend on a small sample of trade results.

Slippage control is not only a design for improving performance. It is also a design for avoiding orders under poor conditions. Check trade count and fill quality together.

8.2 Parameter Dependency

If InpMaxSpreadPoints or InpDeviationPoints is optimized too finely, the settings may fit only a specific period.

To avoid over-optimization, check behavior across multiple periods, multiple symbols, and different spread conditions.

9. Items to Check in Forward Testing

Conclusion:
In forward testing, check actual fill differences, spread widening, order rejections, and stability in the VPS environment.
Execution conditions that are hard to reproduce in backtesting can affect performance in real-time operation.

Record the following items during forward testing.

  • Fill difference
  • Behavior when spreads widen
  • Trading frequency
  • Drawdown
  • Gap from backtesting
  • Broker differences
  • Stability in the VPS environment
  • Return code when an order fails
  • Difference between demo and live accounts

9.1 Importance of Fill Difference Logs

Logging the expected order price and the actual fill price makes it easier to separate EA problems from trading environment problems.

If fill differences concentrate in specific time periods, the trading time filter or spread limit may need to be reviewed.

9.2 Difference Between Demo and Live Accounts

Demo and live accounts may have different fill conditions and spread conditions. Even if there are few problems on a demo account, the same behavior is not guaranteed on a live account.

Before live operation, run forward tests with small lots or limited conditions, and check order failures, fill differences, and drawdown.

10. Notes for Live Operation

Conclusion:
In live operation, slippage control alone cannot prevent losses.
Risk management must include spread widening, execution delays, insufficient margin, leverage, and broker specifications.

Pay attention to the following points in live operation.

  • Spreads and fill differences can widen around economic news releases
  • Unfavorable fills are more likely during low-liquidity hours
  • VPS latency may affect order processing
  • High leverage can make drawdown larger
  • Order results may change depending on the broker’s filling mode
  • Backtest results do not guarantee future profits

10.1 Relationship With Stop-Loss Width

In an EA with a narrow stop-loss width, slippage has a larger impact. Even a fill difference of a few points can break the expected risk-reward balance.

Stop-loss width, spread limit, and allowed deviation should not be set separately. They should be aligned as assumptions of the entire trading logic.

10.2 Handling Order Rejections

Avoid a design that repeats reorders unconditionally when an order is rejected. If repeated orders occur during sharp market moves, they may lead to unintended fills or excessive logs.

When retrying orders, combine a retry limit, waiting time, price refresh, and spread recheck.

11. Common Design Mistakes

Conclusion:
Common mistakes include assuming that deviation alone can control slippage, confusing pips and points, and skipping post-order result checks.
Slippage control should be designed to include state management before and after the order.

11.1 Confusing Pips and Points

deviation is specified in points. If values are entered with a pips-based assumption, the allowed range may become narrower or wider than intended.

Use the symbol’s SYMBOL_POINT and digits to make the meaning of values clear inside the EA.

11.2 Ordering With an Old Price

Avoid keeping the price received in OnTick for too long and not refreshing it immediately before the order. If the price is old, the order price and current price are more likely to diverge.

Immediately before placing an order, refresh Bid and Ask and recheck the spread.

11.3 Skipping OrderCheck

If OrderCheck is skipped, it becomes harder to detect causes such as insufficient margin, invalid lots, or stop-level violations before placing the order.

OrderCheck does not guarantee a fill, but it is useful as a pre-order condition check.

11.4 Not Keeping Logs

An EA that does not record fill price, expected price, spread, and return code is difficult to analyze during forward testing.

In tests close to live operation, record not only profit and loss but also execution quality logs.

12. Summary

Conclusion:
MQL5 slippage control should be designed by combining MqlTradeRequest.deviation, spread limits, OrderCheck, and result checks after OrderSend.
The important point is not to prevent order price differences completely. It is to define an acceptable range, avoid orders under poor conditions, and create a state where fills can be reviewed afterward.

In an MQL5 EA, separating trading signals from order execution makes execution quality issues easier to analyze. If spread, filling mode, lot restrictions, account type, and broker specifications are not considered, the gap between backtesting and live operation may become large.

Slippage control is not a mechanism that guarantees profit. Before live operation, use forward testing in addition to backtesting to check fill differences, order rejections, and behavior when spreads widen.

FAQ

Q1. What is slippage control in MQL5?

Slippage control in MQL5 is a design for managing the difference between the expected order price and the actual fill price. It is implemented by combining deviation, a spread limit, pre-order checks, and post-fill logs.

Q2. Can setting deviation prevent slippage?

deviation specifies the allowed price difference, but it does not completely prevent slippage. Results change depending on sharp market moves, filling mode, liquidity, and broker specifications.

Q3. What is the difference between spread control and slippage control?

Spread control judges market conditions before an order is placed. Slippage control manages execution so the gap between the order price and fill price stays within an acceptable range.

Q4. Should OrderCheck always be used?

When an EA handles order processing, using OrderCheck to check margin, lots, and trading conditions makes cause analysis easier. OrderCheck does not guarantee a fill, but it is useful as a pre-order check.

Q5. What happens if pips and points are confused?

Because deviation uses points, confusing pips and points can make the allowed range different from what you intended. Check the symbol’s digits and SYMBOL_POINT before setting the value.

Q6. Can slippage control be checked in backtesting?

Backtesting can show the impact on trade count, profit and loss, drawdown, and spread conditions. However, because actual fill differences and server processing cannot be fully reproduced, forward testing is required.

Q7. What needs the most attention in live operation?

In live operation, pay attention to spread widening around economic news releases, execution delays, VPS conditions, broker specifications, and account type differences. Backtest results do not guarantee future profits, so fill difference logs should be reviewed continuously.