Welcome, fellow MQL developers. Encountering runtime errors is a standard part of the development lifecycle when building Expert Advisors, custom indicators, or scripts in MetaTrader.
Among the more common and often frustrating errors faced by MQL4 programmers is Error 129, cryptically labeled ‘Invalid Price’. This error directly impacts order execution and, if not understood and handled correctly, can cripple an otherwise profitable trading strategy.
This article delves deep into the root causes of Error 129, providing practical troubleshooting steps and best practices to ensure your automated trading systems operate reliably.
Understanding MQL4 Error 129: ‘Invalid Price’
What Does Error 129 Signify?
In the context of MQL4, Error 129 (ERRINVALIDPRICE) is returned by trade functions like OrderSend() when the price provided for the order operation is deemed invalid by the trading server. This validation occurs before the order is even queued for execution. It means the price you attempted to use for opening a position, setting a stop-loss or take-profit, or modifying an existing order is unacceptable according to the server’s current market state and rules.
Unlike errors related to insufficient funds or invalid volume, Error 129 specifically targets the price parameter(s) passed to the trade function.
Common Scenarios Leading to ‘Invalid Price’
Error 129 most frequently appears in the following situations:
- Placing Market Orders: Attempting to send a Buy or Sell order using a price that is significantly different from the current market price (Bid or Ask) due to latency or rapid price movement.
- Placing Pending Orders: Setting a Buy Limit/Stop or Sell Limit/Stop order with a price that violates broker-defined rules, such as being too close to the current market price (often referred to as ‘Stop Level’).
- Modifying Existing Orders: Trying to change the Stop Loss or Take Profit levels of an open position or pending order to prices that are too close to the current market price or the order’s open price, infringing broker rules.
- Using Stale Price Data: Calculating an order price based on old tick data, especially in volatile markets.
Understanding that this validation happens server-side is crucial. Your terminal might show a price, but the server is the final arbiter of its validity at the moment of processing the request.
Root Causes of MQL4 Error 129
The ‘Invalid Price’ error is usually a symptom of one or more underlying issues related to how prices are obtained, validated, or used in your MQL4 code, often interacting with market conditions and broker rules.
Incorrect Price Input in OrderSend Function
This is perhaps the most straightforward cause. OrderSend() requires specific price types depending on the order type:
- OP_BUY: Should use
Askprice for market execution. - OP_SELL: Should use
Bidprice for market execution. - OPBUYLIMIT, OPBUYSTOP: Require a specific price level.
- OPSELLLIMIT, OPSELLSTOP: Require a specific price level.
- OP_CLOSE: Usually uses the opposing market price (
Bidto close a Buy,Askto close a Sell). - OP_MODIFY: Requires specific prices for SL/TP or the order’s open price (for stop/limit price changes).
Using the wrong price (e.g., Bid for a OP_BUY), using a zero price, or using a price that is malformed (incorrect precision relative to Digits) will trigger Error 129.
// Incorrect example: Using Bid for a Buy order
// int ticket = OrderSend(Symbol(), OP_BUY, volume, Bid, slippage, sl_price, tp_price, NULL, magic_number, 0, Green);
// Correct example: Using Ask for a Buy order
int ticket = OrderSend(Symbol(), OP_BUY, volume, Ask, slippage, sl_price, tp_price, NULL, magic_number, 0, Green);
if(ticket < 0)
{
int error = GetLastError();
Print("OrderSend failed. Error: ", error);
}
Slippage and Market Volatility
Even if you use the correct Bid or Ask price at the moment you call OrderSend(), that price might change very slightly between the time your terminal sends the request and the server receives and processes it. This is especially true in volatile markets.
The slippage parameter in OrderSend() specifies the maximum acceptable price deviation from the requested price. If the actual execution price on the server differs from your requested price by more than the specified slippage and the server cannot fill the order within that slippage, it might result in an ‘Invalid Price’ error (among other potential rejection reasons, though 129 is common for price validation issues). A low slippage value in a fast market is a frequent culprit.
Broker Restrictions and Price Feeds
Brokers often impose rules:
- Stop Levels: Minimum distance (in points) that pending orders (Limit/Stop) and Stop Loss/Take Profit levels must be placed away from the current market price. This minimum distance is often stored in
MarketInfo(Symbol(), MODE_STOPLEVEL). Attempting to place an order or modify SL/TP within this zone will result in Error 129. - Frozen Levels: Prices may be ‘frozen’ near pending order levels during certain market events, preventing modifications. While less commonly mapped directly to 129, it’s a related price restriction.
- Specific Symbol Rules: Some symbols might have unique trading hours or price feed characteristics that affect order validity at certain times.
Symbol Specification Problems
Ensuring you use the correct Digits and Point values for the symbol is critical for price calculations and formatting. Using incorrect precision can lead to prices that the server considers invalid. MarketInfo(Symbol(), MODE_DIGITS) and MarketInfo(Symbol(), MODE_POINT) are your go-to functions here.
// Get correct symbol info
double point = MarketInfo(Symbol(), MODE_POINT);
int digits = MarketInfo(Symbol(), MODE_DIGITS);
int stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL);
// Example calculation needing correct point/digits
double my_sl = Bid + 50 * point; // Example SL for a Sell order
// Ensure price has correct precision (optional but good practice)
double validated_price = NormalizeDouble(my_sl, digits);
// Check against stop level before sending
if(MathAbs(Bid - validated_price) < stopLevel * point)
{
Print("SL price is too close to market.");
// Adjust SL or do not send order
}
Troubleshooting and Solutions for Error 129
Debugging Error 129 requires a systematic approach, focusing on the price values used in your trade operations.
Debugging Price Variables
Print the exact price(s) you are passing to OrderSend() or OrderModify() just before making the call. Compare these printed values with the current Bid and Ask prices at that moment. Also, check the price precision (number of decimal places) and ensure it matches Digits for the symbol.
double current_ask = Ask;
double current_bid = Bid;
double order_price;
if(OrderType == OP_BUY)
{
order_price = current_ask;
}
else if(OrderType == OP_SELL)
{
order_price = current_bid;
}
// ... handle pending orders similarly ...
Print("Attempting OrderSend for ", Symbol(), ", Type: ", OrderType, ", Price: ", DoubleToStr(order_price, Digits), ", Bid: ", DoubleToStr(current_bid, Digits), ", Ask: ", DoubleToStr(current_ask, Digits));
int ticket = OrderSend(Symbol(), OrderType, volume, order_price, slippage, sl_price, tp_price, NULL, magic_number, 0, clrNone);
if(ticket < 0)
{
int error = GetLastError();
Print("OrderSend failed. Error: ", error, ", Last sent Price: ", DoubleToStr(order_price, Digits));
}
Implementing Slippage Control
If you suspect volatility is the issue, try increasing the slippage parameter in OrderSend(). Experiment with a larger value (e.g., 5, 10, 20 points) to see if the error disappears. Be aware that a larger slippage means your order might be filled at a significantly worse price than intended. You’ll need to balance reliability against potential price deviation.
Verifying Symbol Information
Always retrieve and use the current Point, Digits, and StopLevel using MarketInfo() (MQL4) or SymbolInfoDouble()/SymbolInfoInteger() (MQL5) for the specific symbol you are trading at runtime. Do not hardcode these values, as they can vary between brokers and account types.
// Get dynamic symbol info
double current_point = MarketInfo(Symbol(), MODE_POINT);
int current_digits = MarketInfo(Symbol(), MODE_DIGITS);
int current_stop_level = MarketInfo(Symbol(), MODE_STOPLEVEL);
// Use these values for all price calculations and validation
double min_distance = current_stop_level * current_point;
// Example check before placing a Buy Limit order
double limit_price = NormalizeDouble(Bid - 100 * current_point, current_digits);
if (MathAbs(limit_price - Ask) < min_distance)
{
Print("Buy Limit price is too close to current Ask.");
// Handle this situation
} else {
// Proceed with OrderSend for Buy Limit
}
Using AccountInfoDouble(ACCOUNTTRADEEXPERT_MAGIC) and OrderMagicNumber()
While Error 129 is specifically a price validation issue, ensuring correct management of orders is part of overall EA robustness. The prompt mentioned ACCOUNT_TRADE_EXPERT_MAGIC and OrderMagicNumber(). It’s important to clarify that the magic number itself does not cause Error 129. An invalid or incorrect magic number might lead to other order management issues or errors (like attempting to close the wrong order), but it has no bearing on the validity of the price passed to OrderSend or OrderModify in the eyes of the server’s price validation logic.
However, as part of a general debugging process for trade functions, always verify that you are using the correct magic number when identifying orders belonging to your EA, especially in functions like OrderSelect() or OrderModify(). This is a good practice for reliable EA operation but is not a direct solution for Error 129.
Best Practices to Prevent ‘Invalid Price’ Errors
Proactive measures are always better than reactive debugging. Implement these practices to minimize occurrences of Error 129.
Price Validation Before Order Execution
Before calling OrderSend() or OrderModify(), add explicit checks:
- Confirm the calculated price is not zero or NaN.
- For market orders, verify that the price you are using (
BidorAsk) is fresh (e.g., check the time of the last tick). If the market hasn’t moved for a while, the price might still be considered stale by the server in some specific contexts. - For pending orders and SL/TP, check that the proposed price respects the
StopLevelrequirement for the symbol.MarketInfo(Symbol(), MODE_STOPLEVEL)gives you this critical value.
Handling Market Conditions and Volatility
In highly volatile periods (e.g., during major news releases):
- Consider temporarily disabling trading or widening your acceptable slippage.
- Implement retry logic for
OrderSend()calls that fail with Error 129, perhaps after fetching a fresh price and/or increasing slippage slightly. - Be aware that even with slippage, orders might be rejected if the price moves too drastically.
Proper Error Handling and Logging
Always check the return value of trade functions (OrderSend, OrderModify, OrderClose) and call GetLastError() if they fail. Log the error code, the function name, the symbol, and critically, the exact price(s) and parameters you attempted to use. This log is invaluable for pinpointing why the price was rejected.
// Example logging after a failed trade operation
int error_code = GetLastError();
if (error_code != 0)
{
Print(TimeCurrent(), " ", Symbol(), " TRADE FAILED. Error: ", error_code, " (", ErrorDescription(error_code), "). Function: OrderSend/Modify/Close.");
// Log parameters used (price, volume, slippage, etc.) based on the operation attempt
}
Advanced Techniques and Considerations
For sophisticated EAs, you might need more dynamic approaches to price handling.
Working with Bid and Ask Prices Correctly
Reiterate the fundamental rule: OP_BUY uses Ask, OP_SELL uses Bid for market orders. For pending orders and SL/TP, understand that Buy orders (OPBUY, OPBUYLIMIT, OPBUYSTOP) have SL below and TP above the entry, measured against the Bid price for triggering/checking. Sell orders (OPSELL, OPSELLLIMIT, OPSELLSTOP) have SL above and TP below, measured against the Ask price.
Adjusting Slippage Dynamically
Instead of a fixed slippage, you can calculate slippage based on recent market volatility. Using indicators like Average True Range (ATR) or measuring the standard deviation of recent price changes can help you set a more appropriate slippage value that adapts to current market conditions, reducing Error 129 during volatility while minimizing excessive slippage during calm periods.
Dealing with Asynchronous Order Execution
MQL4’s OrderSend is synchronous. You call it, and the code waits for the server response. In MQL5, OrderSend is often asynchronous, and you receive the result later via trade transaction events. While Error 129 is tied to MQL4’s synchronous model where the rejection is immediate, the principle remains: the price is validated server-side at the point of attempted execution. The potential for price change between your decision and server validation is the core issue. Understanding this difference highlights why obtaining the absolute freshest price just before the call is paramount in MQL4.
In conclusion, Error 129 ‘Invalid Price’ in MQL4 is primarily a result of discrepancies or rule violations related to the price parameter provided to trade functions. By understanding the causes – incorrect price type, insufficient slippage, broker restrictions, and stale data – and implementing rigorous debugging, validation, and dynamic handling techniques, you can significantly reduce its occurrence and build more robust and reliable automated trading systems.