Download MetaTrader 5

A Pattern Trailing Stop and Exit the Market

27 June 2008, 08:29
Sergey Kravchuk
1
2 807

Introduction

Developers of order modification/closing algorithms suffer from an imperishable woe - how to compare results obtained by different methods? The mechanism of checking is well known - it is Strategy Tester. But how to make an EA to work equally for opening/closing orders? The article describes a tool that provides strong repetition of order openings that allows us to maintain a mathematically correct platform to compare the results of different algorithms for trailing stops and for exiting the market.

If you are debugging a complex EA that is supposed to independently calculate the time to enter the market, trailing stops, and exiting the market, then it is practically impossible to get a repeatable pattern to be compared to other ones. Imagine a situation where there is a rather long signal for opening an order. Ideally, the order will be opened. Then, if the direction chosen is correct and the price moves in the predicted direction, trailing stop starts to work.According to price fluctuations, a too closely placed stop can too early close an order that could otherwise "top up" the profits. If the opening signal is still valid at that time, the EA will close a new order. As a result, we will have to compare the results of one, "correct" order with the results of a few other orders that have been opened after early closures. To avoid this situation, the following will be suggested.


Problem Statement

  1. Order opening/closing point patterns are marked in the chart.
  2. Opening/closing times and trade direction (buy/sell) are saved in a file.
  3. Expert Advisor to read the prepared file and to strictly execute its commands is created.

Opening points must be set at market reversals - it's good that they are quite obvious on history. However, the closing points should be selected not when the price reaches the opposite reversal point, but after that. Don't forget that our task is to optimize trailing and exiting the market, so we should allow any algorithm, even improper one, to work "to its end". If it is still unable to fix the profit, we will see its losses that will become a signal to us to rework the algorithm.


Look at the picture above. The violet line shows ideally correct enter and exit. It can be used for calculation of the maximal profits we want to/can gain. However, for the purposes of trailing tests, we will use something similar to the blue line. It shows the nature of real trading: enter with some delay (like, we were waiting for reversal confirmation) and closing on the edge of break-even (like we were frightened of a strong reversal started and of that we could lose everything).

In trading performed "along the blue line", there are three potential points of triggering stops after trailings:

  1. Aggressive trailing at the minimal distance from the current price.
  2. Normal, "patient" trailing.
  3. Ideal trailing "sweating" profits up to the last drop.

Besides, a false triggering of the too "impatient" trailing may take place in the area round point 4.

Now that we know how to "mark" ideal areas, the only thing remaining is to make it as comfortable as possible.


Marking Tools


To facilitate the chart marking with ideal lines, let's prepare a set of scripts. Two scripts, TL_Buy and TL_Sell, will create marking lines for buying and selling operations, respectively. Script TL_Write will look through all created lines and save their characteristics in a file for Expert Advisor TL_Trade to work with them. One more script TL_Read will be able to read the created file, and to re-form all lines on its basis. This may become useful for correcting the available lines or for adding some new ones, or for removing the existing ones.

For read/write scripts to be able to work with their lines, we will name all the lines according certain rules:

  1. names of all ideal lines start with the same prefix (TL_). You can later use the prefix to select and delete the lines;
  2. the prefix is followed by one character that is the operation code: B-buy, S-sell;
  3. the operation code in the line name is followed by the line number to distinguish the lines from each other.


As a result, we should get in the chart the lines of, for example, the following names: TL_B1 TL_B2, TL_S3, etc.

Scripts that build lines are simply dropped on the chart, and the corresponding line appears in the dropping point. You can move its ends in such a way that they mark the necessary ideal "blue line" of trading. When attached to the chart, the read/write scripts request the name of the file to be saved and read. This will allow us to easily use different sets of lines, for example, for different currency pairs.

The code of the scripts is quite transparent and provided with all necessary comments, so I'll take the liberty to skip the description of their algorithms - you can see them from their codes.

