Complete Guide to Trade Validation in MetaTrader 5

A comprehensive guide to implementing robust trade validation in MetaTrader 5 Expert Advisors, covering StopLevel, FreezeLevel, and the CTradeValidation class.

Table of Contents

  1. Introduction
  2. Understanding StopLevel and FreezeLevel
  3. Why Validate Trades Before Sending or Modifying Orders?
  4. Introducing the CTradeValidation Class
  5. Real-World Example
  6. Get the Code
  7. Conclusion

If you’re developing an Expert Advisor (EA) for MetaTrader 5—whether for your own use or for publication on the MQL5 Market—one of the most important aspects is robust trade validation. Many new developers overlook this, leading to EAs that frequently encounter errors, miss trades, or even put trading accounts at risk.

This article will walk you through the importance of trade validation, introduce the CTradeValidation class, and provide detailed explanations and practical examples for every function in the class. By the end, you’ll understand how to make your EA more professional, reliable, and market-ready.

Before diving into the details, it’s highly recommended to familiarize yourself with:

  • The checks a trading robot must pass before publication in the Market
  • Requirements and limitations in making trades
  • Order Characteristics and Rules for Making Trades

The CTradeValidation class implements all the necessary checks based on the following MetaTrader rules.

MQL5 Rules1 MQL5 Rules2

Understanding StopLevel and FreezeLevel

1. StopLevel

Definition: The minimum distance (in points) required between the current price and Stop Loss (SL) or Take Profit (TP) levels to place or modify orders.

Key Rules:

  • Market Orders (Buy/Sell):
    • For Buy orders: Bid - SL ≥ StopLevel and TP - Bid ≥ StopLevel
    • For Sell orders: SL - Ask ≥ StopLevel and Ask - TP ≥ StopLevel
  • Pending Orders:
    • BuyLimit: Open price must be Ask - OpenPrice ≥ StopLevel
    • SellStop: Open price must be Bid - OpenPrice ≥ StopLevel

Examples:

  • Buy Order:
    • Current Bid = 1.2000, StopLevel = 10 points (0.0010)
    • Valid SL: ≤ 1.1990 (since 1.2000 - 1.1990 = 0.0010)
    • Invalid SL: 1.1995 (rejected as it’s only 5 points away)
  • SellLimit Order:
    • Open price set at 1.2050, Bid = 1.2000, StopLevel = 15 points
    • Valid if 1.2050 - 1.2000 ≥ 0.0015 (valid here: 50 points > 15)

2. FreezeLevel

Definition: A price band around the current market price where orders cannot be modified, deleted, or closed.

Key Rules:

  • Market Orders:
    • Cannot close if SL/TP is within FreezeLevel
    • Example: If FreezeLevel = 5 pips, and current Bid = 1.2000, you cannot set TP at 1.2004 (only 4 pips away)
  • Pending Orders:
    • Cannot modify or delete if open price is within FreezeLevel of the current price

Examples:

  • Pending BuyStop Order:
    • Open price = 1.2050, current Ask = 1.2045, FreezeLevel = 5 pips
    • If the price moves to 1.2047 (within 3 pips of open price), you cannot delete/modify the order
  • Market Buy Order:
    • SL set at 1.1990, TP at 1.2050, current Bid = 1.2000, FreezeLevel = 10 pips
    • If Bid moves to 1.1995, you cannot modify SL/TP as they are within the freeze zone

3. Key Differences

Feature StopLevel FreezeLevel
Purpose Ensures valid SL/TP placement Blocks modifications near execution
Applies to Order placement/modification Order modification/deletion
Violation Error Error 130 (invalid stops) Error 145 (order frozen)

4. Practical Code Example

1
2
3
double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;  
double freezeLevel = MarketInfo(Symbol(), MODE_FREEZELEVEL) * Point;  
Print("StopLevel: ", stopLevel, " | FreezeLevel: ", freezeLevel);

