Trailing stops are an essential tool for traders who want to secure profits as the market moves in a favorable direction. In the context of the MetaTrader 5 platform using MQL5, the trailing stop technique allows you to automatically adjust the stop loss order based on real-time price movements. This document provides an in-depth guide on how to implement a trailing stop for an already opened position.
The guiding principle behind a trailing stop is fairly straightforward: as a position becomes profitable, the stop loss is shifted closer to the current price – thereby protecting the trader’s accumulated gains. When the market reverses, the stop loss remains at the adjusted level, potentially triggering an exit that locks in the profit without requiring constant manual intervention.
To successfully add a trailing stop to an open position, it is first necessary to understand the key underlying concepts:
The trailing stop logic is typically executed within an Expert Advisor’s OnTick() function. Given that this function is triggered with every tick (update of price), it is the ideal place to implement logic that monitors the live market. The key is to:
The success and responsiveness of a trailing stop mechanism rely on three critical parameters:
These parameters ensure that the stop loss does not shift too early, thus avoiding premature stop-outs, but also not too late so that the profits are effectively protected.
In MQL5, working with trades and modifying orders can be achieved using specific structures and API functions:
Using these built-in functions, you can safely and programmatically adjust the stops on your open positions.
The initial step in enabling a trailing stop on an open position is to iterate over all available positions. Using the built-in function PositionSelectByIndex(), you can extract details about individual positions, including symbols, open prices, and current prices.
//+------------------------------------------------------------------+
//| Function to update trailing stop for each open position |
//+------------------------------------------------------------------+
void CheckAndApplyTrailingStop()
{
// Loop through open positions, checking each one
for (int i = PositionsTotal() - 1; i >= 0; i--)
{
if (PositionSelectByIndex(i))
{
ulong ticket = PositionGetInteger(POSITION_TICKET);
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// Proceed with trailing calculations for positions that fulfill the criteria.
UpdatePositionTrailingStop(ticket, type, currentPrice, openPrice);
}
}
}
In the snippet above, each position is selected sequentially (ensuring the safe handling of the positions array). The current price and open price form the basis for subsequent logic that determines if a trailing adjustment is in order.
Determining whether to adjust a trailing stop depends on checking if the market price has moved favorably beyond the previously defined trailing start threshold. For a long position (buy), the logic ensures that if the current price exceeds the open price by a specified amount, the stop loss is recalculated by subtracting the trailing distance from the current price. Conversely, for a short position (sell), the calculation adds the trailing distance.
The calculation involves comparing the new potential stop loss with the existing one. Only if the new stop loss represents a more favorable position (i.e., further in profit) does the system execute an update.
| Position Type | Calculation | Condition |
|---|---|---|
| Buy | Current Price - (Trailing Distance * Point) | New Stop Loss > Open Price and New Stop Loss is greater than previous stop loss |
| Sell | Current Price + (Trailing Distance * Point) | New Stop Loss < Open Price and New Stop Loss is lower than previous stop loss |
This table concisely summarizes the computation and condition-checking mechanism behind the trailing stop logic. Using a conditional approach ensures that the trailing stop only moves in a direction that favors securing profits.
void UpdatePositionTrailingStop(ulong ticket, ENUM_POSITION_TYPE type, double currentPrice, double openPrice)
{
double newStopLoss = 0;
double previousStopLoss = PositionGetDouble(POSITION_SL);
// Define the trailing distance in points (assuming a fixed distance for this example)
double trailDistance = 100 * _Point; // Example: 100 points trail
if (type == POSITION_TYPE_BUY)
{
newStopLoss = currentPrice - trailDistance;
// Only adjust if the new stop loss is more favorable
if (newStopLoss > openPrice && newStopLoss > previousStopLoss)
{
ModifyStopLoss(ticket, newStopLoss);
}
}
else if (type == POSITION_TYPE_SELL)
{
newStopLoss = currentPrice + trailDistance;
if (newStopLoss < openPrice && newStopLoss < previousStopLoss)
{
ModifyStopLoss(ticket, newStopLoss);
}
}
}
This code calculates the new potential stop loss based on the current market price and only executes an update if the new stop is better than the existing one. It is crucial to maintain a robust check to avoid unnecessary modifications that could lead to premature stop-outs.
Once the new stop loss level is computed, the next step is to modify the open position by sending a trade request. MQL5’s OrderSend() function is used in conjunction with MqlTradeRequest and MqlTradeResult to execute this update.
The following code snippet shows how to encapsulate the stop loss modification in a function:
void ModifyStopLoss(ulong ticket, double newStopLoss)
{
MqlTradeRequest request = {0};
MqlTradeResult result = {0};
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = newStopLoss;
request.deviation = 3; // Acceptable deviation in points
if(!OrderSend(request, result))
{
Print("Error modifying stop loss for ticket ", ticket, ": ", GetLastError());
}
else
{
Print("Stop loss updated to ", newStopLoss, " for ticket ", ticket);
}
}
Here, the TRADE_ACTION_SLTP action instructs the terminal to modify only the stop loss (or take profit) value. The deviation parameter provides a buffer which can help ensure that the command goes through despite minor fluctuations.
The trailing stop mechanism becomes most effective when integrated directly into your EA’s main event handler – typically the OnTick() function. This integration ensures the trailing stop’s conditions are continuously evaluated as new market data is received.
Below is an example that combines iterating through open positions, computing the new stop loss values, and modifying these stops inside the OnTick() callback:
//+------------------------------------------------------------------+
//| Expert Advisor OnTick implementation with trailing stop logic |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if any open positions exist and update their trailing stops
if(PositionsTotal() > 0)
{
CheckAndApplyTrailingStop();
}
}
This simple yet effective implementation ensures that every time new price data is available, the EA reviews all open positions and applies the trailing stop logic accordingly. For more advanced applications, consider incorporating specific parameters such as trailing start and trailing step values which can be declared as external input variables.
In a real-world environment, a one-size-fits-all trailing distance may not be optimal. Instead, traders often define these trailing parameters to fit different instruments, volatility levels, or trading strategies. By fine-tuning parameters like the trailing start, step, and distance, you can tailor the risk management process.
The following table summarizes potential input parameters that a trader might adjust:
| Parameter | Description | Example Value |
|---|---|---|
| Trailing Start | Minimum move in points or profit before the trailing stop activates | 50 |
| Trailing Step | Points moved that trigger additional trailing stop adjustments | 20 |
| Trailing Distance | Distance from the current price at which the new stop loss is set | 10 |
Adjust these parameters based on your trading instrument and risk profile. This flexibility allows your EA to be robust across different market conditions, ensuring that the trailing stop provides maximum utility without interfering with your overall strategy.
For more complex trading strategies, it might be beneficial to encapsulate the trailing stop logic in a class. By doing so, you create a modular component that can be instantiated with specific parameters and reused across different trading scenarios.
A class-based structure helps in maintaining readability, reduces code duplication, and makes the integration of additional features easier.
//+------------------------------------------------------------------+
//| Trailing Stop Class for handling stop loss adjustments |
//+------------------------------------------------------------------+
class CTrailingStop
{
private:
string m_symbol;
long m_magic;
int m_trailStart;
int m_trailStep;
int m_trailDistance;
public:
// Constructor to initialize trailing stop parameters
CTrailingStop(string symbol, long magic, int trailStart, int trailStep, int trailDistance)
{
m_symbol = symbol;
m_magic = magic;
m_trailStart = trailStart;
m_trailStep = trailStep;
m_trailDistance = trailDistance;
}
// Method to update trailing stop for the specific position
void UpdateTrailingStop()
{
for(int i = PositionsTotal()-1; i >= 0; i--)
{
if(PositionSelectByIndex(i) &&
PositionGetString(POSITION_SYMBOL) == m_symbol &&
PositionGetInteger(POSITION_MAGIC) == m_magic)
{
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double newStopLoss = 0;
if(type == POSITION_TYPE_BUY)
{
newStopLoss = currentPrice - m_trailDistance * _Point;
if(newStopLoss > openPrice && newStopLoss > PositionGetDouble(POSITION_SL))
{
ModifyPositionStopLoss(PositionGetInteger(POSITION_TICKET), newStopLoss);
}
}
else if(type == POSITION_TYPE_SELL)
{
newStopLoss = currentPrice + m_trailDistance * _Point;
if(newStopLoss < openPrice && newStopLoss < PositionGetDouble(POSITION_SL))
{
ModifyPositionStopLoss(PositionGetInteger(POSITION_TICKET), newStopLoss);
}
}
}
}
}
// Helper method to modify the stop loss of a position
void ModifyPositionStopLoss(ulong ticket, double newStopLoss)
{
MqlTradeRequest request = {0};
MqlTradeResult result = {0};
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = newStopLoss;
request.deviation = 3;
if(!OrderSend(request, result))
{
Print("Error in modifying SL for ticket ", ticket, ": ", GetLastError());
}
}
};
Once such a class is defined, you can simply instantiate it when a new position is opened and consistently update its trailing stop by calling the UpdateTrailingStop() method within your OnTick() function.
Before deploying your trailing stop logic in a live trading environment, thorough testing using a demo account is crucial. Testing scenarios should include:
Optimization may involve fine-tuning the trailing starting point and the step interval so that the strategy is neither too conservative (causing missed profit opportunities) nor too aggressive (leading to premature stop-outs).
Implementing a trailing stop creates additional processing overhead since the EA must continuously check and evaluate each open position on every market tick. To ensure high performance:
In conclusion, adding a trailing stop to an open position in MQL5 involves a multi-step process that integrates real-time market analysis with robust programming techniques. By continuously monitoring each position, calculating the new stop loss based on predefined trailing parameters, and executing an update via MQL5’s trade functions, traders can dynamically protect their profit margins.
We explored not only the basic iterative process needed to assess open positions, but also the detailed calculation of the new stop loss value and how to utilize MQL5 functions such as OrderSend() to update these values. Furthermore, the development of a class-based approach was highlighted to demonstrate a modular and reusable solution that can easily be integrated with more advanced trading strategies.
Whether employing a simple functional approach or a full-fledged trailing stop class, careful testing and parameter optimization are essential to accommodate varying market conditions. Ultimately, automation of trailing stops via MQL5 acts as a strategic tool to lock in profits while allowing trades to run in potentially favorable conditions.