MQL4 Trailing Stop

There might be many different types of trailing stop possibilities out there, but for our purposes, we will be examining the most effective: the trailing stop that will activate after a custom threshold and then trail by a custom level of pips. This trailing stop moves the stop loss up or down with the order price after it first reaches profit threshold. Learn in this MQL4 Trailing Stop article how to program your Expert Advisor with trailing stops locking in profit and providing loss protection.

For instance,  if you set the profit threshold to 50 and the trailing stop to 50, the trade will have to move 50 pips in profit before the trailing stop is activated, at which point the stop will be moved to break even. If you further indicated that your trailing step is 5, then the market would have to move up 5 pips more in profit before your stop would rise 5 pips above breakeven. It would then adjust increasingly upwards on that basis, locking in 5 more pips of profit for each additional 5 pip gain. 

We will thus need three external variables for this new trailing stop, one indicating the profit threshold, the other indicating the trailing stop, and the last indicating the stepping distance: 

extern double TrailingStart = 50;
extern double TrailingStop = 25;
extern double TrailingStep = 5;

The variables that the user manipulates are thus the TrailingStart, which is the profit threshold that needs to be achieved before the trailing stop can be activated; the TrailingStop, which is the amount of pips we will trail from new profit highs; and the TrailingStep, the amount of new pips that need to be gained before the stop can be increased by the amount of the gain. 

Let us examine the code for this trailing stop:

void TrailOrder(double Trailingstart,double Trailingstop){
int ticket = 0;
double tStopLoss = NormalizeDouble(OrderStopLoss(), Digits); // Stop Loss
int cnt,vPoint,vSlippage;

double sl     = OrderStopLoss(); // Stop Loss

  if (Digits == 3 || Digits == 5)
    {vPoint = Point * 10; vSlippage = Slippage * 10;    }
    else
    {vPoint = Point; vSlippage = Slippage;}

 

RefreshRates();
if(OrdersTotal()>0){
for(cnt=OrdersTotal();cnt>=0;cnt–){
OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
if(OrderType()<=OP_SELL && OrderSymbol()==Symbol()
&& OrderMagicNumber()==MagicNumber){

if(OrderType()==OP_BUY){
if(Ask> NormalizeDouble(OrderOpenPrice()+TrailingStart* vPoint,Digits)
 && tStopLoss < NormalizeDouble(Bid-(TrailingStop+TrailingStep)*vPoint,Digits)){
tStopLoss = NormalizeDouble(Bid-TrailingStop*vPoint,Digits);
ticket = OrderModify(OrderTicket(),OrderOpenPrice(),tStopLoss,OrderTakeProfit(),0,Blue);
if (ticket > 0){
Print (“TrailingStop #2 Activated: “, OrderSymbol(), “: SL”, tStopLoss, “: Bid”, Bid);
return(0);
}}}

if (OrderType()==OP_SELL) {
if (Bid < NormalizeDouble(OrderOpenPrice()-TrailingStart*vPoint,Digits)
&& (sl >(NormalizeDouble(Ask+(TrailingStop+TrailingStep)*vPoint,Digits)))
|| (OrderStopLoss()==0)){
tStopLoss = NormalizeDouble(Ask+TrailingStop*vPoint,Digits);
ticket = OrderModify(OrderTicket(),OrderOpenPrice(),tStopLoss,OrderTakeProfit(),0,Red);
if (ticket > 0){
Print (“Trailing #2 Activated: “, OrderSymbol(), “: SL “,tStopLoss, “: Ask “, Ask);
return(0);
}}}
}}}}

 

Explanation

We are using void TrailOrder() as our custom function. 

You will notice the for loop and the OrderSelect() function from our order loop code. It will loop through the order pool and examine each order to see if we need to apply a trailing stop to it. If the current market order is a buy market order, as indicated by OP_BUY, and if it matches our chart symbol and magic number, we will proceed with the trailing stop calculation. 

There are two conditions that must be determined prior to activating the two trailing stop:

Condition1: Check for TrailingStart. We are asking the program to watch if the ask price is greater than the open price plus the TrailingStart value. For instance, if the trailing start was set to 50 pips, then the trailing stop can only be activated only if the market reaches 50 pips in profit. It makes much more sense to have the trailing stop be activated after a profit threshold than at the onset. 

Condition2: Check for TrailingStop and TrailingStep. This second condition checks to see if the current stop loss is less than the current price minus the trailing stop and trailing step. For instance, if the trailing start were 50 pips, trailing stop 25 pips and the trailing step 5 pips, then when the trade moves in profit by 50 pips, condition1 is activated, the program can proceed to condition2, checking for TrailingStop and TrailingStep. Condition2 then makes sure that the first stop is adjusted 25 pips below the 50 pips profit threshold. If the trade continues to move in profit, the trailing stop would move up an additional 5 pips for each additional 5 pip increase, as defined TrailingStep amount. 

We determine the stop loss distance by subtracting our trailing stop setting, multiplied by vPoint(), from the current bid price. This is stored in the variable tStopLoss. We pass the tStopLoss variable as our new stop to the OrderModify() function. 

Note: We use the MQL function NormalizeDouble() to round our variables to the correct number of digits after the decimal point. Prices can be quoted up to eight decimal places, and NormalizeDouble() helps to round that down to 4 or 5 digits (2-3 digits for JPY pairs).

We are adding a Print function here to our code to give us a print read-out on our journal tab of when this trailing stop was activated. 
 
In general, the trailing stop conditions for the sell orders follow the same logic as the buy orders. 


So now, how does this custom function get inserted into our Start() Function so that our code knows that we are working with it? 

We simply put a single line anywhere within the start() function, preferably near the beginning of the function: 

if(TrailingStop>0 && TrailingStart > 0) TrailOrder (TrailingStart, TrailingStop);

Basically, I am creating a conditional if expression that checks to see if TrailingStart and TrailingStop are both greater than 0, that is, if the user has defined integer values for these fields. If so, then the custom function, TrailOrder(), will be activated. If not, if both or either field remains at the default of 0, the trailing stop remains deactivated. 

That is all there is to it. You now have a sophisticated trailing stop mechanism as an extra weapon in your arsenal.