Output: If EURUSD has StopLevel = 10 and FreezeLevel = 5, this prints 0.0010 and 0.0005 (assuming 4-digit pricing).

5. Common Scenarios

  • Pending Order Placement:
    • Trying to set a BuyLimit at 1.1990 when Ask = 1.2000 and StopLevel = 15 points will fail (distance = 10 points < 15)
  • Modifying SL on a Buy Order:
    • Original SL = 1.1990, new SL = 1.1995 (current Bid = 1.2000, FreezeLevel = 5 pips)
    • If 1.2000 - 1.1995 = 5 pips, modification is blocked
  • Deleting a Pending SellStop:
    • Open price = 1.1900, current Bid = 1.1903, FreezeLevel = 5 pips
    • Deletion fails as the price is within the freeze zone

6. Broker-Specific Behavior

  • ECN Brokers: Often have StopLevel = 0, but dynamic checks apply
  • News Events: FreezeLevel may spike during high volatility to prevent scalping

Why Validate Trades Before Sending or Modifying Orders?

1. Broker and Platform Rules

Every broker and symbol has specific trading rules:

  • StopLevel: The minimum distance (in points) required between the current price and your stop loss (SL) or take profit (TP)
  • FreezeLevel: A zone near the current price where you cannot modify or delete orders
  • Lot Size Limits: Minimum, maximum, and step size for trading volumes
  • Margin Requirements: You must have enough free margin to open or modify a position
  • Tick Size: The smallest price increment for the symbol

If you ignore these, your EA will:

  • Get frequent “Invalid Stops” or “Not enough money” errors
  • Miss trading opportunities
  • Risk being rejected from the MQL5 Market for poor reliability

2. Professionalism and Trust

A professional EA:

✅ Checks all conditions before sending/modifying orders
✅ Handles all possible errors gracefully
✅ Protects the trader’s account from unexpected behavior

If you want your EA to be trusted by others (or yourself!), validation is not optional—it’s essential.

Introducing the CTradeValidation Class

The CTradeValidation class is designed to centralize and simplify all the checks you need before trading. It helps you:

  • Validate order placement, modification, and deletion
  • Calculate safe lot sizes
  • Ensure compliance with all broker and symbol rules

Let’s explore every function in this class, with detailed explanations and practical code examples.

1. Constructor: CTradeValidation(string _symbol, bool use_max_level = false)

What It Does

Initializes the validation object for a specific symbol, fetching all the necessary trading parameters (point size, stop level, freeze level, pip value, etc.).

Parameters

  • string _symbol: The symbol you want to trade (e.g., “EURUSD”)
  • bool use_max_level (optional, default: false):
    • If true, the class will use the maximum of the symbol’s stop level and freeze level for all validations
    • This is extra safe, ensuring your EA never gets rejected due to either restriction

Example

1
2
3
4
5
// Standard usage (uses default stop/freeze levels)
CTradeValidation validation(_Symbol);

// Extra safe: use the maximum of stop and freeze levels for all checks
CTradeValidation validation(_Symbol, true);

When to Use use_max_level

Some brokers set a freeze level that is higher than the stop level. If you want to avoid any risk of your order being rejected due to either, set use_max_level to true. This is especially useful for EAs you plan to sell or distribute.

2. GetStopLevel & GetFreezeLevel

What They Do

  • GetStopLevel(): Returns the minimum distance (in points) required between the current price and your SL/TP
  • GetFreezeLevel(): Returns the freeze level (in points) where modifications are not allowed

Example

1
2
3
double stopLevel = validation.GetStopLevel();
double freezeLevel = validation.GetFreezeLevel();
Print("StopLevel: ", stopLevel, " FreezeLevel: ", freezeLevel);

Practical Use

Before setting SL/TP or modifying orders, check these values to ensure your prices are valid. For example, if the stop level is 200 points, your SL/TP must be at least 200 points away from the current price.

3. CalculateValidLot

What It Does

Calculates a valid lot size based on your risk percentage, entry price, and stop loss, ensuring it fits the broker’s lot limits.