/****************************************************************
 PATTERN TRADING: TL_Buy - creation of a new, pattern buying line
 Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
int start()
{
  int MaxNo=0,i,No; 
  
  if(WindowOnDropped()!=0) { MessageBox("Script should be dropped in the main window","ERROR", IDOK + MB_ICONERROR); return(1); }
 
  // find the maximum suffix number for all lines
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) 
    {
      No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // select the line number
      if(MaxNo<No) MaxNo=No; // store it, if it is larger
    }
  }
  
  datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // find the coordinates of the script dropping point
 
  int width = 5*Period()*60;                             // width of the created line in bars converted into time units
  double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // height of the created line in ticks converted into price units
  
  string LineName = _prefix_+"B"+(MaxNo+1);  // create a name for a new line
  ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0-height, t0+width,p0+height); // create a line
  ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray
  ObjectSet(LineName,OBJPROP_WIDTH,2);   // set its width
  ObjectSet(LineName,OBJPROP_COLOR,Blue); // set its color
 
}

/****************************************************************
 PATTERN TRADING: TL_Sell - creation of a new, pattern selling line
 Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua
*****************************************************************/
 
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
int start()
{
  int MaxNo=0,i,No; 
  
  if(WindowOnDropped()!=0) { MessageBox("Script should be dropped in the main window","ERROR", IDOK + MB_ICONERROR); return(1); }
 
  // find the maximum suffix number for all lines
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) 
    {
      No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // select the line number
      if(MaxNo<No) MaxNo=No; // store it, if it is larger
    }
  }
  
  datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // find the coordinates of the script dropping point
 
  int width = 5*Period()*60;                             // width of the created line in bars converted into time units
  double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // height of the created line in ticks converted into price units
  
  string LineName = _prefix_+"S"+(MaxNo+1);  // create a name for a new line
  ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0+height, t0+width,p0-height); // create a line
  ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray
  ObjectSet(LineName,OBJPROP_WIDTH,2);   // set its width
  ObjectSet(LineName,OBJPROP_COLOR,Red); // set its color
 
}

