Problem Description
Solution
To change the entry price of a pending limit order when the market moves away from it, you need to use the OrderModify() function. This logic is often referred to as a "Trailing Pending Order."
The concept works similarly to a Trailing Stop, but instead of modifying the Stop Loss of an open position, it modifies the Open Price of a pending order to maintain a specific distance from the current market price.
Logic Implementation
- Identify the Order: Loop through the order pool to find your specific pending order (Buy Limit or Sell Limit).
- Calculate Distance: Compare the current market price (
Askfor Buy Limits,Bidfor Sell Limits) with the order's current open price. - Check Threshold: If the gap between the market price and the order price exceeds your defined
TrailDistance, calculate the new entry price. - Modify: Use
OrderModify()to update the entry price.
MQL4 Code Example
Here is a complete Expert Advisor (EA) script that monitors pending limit orders and drags them along if the market moves away, keeping them at a fixed distance.
//+------------------------------------------------------------------+
//| TrailingPendingOrder.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Input Parameters
input int TrailDistancePips = 20; // Distance to maintain from market price (Pips)
input int MagicNumber = 12345; // Magic number to filter orders
input bool ModifyStopLoss = true; // Move SL relative to new price?
input bool ModifyTakeProfit = true; // Move TP relative to new price?
//--- Global variables
double m_point;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Adjust point for 3/5 digit brokers
if(Digits == 3 || Digits == 5) m_point = Point * 10;
else m_point = Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Loop through all orders
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// Filter by Symbol and Magic Number
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
// Handle Buy Limits
if(OrderType() == OP_BUYLIMIT)
{
ProcessBuyLimit();
}
// Handle Sell Limits
if(OrderType() == OP_SELLLIMIT)
{
ProcessSellLimit();
}
}
}
}
}
//+------------------------------------------------------------------+
//| Logic to trail Buy Limit orders |
//+------------------------------------------------------------------+
void ProcessBuyLimit()
{
// Current market price is Ask.
// If Ask moves up (away), we want to pull the Buy Limit up.
double targetPrice = Ask - (TrailDistancePips * m_point);
// Check if the calculated target price is higher than the current order price
// We add Point to ensure we don't modify for tiny fluctuations (floating point errors)
if(targetPrice > OrderOpenPrice() + Point)
{
// Ensure we are not too close to market (FreezeLevel check)
double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
if(Ask - targetPrice < stopLevel) return;
double newPrice = NormalizeDouble(targetPrice, Digits);
double newSL = OrderStopLoss();
double newTP = OrderTakeProfit();
// Calculate relative SL/TP shift
if(ModifyStopLoss && OrderStopLoss() > 0)
{
double diff = newPrice - OrderOpenPrice();
newSL = NormalizeDouble(OrderStopLoss() + diff, Digits);
}
if(ModifyTakeProfit && OrderTakeProfit() > 0)
{
double diff = newPrice - OrderOpenPrice();
newTP = NormalizeDouble(OrderTakeProfit() + diff, Digits);
}
// Modify the order
if(!OrderModify(OrderTicket(), newPrice, newSL, newTP, OrderExpiration(), clrBlue))
{
Print("Error modifying Buy Limit: ", GetLastError());
}
}
}
//+------------------------------------------------------------------+
//| Logic to trail Sell Limit orders |
//+------------------------------------------------------------------+
void ProcessSellLimit()
{
// Current market price is Bid.
// If Bid moves down (away), we want to pull the Sell Limit down.
double targetPrice = Bid + (TrailDistancePips * m_point);
// Check if the calculated target price is lower than the current order price
if(targetPrice < OrderOpenPrice() - Point)
{
// Ensure we are not too close to market (FreezeLevel check)
double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
if(targetPrice - Bid < stopLevel) return;
double newPrice = NormalizeDouble(targetPrice, Digits);
double newSL = OrderStopLoss();
double newTP = OrderTakeProfit();
// Calculate relative SL/TP shift
if(ModifyStopLoss && OrderStopLoss() > 0)
{
double diff = newPrice - OrderOpenPrice(); // diff is negative here
newSL = NormalizeDouble(OrderStopLoss() + diff, Digits);
}
if(ModifyTakeProfit && OrderTakeProfit() > 0)
{
double diff = newPrice - OrderOpenPrice(); // diff is negative here
newTP = NormalizeDouble(OrderTakeProfit() + diff, Digits);
}
// Modify the order
if(!OrderModify(OrderTicket(), newPrice, newSL, newTP, OrderExpiration(), clrRed))
{
Print("Error modifying Sell Limit: ", GetLastError());
}
}
}
//+------------------------------------------------------------------+
Key Components of the Code
TrailDistancePips: This input determines the gap you want to maintain. For example, if set to 20 pips, the Buy Limit will always try to stay 20 pips below the current Ask price as the price rises.ProcessBuyLimit:- It calculates
targetPrice = Ask - Distance. - It checks
if (targetPrice > OrderOpenPrice()). This ensures we only move the order up (following the market away). We do not move it down if the market comes closer to the order (that would prevent the order from ever filling).
- It calculates
ProcessSellLimit:- It calculates
targetPrice = Bid + Distance. - It checks
if (targetPrice < OrderOpenPrice()). This ensures we only move the order down as the market drops away.
- It calculates
- SL/TP Adjustment: The code calculates the difference between the old open price and the new open price, then applies that difference to the Stop Loss and Take Profit. This ensures your risk/reward ratio remains exactly the same as the order moves.
- Normalization:
NormalizeDouble(..., Digits)is crucial. Without it,OrderModifywill fail with Error 129 (Invalid Price) because calculated doubles often have invisible trailing decimals (e.g., 1.12345000001).
Q&A: Modifying Pending Orders
Q: Why does OrderModify return Error 130 (Invalid Stops) when moving a pending order?
A: This usually happens if you move the entry price too close to the current market price, violating the broker's MODE_STOPLEVEL (Freeze Level). The code provided includes a check for stopLevel to prevent this. It can also happen if you move the entry price but fail to move the SL/TP, causing the SL/TP to end up on the "wrong side" of the current market price relative to the new entry.
Q: Can I use this logic for Stop Orders (Buy Stop / Sell Stop)?
A: Yes, but the logic must be inverted. For a Buy Stop (which is above price), if the price moves down (away), you would calculate Ask + Distance and move the Buy Stop down. The provided code is specifically for Limit orders.
Q: Does this code work on 5-digit brokers?
A: Yes. The OnInit function detects 3 or 5 digit brokers and adjusts the m_point multiplier automatically so that TrailDistancePips represents standard pips (e.g., 0.0001) rather than points (0.00001).