Parameters

  • double sl_price: The stop loss price
  • double entry_price: The entry price for the trade
  • double risk_percent: The percentage of your account balance you want to risk

Example

1
2
double lot = validation.CalculateValidLot(sl_price, entry_price, 1.0); // 1% risk
Print("Calculated lot: ", lot);

Practical Scenario

Suppose you want to risk 1% of your account on each trade. This function calculates the lot size that matches your risk, taking into account the distance to your stop loss and the symbol’s tick value. It also ensures the lot size is within the broker’s allowed range.

4. CheckMargin

What It Does

Checks if you have enough free margin to open a position with the given lot size and order type. Optionally, it can reduce the lot size to the maximum allowed by your margin.

Parameters

  • double &lot_size: The lot size you want to check (may be reduced if not enough margin)
  • ENUM_ORDER_TYPE order_type: The type of order (e.g., ORDER_TYPE_BUY)
  • bool use_max_margin (optional, default: true): If true, will reduce the lot size to the maximum possible if you don’t have enough margin

Example

1
2
3
4
5
6
double lot = 0.5;
if(validation.CheckMargin(lot, ORDER_TYPE_BUY)) {
    Print("Safe to trade with lot: ", lot);
} else {
    Print("Not enough margin!");
}

Practical Scenario

If you try to open a trade with a lot size that’s too large for your available margin, this function will either reject the trade or (if use_max_margin is true) reduce the lot size to the maximum possible.

5. CalculateMaxLot

What It Does

Calculates the maximum lot size you can open with your available margin for a given order type.

Parameters

  • ENUM_ORDER_TYPE order_type
  • double available_margin

Example

1
2
double maxLot = validation.CalculateMaxLot(ORDER_TYPE_SELL, AccountInfoDouble(ACCOUNT_MARGIN_FREE));
Print("Max lot for available margin: ", maxLot);

Practical Scenario

If you want to scale your position size based on your account’s free margin, use this function to find the maximum safe lot size.

6. GetPipPoint

What It Does

Returns the pip value for the symbol (useful for calculating distances in pips).

Example

1
2
double pip = validation.GetPipPoint();
Print("Pip value: ", pip);

Practical Scenario

If you want to set your stop loss 20 pips away, you can calculate the price distance as 20 * validation.GetPipPoint().

7. Round2Ticksize

What It Does

Rounds a price to the nearest valid tick size for the symbol.

Parameters

  • double price: The price to round

Example

1
2
double roundedPrice = validation.Round2Ticksize(price);
Print("Rounded price: ", roundedPrice);

Practical Scenario

Some symbols have a tick size of 0.01, others 0.0001. This function ensures your prices are always valid for the symbol, avoiding rejections.

8. ValidateOrderPlacement

What It Does

Checks if your stop loss and take profit are at valid distances for a new order (market or pending).

Parameters

  • ENUM_ORDER_TYPE order_type: The type of order (e.g., ORDER_TYPE_BUY)
  • double sl_price: The stop loss price
  • double tp_price: The take profit price
  • double open_price (optional): The entry price for pending orders

Example

1
2
3
4
5
6
7
8
double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = entry - 20 * validation.GetPipPoint();
double tp = entry + 40 * validation.GetPipPoint();
if(validation.ValidateOrderPlacement(ORDER_TYPE_BUY, sl, tp, entry)) {
    // Place the order
} else {
    Print("SL/TP not valid for order placement!");
}

Practical Scenario

This function prevents sending orders that will be rejected due to SL/TP being too close to the price, which is a common error for new EAs.

9. ValidateSLModify

What It Does

Checks if modifying the stop loss is allowed (for both market and pending orders).

Parameters

  • ENUM_ORDER_TYPE order_type
  • double old_sl: The current stop loss
  • double new_sl: The new stop loss you want to set
  • double open_price (optional): The open price for pending orders

Example