/****************************************************************
 PATTERN TRADING: TL_Write - saving the coordinates of pattern lines in a file
 Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
#property show_inputs
 
extern string FileNameForWrite = "TL_DATA.TXT";
 
int start()
{
  int LinesCNT=0,i; string Operation; double p; datetime t;
  
  int fh=FileOpen(FileNameForWrite,FILE_CSV|FILE_WRITE,';');
 
  // look through all lines created and save the opening commands for the EA from them
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) // our line
    {
      string LineName = ObjectName(i);  
 
      datetime t1=ObjectGet(LineName,OBJPROP_TIME1);
      datetime t2=ObjectGet(LineName,OBJPROP_TIME2);
 
      double p1=ObjectGet(LineName,OBJPROP_PRICE1);
      double p2=ObjectGet(LineName,OBJPROP_PRICE2);
 
      LinesCNT++; // increase the counter for producing the final message
      
      Operation = StringSubstr(ObjectName(i),StringLen(_prefix_),1); 
      
      // prices are necessary only for restoring the line in the chart
      FileWrite(fh,Operation,TimeToStr(t1),DoubleToStr(p1,Digits),TimeToStr(t2),DoubleToStr(p2,Digits)); 
    }
  }
  
  FileClose(fh);
  
  MessageBox("Stored sections "+(LinesCNT)+" pcs.","Done", IDOK + MB_ICONINFORMATION);
}


/****************************************************************
 PATTERN TRADING: TL_Read - drawing pattern lines from the file
 Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
#property show_inputs
 
extern string FileNameForRead = "TL_DATA.TXT";
 
int start()
{
  int LinesCNT=0,i; 
  
  int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';');
  if(fh<0) { MessageBox("Error opening file \"" + FileNameForRead + "\"","ERROR", IDOK + MB_ICONERROR); return(1); }
 
  // first of all, delete everything
  for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; } }
 
  // look through all lines created and save the opening commands for the EA from them
  while(true)
  {
    string Operation=FileReadString(fh);
    
    if(FileIsEnding(fh)) break; // file ended? - exit
    
    // read the section's coordinates
    datetime t1=StrToTime(FileReadString(fh));
    double   p1=StrToDouble(FileReadString(fh));
    datetime t2=StrToTime(FileReadString(fh));
    double   p2=StrToDouble(FileReadString(fh));
 
    // draw a section
    LinesCNT++;
    string LineName = _prefix_+Operation+(LinesCNT);  // create a name for a new line
    ObjectCreate(LineName,OBJ_TREND,0,t1,p1, t2,p2);  // create a line
    ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray
    ObjectSet(LineName,OBJPROP_WIDTH,2);   // set its width
    if(Operation=="B") ObjectSet(LineName,OBJPROP_COLOR,Blue); else  ObjectSet(LineName,OBJPROP_COLOR,Red);// set its color
 
  }
  FileClose(fh);
  
  MessageBox("Read sections "+(LinesCNT)+" pcs.","Done", IDOK + MB_ICONINFORMATION);
}


/****************************************************************
 PATTERN TRADING: TL_Clear - deletion of all pattern lines
 Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
int start()
{
  int LinesCNT=0,i;
  
  for(i=0;i<ObjectsTotal();i++) 
  {
    if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; LinesCNT++; }
  }
}

File Positioning


Positioning files is a very important point. Using standard means, the working scripts can only create files in directory c:\Program Files\MetaTrader 4\experts\files. However, when testing Expert Advisors, the Tester has access to the folder of the same name located "inside its own directory", c:\Program Files\MetaTrader 4\tester\files.

This is why, after creation of files and before using them in the testing EA, you should independently copy them from c:\Program Files\MetaTrader 4\experts\files into c:\Program Files\MetaTrader 4\tester\files.

You will have to repeat this operation after re-creation of the file having changed something in the lines.

Testing EA


The testing EA doesn't produce any difficulties in its code. The following blocks are emphasized in it:

  1. order closing block when reaching the end of the pattern section;
  2. order opening block when reaching the start of the pattern section;
  3. block testing trailing stop and exiting the market.

Their work is quite obvious in the source code. Only a few comments must be given here:

  1. Since the lines can be created in no particular order, the entire set of lines will be tested at each "tick" of the Tester, in order to find those, on which it is necessary to open/close.
  2. Opening/closing times are written in the comment on the order in the internal format of time and date representation. This is necessary, first of all, for us not to open the same order several times if it is closed by trailing well in advance before its pattern line ends. Secondly, when checking open orders and taking their closing times from their comments, we will close the order exactly at the time when its control line is ended, since the closing time is written in the open order itself.
  3. Parameter ProcedTrailing enables/disables trailing processing. This will allow us to pass the EA without trailing at all to get the most pessimistic result in order to compare it to the obtained optimization results.

/****************************************************************
 PATTERN TRADING: TL_Trader - trading on pattern lines
 Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua
*****************************************************************/
 
#include <WinUser32.mqh>
 
#define _prefix_ "TL_"
 
extern string FileNameForRead = "TL_DATA.TXT";
extern double Lots = 0.1;
extern double StopLoss = 0;
extern double TrailingStop = 30;
extern bool   ProcedTrailing=true; // process the trailing block
 
double SL; // to calculate the SL values for opening an order
 
