Русский 中文 Español Deutsch 日本語 Português
A Pattern Trailing Stop and Exit the Market

A Pattern Trailing Stop and Exit the Market

MetaTrader 4Tester | 27 June 2008, 08:29
8 094 1
Sergey Kravchuk
Sergey Kravchuk

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 Ltd.
Original article: https://www.mql5.com/ru/articles/1527

Attached files |
TL_Trader.zip (7.01 KB)
Last comments | Go to discussion (1)
[Deleted] | 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.

Two-Stage Modification of Opened Positions Two-Stage Modification of Opened Positions
The two-stage approach allows you to avoid the unnecessary closing and re-opening of positions in situations close to the trend and in cases of possible occurrence of divirgence.
How to Become a Participant of Automated Trading Championship 2008? How to Become a Participant of Automated Trading Championship 2008?
The main purpose of the Championship is to popularize automated trading and accumulate practical information in this field of knowledge. As the Organizer of the Championship, we are doing our best to provide a fair competition and suppress all attempts to “play booty”. It is this reasoning that sets the strict Rules of the Championship.
Automated Trading Championship: The Reverse of the Medal Automated Trading Championship: The Reverse of the Medal
Automated Trading Championship based on online trading platform MetaTrader 4 is being conducted for the third time and accepted by many people as a matter-of-course yearly event being waited for with impatience. However, this competition specifies strict requirements to the Participants. This is precisely the topic we're going to discuss in this article.
Integrating MetaTrader 4  Client Terminal with MS SQL Server Integrating MetaTrader 4 Client Terminal with MS SQL Server
The article gives an example of integrating MetaTrader 4 Client Terminal with MS SQL Server using a dll. Attached are both source codes in С++ and in MQL4, and a ready-made and compiled Visual C++ 6.0 SP5 project.