1
2
3
4
5
if(validation.ValidateSLModify(ORDER_TYPE_SELL, old_sl, new_sl, open_price)) {
    // Modify SL
} else {
    Print("SL modification not allowed!");
}

Practical Scenario

If you want to move your stop loss to break even, this function checks if the new SL is valid according to broker rules.

10. ValidateTPModify

What It Does

Checks if modifying the take profit is allowed.

Parameters

  • ENUM_ORDER_TYPE order_type
  • double old_tp: The current take profit
  • double new_tp: The new take profit you want to set
  • double open_price (optional): The open price for pending orders

Example

1
2
3
4
5
if(validation.ValidateTPModify(ORDER_TYPE_BUY, old_tp, new_tp, open_price)) {
    // Modify TP
} else {
    Print("TP modification not allowed!");
}

Practical Scenario

If you want to trail your take profit as the trade moves in your favor, this function ensures your new TP is valid.

11. ValidateOpenPriceModify

What It Does

Checks if you can modify the open price of a pending order (Buy Limit, Sell Limit, Buy Stop, Sell Stop).

Parameters

  • ENUM_ORDER_TYPE order_type
  • double old_open_price
  • double new_open_price

Example

1
2
3
4
5
if(validation.ValidateOpenPriceModify(ORDER_TYPE_BUY_LIMIT, old_open, new_open)) {
    // Modify pending order price
} else {
    Print("Open price modification not allowed!");
}

Practical Scenario

If you want to move a pending order closer to the market, this function checks if the new price is allowed.

12. ValidateClosePosition

What It Does

Checks if you can close a position, ensuring SL/TP are not within the freeze level.

Parameters

  • ENUM_ORDER_TYPE order_type
  • double sl_price
  • double tp_price

Example

1
2
3
4
5
if(validation.ValidateClosePosition(ORDER_TYPE_SELL, sl, tp)) {
    // Close position
} else {
    Print("Position cannot be closed due to freeze level!");
}

Practical Scenario

If your SL or TP is too close to the current price, the broker may not allow you to close the position. This function checks for that.

13. ValidateDeletePendingOrder

What It Does

Checks if you can delete a pending order, ensuring the open price is not within the freeze level.

Parameters

  • ENUM_ORDER_TYPE order_type
  • double open_price

Example

1
2
3
4
5
if(validation.ValidateDeletePendingOrder(ORDER_TYPE_BUY_STOP, open_price)) {
    // Delete pending order
} else {
    Print("Pending order cannot be deleted due to freeze level!");
}

Practical Scenario

If you try to delete a pending order that’s too close to the market price, the broker may reject the request. This function prevents that.

Real-World Example: Using CTradeValidation in an EA

Here’s a simple example of how you might use this class in your EA’s order logic:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
CTradeValidation validation(_Symbol, true); // Use max level for safety

double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = entry - 20 * validation.GetPipPoint();
double tp = entry + 40 * validation.GetPipPoint();
double lot = validation.CalculateValidLot(sl, entry, 1.0);

if(!validation.ValidateOrderPlacement(ORDER_TYPE_BUY, sl, tp, entry)) {
    Print("Order placement not valid!");
    return;
}

if(!validation.CheckMargin(lot, ORDER_TYPE_BUY)) {
    Print("Not enough margin!");
    return;
}

// All checks passed, send the order
trade.Buy(lot, _Symbol, entry, sl, tp);

Get the Code

The complete CTradeValidation class is available for free on GitHub. You can download and use it in your projects:

GitHub Repository: Trade_Validation

The repository contains:

  • Complete source code of the CTradeValidation class
  • Example usage
  • Documentation
  • Free to use in your projects

Conclusion

By using the CTradeValidation class, you make your EA:

🛡️ More robust and error-free
🚀 Ready for the MQL5 Market
💰 Safer for your own and your clients’ accounts
🧩 Easier to maintain and extend

Always validate before you trade!
This is the mark of a professional EA developer.

If you have questions or want to see more examples, feel free to ask in the comments!