int start()
{
  int LinesCNT=0,i,ticket,pos; double p; datetime t; string s;
 
  int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';'); // to test the file, it is necessary to pout it into tester\files\TL_DATA.txt
 
  if(fh<0) { MessageBox("Error opening file \"" + FileNameForRead + "\"","ERROR", IDOK + MB_ICONERROR); return(1); }
 
  // check all entries: if the opening time has already passed and no order with such a comment is found in history or in open orders
  // then it has not been opened yet - open it as it's said there
 
  while(true)
  {
    string Operation=FileReadString(fh);
    
    if(FileIsEnding(fh)) break; // file ended? - exit
    
    // count the section coordinates
    string st1=FileReadString(fh);
    string sp1=FileReadString(fh);
    string st2=FileReadString(fh);
    string sp2=FileReadString(fh);
    datetime t1=StrToTime(st1);
    double   p1=StrToDouble(sp1);
    datetime t2=StrToTime(st2);
    double   p2=StrToDouble(sp2);
    
  
    // what if sections' ends are mixed?
    if(t1>t2) { p=p1; p1=p2; p2=p; t=t1; t1=t2; t2=t;  s=st1; st1=st2; st2=s; s=sp1; sp1=sp2; sp2=s; } 
 
    string MarkComent = t1+"="+t2;
    
    //**********************************************************************************
    // 1) block closing the orders as soon as the end of the pattern section is reached.
    //**********************************************************************************
    for(i=0;i<OrdersTotal();i++)
    {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
      if(OrderComment()==MarkComent && TimeCurrent()>=t2) // order must be closed
      {
        if(OrderType()==OP_BUY) OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
        else OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
      }
    }
 
    //**********************************************************************************
    // 2) block opening orders as soon as the beginning of the pattern section is passed.
    //**********************************************************************************
    bool OrderNotPresent=true; // a sign showing that we haven't opened such an order yet
    if(t1<=TimeCurrent() && TimeCurrent()<t2) // time to open - make sure that this order is not opened or closed
    {
      for(i=0;i<OrdersTotal();i++)
      {
        if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
        if(OrderComment()==MarkComent) { OrderNotPresent=false; break; } // order already exists
      }
      for(i=0;i<OrdersHistoryTotal() && OrderNotPresent;i++)
      {
        if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) continue;
        // order in history is added to the end, something like "[sl]" - it must be cut off!!
        pos = StringFind(OrderComment(),"[");
        string CurOrderComment = StringSubstr(OrderComment(),0,pos);
        if(CurOrderComment==MarkComent) { OrderNotPresent=false; break; } // order already exists
      }
      if(OrderNotPresent) // no such order - open it
      {
        // open an order
        if(Operation=="B") // Buy
        { 
          if(StopLoss<=0) SL=0; else SL=Ask-StopLoss*Point;
          ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,SL,0,MarkComent,1235,0,Blue);
          OrderSelect(ticket,SELECT_BY_TICKET);
        }
        else // Sell
        {
          if(StopLoss<=0) SL=0; else SL=Bid+StopLoss*Point;
          ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,SL,0,MarkComent,1235,0,Red);
          OrderSelect(ticket,SELECT_BY_TICKET);
        }
      }
    }
  }
  
  FileClose(fh);
 
  
  //******************************************************
  // 3) block testing trailing stop and exit the market
  //******************************************************
  if(ProcedTrailing)
  {
    for(i=0;i<OrdersTotal();i++)
    {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
      if(OrderType()==OP_BUY)
      {
        if(Bid-OrderOpenPrice()>Point*TrailingStop)
        {
         if(OrderStopLoss()<Bid-Point*TrailingStop)
           {
            OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
            return(0);
           }
        }
      }
      if(OrderType()==OP_SELL)
      {
       if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
         {
          if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
            {
             OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
             return(0);
            }
         }
      }
    }
  }
  
  return(0);
}

How the System Works When "Fully Integrated"


To test the system, we used a microset of 14 lines. Below is the content of the pattern file named TL_DATA.txt:

B;2007.12.28 05:00;1.4605;2008.01.09 22:00;1.4658
B;2008.01.29 05:00;1.4767;2008.02.05 05:00;1.4811
B;2008.02.15 16:00;1.4687;2008.02.21 09:00;1.4735
B;2008.02.21 14:00;1.4738;2008.02.26 07:00;1.4812
B;2008.02.28 14:00;1.5129;2008.03.05 12:00;1.5186
B;2008.03.05 22:00;1.5261;2008.03.11 20:00;1.5316
B;2008.03.13 01:00;1.5539;2008.03.18 22:00;1.5620
B;2008.03.26 14:00;1.5724;2008.03.28 10:00;1.5758
S;2007.11.30 13:00;1.4761;2007.12.10 22:00;1.4711
S;2007.12.14 04:00;1.4626;2007.12.28 00:00;1.4610
S;2008.01.17 17:00;1.4688;2008.01.24 13:00;1.4671
S;2008.02.07 12:00;1.4633;2008.02.14 11:00;1.4617
S;2008.03.19 23:00;1.5641;2008.03.25 23:00;1.5629
S;2008.03.31 19:00;1.5811;2008.04.08 04:00;1.5796

This is how it looks on a chart:

Not the most efficient trading, but an ideal ground to test trailing. ;)

If we start the Tester with trailing stops disabled (ProcedTrailing=false), we will obtain such a poor result:



A simple optimization by trailing-stop size provides the optimal value of 95 points.


The optimal value producing the largest profit is in good agreement with the bar size statistics of the period under test: 95 points make 98% of all bars of this period, which is clearly seen in the chart of the ft.BarStat indicator.


As you can see, the optimized results look much more attractively:




They are seen in the chart even more clearly. Please note that all orders were opened precisely in the beginning of pattern lines!


Look at the very first section (March, 5). The profits were licked off by a down spike, but the general buy trend remained the same. If we tested trailing with a normal EA, it quite probably open a buying position that would retain up to the end of the second line (after March 17). In this case, we would obtain incomparable results. The profit, gained in the second case, would result from the successful repeated opening a new order, not to the trailing stop mechanism. However, the problem is not to gain the largest profit, but to get as efficient trailing stop mechanism as possible. This why it is very important to us that all orders are opened at the same time and not retain for too long. However, in this case, the increase in profits resulted from optimization will reflect the efficiency of trailing algorithm!

Conclusion

I hope very much that the discussion about this present article will not be drowned in speaking ill of something that I have NOT done - an interested reader will find a use for the proposed research algorithm even as it is represented here. The article describes a tool I have finally got down to. The idea long time inhabited my mind finally became clear and was realized as an MQL4 code. I didn't have time to conduct the research, for which this tool had been created. This is why the article doesn't provide comparative analysis of various trailing algorithms - I'm going to deal with them in the nearest future, so the second part of the article will be ready for publishing soon. Nevertheless, this articles seems to be useful for MQL4 Community as it is.

One more reason why I decided to publish the "naked" tool is that I'm a professional programmer and a novice trader. This means that I can independently develop my MQL4 code to the highest possible simplicity or complexity, but I can only develop trading tactics and trailing algorithms as efficiently as program the code only together with traders who are as professional in trading as I am in programming. This is why I'm highly interested in construction communications with my colleagues Forex "guild fellows" and will be glad to work in cooperation on bringing my tool to a state when it is possible to use it in real trading. If you are interested in cooperation with me, please contact me through my profile.

As a further development, we can test several trailing algorithms on one chart simultaneously. At the beginning of the pattern section, we will open a few orders (to the amount of algorithms under test), each being trailed according to its algorithm. Conceptually, we should obtain the picture representing several images superimposed over each other. Then we will be able to compare the results both by quantity (net profits) and by quality - we will be able to see which algorithm fulfills its functions more precisely. However, this will require from us to teach our EA to distinguish orders by their trailing alternative, but this problem is quite solvable.

By the way, these methods can be used for comparing both entering and exiting tactics! For this, you should just prepare a bit different set of lines and make sure that your order opening points according to your methods are located as close to the pattern openings in the chart as possible. However, this is a subject of a separate research and of a new article.

Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/1527

Attached files |
TL_Trader.zip (7.01 KB)
Last comments | Go to discussion (1)
MQL4 Comments
MQL4 Comments | 2 Jul 2008 at 23:10

Nice effort. But, I couldn't understand, that how this pattern work will help me in my trading decisions.

Better if a step by step instruction can be provided, with a live example. Say I am trading EURUSD. How can I train my trailing algorithm using these EAs.

Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

False trigger protection for Trading Robot False trigger protection for Trading Robot

Profitability of trading systems is defined not only by logic and precision of analyzing the financial instrument dynamics, but also by the quality of the performance algorithm of this logic. False trigger is typical for low quality performance of the main logic of a trading robot. Ways of solving the specified problem are considered in this article.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.