Three Aspects of Manual Trading Automation. Part 1: Trading

13 June 2013, 14:07
Sergey Kravchuk
7 487


Over the years of working at developments for the MetaTrader 4 trading platform, I have tried a lot of models and approaches to creating an automated workstation for a trader. The very first, most obvious solution was implemented in the set of trading scripts Mouse Only Trader which was generally quite successful. Having enhanced it with risk management calculations and money management functions, I came up with a quite functional tool called the Trading Mouse.

Billed by its developers primarily as a means of creating fully automated trading robots, the MetaTrader 4 trading terminal and its ergonomics appeared to be absolutely inadequate for people who need a comfortable work space. So I began to experiment with a graphical interface, while looking into the possibilities of using a mouse and keyboard directly on a trading chart. The outcome of the experiment was represented by two very fine-featured products: Trading Console and Buy/Sell.

Unfortunately, there was a reverse side to all their great features and user-friendliness - the Expert Advisors showed little response to rapid price changes and trade command execution. The terminal spent more time drawing the interface features, than trading. All attempts to actively trade using that seemingly user-friendly interface involved a fair amount of hassle (especially in pipsing and scalping).

Interface variants

Fig. 1. Interface variants.

Therefore, having reconsidered the requirements to the Expert Advisor in the light of these facts, I created a Scalper that could offer maximum efficiency at minimum eye appeal. Unfortunately, it still had old faults in the form of mouse command processing or having to use the sensor screen of a tablet. As a result, it turned out to be an entirely niche product mostly intended for working on tablets. I managed to finally get rid of virtually all dwims only in the Simple Trader that provided basis for the Expert Advisor that I am going to introduce in this article.

I have intentionally described the evolution of my products to help you understand that the implementation of everything set forth in this article was not derived from the fact that the author could not do otherwise. It is just that after a lot of trial and error, I have settled on the most simple and reliable solution that I would like to share with you.


A Picture is Worth 1,000 Words

Before we start, let's first have a look at the operation of the Expert Advisor that will be considered further below. The video will give you an idea of the processes that take place in the terminal during trading. As a result, the article will be much easier to understand (something like: "ah, so that is how it is implemented!").

For better comprehension of the video, it will be enough to understand that there is an Expert Advisor that operates in the terminal and is controlled by dropping control scripts onto the terminal window. The Expert Adviser itself does not trade. It can only pull stop levels (Stop Loss and Take Profit) after receiving a relevant command from a trader. Script names clearly correspond to their respective actions, while their use is explained in tooltips.

Now that we have a general idea of the subject of this article, let's look into it.


Basic Principles of Manual Trading Automation

First of all, I would like to say that I am too impatient to trade on daily, let alone weekly or monthly charts. I can have a position opened more than a day ago only in case of an absolutely stable and clearly strong trend. My working period is 5 and 15 minutes. There is too much noise on minute charts, whereas hourly charts kill my patience. Many of you will immediately think of two MAs, Alligator, Elder's screens, etc. - how can we do without higher time frames?! Don't we use them to identify short-term trends and market entry points? I do not use them in an explicit form. Instead, I do just fine with a combination of the zigzag and point and figure chart that I plot based on the current 5 and 15 minutes. However, this is not the point now. We are talking about intraday traders. What they do is virtually a classical scalping. One needs to very carefully monitor the current price and instantly respond to market impulses!

This intraday awareness allowed me to drastically simplify the requirements to the trading system. It uses only one order for each symbol! No delays: if you see that the price is going against you, close the position with the minimum loss and either open in the opposite direction, or wait for better conditions for an entry in the same direction. No locking or grids. If you are not sure in what direction you should open a position, simply don't open it! This statement may probably seem controversial but this is how I trade and this approach was followed in the Expert Advisor to be described in this article.

The decision to use one order per symbol has at a clap solved all problems related to interactive selection of a working order for purposes of modifying and closing. As a result, the code of the Expert Advisor has reduced several-fold, as did errors in the code. Among its automatic features are a quick opening/modifying/closing of an order and the implementation of the classical trading principle "let your profits run" in the form of an automatic execution of the Trailing Stop. However, in contrast to the standard Trailing Stop implemented in the terminal, the Trailing Stop featured in this Expert Advisor is fully controlled by the trader and can at any moment be easily modified according to rapidly changing market conditions.

But the main difference from the standard Trailing Stop is the possibility to set the trigger price. Right after opening, you can specify the price upon reaching which the Expert Advisor will begin to trail the accumulated profit. This will often be the price at which the Take Profit is set. The question arises: why cut profit at that level if it continues to grow further?! Since we cannot know that for a fact, we will begin to take profit at that moment, while letting it run using the built-in automatic Trailing Stop.


Well-Known Know-How

I do not think that I am breaking new ground by saying that the only way to manually execute a trade command is to run a certain script. Its actions are performed at the moment of dropping the script onto the chart. Unlike the script, the Expert Advisor has to wait for the next tick to trigger the start. But upon the new tick, the price may happen to be quite different from what you expected. It is probably not so important when trading on longer periods. But in aggressive intraday trading such delays are unacceptable.

In one window, however, you can run only one script at a time. As soon as you start running a new script, the previous script will stop working. The functionality of the script per se is admittedly much poorer than the capabilities of the Expert Advisor. So I decided to divide the operation of the Expert Advisor into two stages: trade command generation and execution. As a result, we have a set of scripts that do not perform any specific actions, but merely pass to the Expert Advisor the details of what it needs to do.

To unify this command mechanism, I decided not to include any meaningful trading actions in scripts. Each script is associated with a specific command (to open an order, modify the Stop Loss/Take Profit, close an order) that has a unique numeric index. So when dropping a script onto the chart we know what command we want to execute. The price and, if necessary, the time for command execution can be set using the dropping point coordinates. We just need to see how we can pass the data related to dropping the script on the chart to the Expert Advisor.

During the development of previous versions of automated traders, I have tried almost all adequate methods of program-to-program communication using operating system level software and Win32API:

  • writing/reading via disk files or via FileMapping in memory,
  • interaction using http protocols,
  • use of mailslots, etc.

They had cool features (e.g. mirror trading on several accounts in different terminals or remote control of an Expert Advisor operating on VPS) but, unfortunately, all of them were very laborious in terms of implementation and support and not worth the trouble. I managed to get rid of the unnecessary through experience, leaving the only right choice - the use of terminal global variables. The interaction of the script and the Expert Advisor in its final form boiled down to the following:

When dropped onto the chart, the script creates several global variables and simulates the new tick in the terminal so as to immediately get the Expert Advisor to execute the command by reading values from the global variables.


Benefits and Features of the Global Script Command Mechanism

The first clear advantage is the use of standard platform mechanisms that ensure a reliable interaction. The source code becomes significantly reduced and the code of the Expert Advisor gets rid of excess .ex4 and .dll libraries, making it easier to modify and maintain.

But the most remarkable feature of this mechanism is the possibility of proper operation in the Strategy Tester's visualization mode. It allows you to easily sharpen your trading skills and improve your trading tactics in the accelerated "market reproduction" mode! You can set any convenient speed of reproducing quotes and keep on practicing...Unlike a demo account that offers operation together with the market, at the live market speed, the visualizer is available at all times (even at weekends and when no Internet connection is available). You can pause it at any time (for a coffee break or in order to carry out a complex analysis of the current situation) and determine the effectiveness of your trading methods, having run through several months' history in just a couple of minutes using the reproduction speed control.

Equally important is the possibility to trade with this Expert Advisor using signals from virtually any indicator that cannot trade on its own. On a chart, such indicators as a rule simply display a certain graphical object, i.e. a signal to enter the market or close an order. Unfortunately, this is all they can do. It is generally impossible to trade or to run the indicator in the Strategy Tester. But if you have the source code of the indicator, you will only need to modify it by adding a block for creating the required global variables (which is just a couple of lines of the source code) and call it at the point where the indicator sets its signal. You will not even have to worry about repeating the signal at every tick! Since the Expert Advisor can only open one order per symbol, a repeated opening command will not make the Expert Advisor endlessly open new orders. With such a command indicator at hand, you will be able to easily test trading signals of the indicator in the Strategy Tester: just start testing the Expert Advisor, attach your indicator to the relevant chart and watch how the Expert Advisor trades based on the indicator signals.

But enough with the theoretical information. It is time to proceed to practical implementation of the idea.


Parameters of Control Commands

Each control script creates several global variables that are used to pass the following parameters to the Expert Advisor:

  • #Trader-Chart - handle of the window onto which the script is dropped. We need it to allow for the operation of several different Expert Advisors in one terminal (e.g. for different symbols or on different time frames). When being dropped onto a certain window, the script gets its handle. This command will be handled by the Expert Advisor that is attached to the same window. Thus, different Expert Advisors will only handle their corresponding commands, ignoring the rest of them. The handle is obtained using the WindowHandle() function.

  • #Trader-Command - command code. Unfortunately, the terminal allows us to store only numbers in global variables so they have to be assigned numerical codes instead of significant string values. To ensure synchronization between values of the Expert Advisor and scripts, we use the general include #Trader-commands.mqh file with the following definitions:

    #define   COMMAND_NOACTION  0
    #define   COMMAND_OPEN      1
    #define   COMMAND_MODIFY    2
    #define   COMMAND_CLOSE     3
    #define   COMMAND_TRALSTART 4
    #define   COMMAND_TRALSTOP  5

    If you need to add your own new command, you simply add its code "COMMAND_MYNEWCOMMAND" and assign it a unique code. This allows you to virtually infinitely scale the functionality of your version of the Expert Advisor.

  • #Trader-Price - price at the script dropping point. Normally, this is a new order opening price, Stop Loss or Take Profit modification price or automatic Trailing Stop trigger price.

  • #Trader-Time - trade command generation time. It is a really very important parameter. It will not allow the Expert Advisor to handle any old hung commands, for any reason remaining in the terminal. For this purpose, there is a special parameter in the Expert Advisor - ExecDelaySec. The Expert Advisor checks the current terminal time before executing a command and if the time stored in the script is greater than the current time by ExecDelaySec seconds, the command will not be executed and its details will simply be deleted.


Source Codes of Control Scripts

In view of the above, it appears that codes of all the scripts are almost identical, with the command code being the only difference. Therefore, the entire common part was arranged as a separate .mqh file to be included in all the control scripts.

It only provides for minimal control functions associated with trading availability. Everything else should be done in the terminal.

#property link      ""
#include <stderror.mqh>
#include <WinUser32.mqh>
#import "user32.dll"
 int RegisterWindowMessageA(string lpstring);
 int PostMessageA(int hWnd,int Msg,int wParam,int lParam);
void start()
  if ( !IsExpertEnabled() )   
  { MessageBox("Experts disabled. Action canceled.","SimpleTrader ERROR", MB_ICONERROR); return; }
  if ( !IsTradeAllowed() )    
  { MessageBox("Trade disabled. Action canceled.","SimpleTrader ERROR", MB_ICONERROR); return; }
  if ( IsTradeContextBusy() ) 
  { MessageBox("Trade context is busy. Action canceled.","SimpleTrader ERROR", MB_ICONERROR); return; }
  GlobalVariableDel ( "#Trader-Chart" );
  GlobalVariableDel ( "#Trader-Command" );
  GlobalVariableDel ( "#Trader-Price" );
  GlobalVariableDel ( "#Trader-Time" );
  // to allow several EAs handle scripts dropped onto THE RELEVANT windows only
  GlobalVariableSet ( "#Trader-Chart", WindowHandle( Symbol(), Period()) ); 
  GlobalVariableSet ( "#Trader-Command", COMMAND_ID );
  GlobalVariableSet ( "#Trader-Price", NormalizeDouble ( WindowPriceOnDropped(), Digits ) );
  GlobalVariableSet ( "#Trader-Time", TimeCurrent() );
  // emulation of the incoming tick to launch the Expert Advisor
  int hwnd = WindowHandle(Symbol(), Period());
  int MT4InternalMsg = RegisterWindowMessageA("MetaTrader4_Internal_Message");
  PostMessageA(hwnd, MT4InternalMsg, 2, 1);  

The script code itself consists of a few lines:

#property copyright "Open buy or sell according to the dropped price of the Stop Loss level"
#include  "#Trader-commands.mqh"
#include  "#Trader-common.mqh"  

#property copyright is used to generate the tooltip text in the terminal. While this text for simple commands may be obvious, your own commands, should you decide to add them to the Expert Advisor, may require a good description in addition to a meaningful name and that is the very place for it.

This line is followed by command codes from the general file. The general executable code provided a few paragraphs above comes next, after setting a command code for a given script.

This is where we stop working with the control scripts and proceed to the operation of the Expert Advisor.


Logic of the Expert Advisor for Manual Trading Automation

Initially, I wanted to present the operating algorithm of the Expert Advisor in the form of a flow chart. But it turned out to be so simple and straightforward that plain text will do.

Get the command –> Check and calculate the parameters –> Execute the command –> Display the results

The input parameters of the Expert Advisor are as minimalistic as the underlying logic. What is controlled be each one of them will be described later on, as we proceed to the explanation of the corresponding blocks of the Expert Advisor source code.

#property copyright "Copyright © 2006-2013, Sergey Kravchuk."
#property link      ""

#include <stdlib.mqh>
#include "scripts\#Trader-commands.mqh"

extern int    MaxAllowedRisk = 2// maximum permissible percentage of losses 
extern int    OpenSlippage   = 2// distance from the current price for placing an order to open a position
extern int    CloseSlippage  = 10; // maximum permissible slippage when closing an order
extern int    ExecDelaySec   = 10; // maximum permissible delay as of the START of command execution

extern int    ShowClosedCnt  = 5// number of closed market orders for the history display

extern int    TralStep       = 5// price change step in trailing
extern color  TralStartColor = BlueViolet;
extern color  Tral0LossStartColor = LimeGreen;
extern int    Tral0LossGap   = 10; // offset from the opening price to the breakeven point in trailing

// arrow colors for order opening and closing
extern color  MarkBuyColor    = Blue;
extern color  MarkSellColor   = Red;
extern color  MarkModifyColor = Magenta;
extern color  MarkCloseColor  = Gray;

extern int    MessageShowSec  = 30; // time of displaying the last message on the screen in seconds

extern bool   ShowComment     = true;
extern bool   WriteGadgetFile = true;

The Expert Advisor first gets the parameters of the terminal for current price calculations (depending on whether it works in the visualizer or a live account). If there is an open order on the working chart symbol, it is immediately selected by the OrderSelect() operator.

// get dynamic parameters
Spread      = MarketInfo ( Symbol(), MODE_SPREAD );
StopLevel   = MarketInfo ( Symbol(), MODE_STOPLEVEL );

// update prices (instead of Ask and Bid, the Strategy Tester uses Close[0])
if ( IsTesting() ) { CurBid = Close[0]; CurAsk = Close[0]+Spread*Point; } 
else { CurAsk = Ask; CurBid = Bid; }

// Select ANY open order (for possible modifications), including Limit orders 
// e.g. to be able to set Take Profit
SelectedOrder = 0; for ( i = 0; i < OrdersTotal() && SelectedOrder <= 0; i++ )
  OrderSelect ( i, SELECT_BY_POS, MODE_TRADES );
  if ( OrderSymbol() == Symbol() && OrderMagicNumber() == MAGIC ) SelectedOrder = OrderTicket();

In case there is an open order for the current symbol, we pull the Trailing Stop. The appropriateness and performance of this action are defined by the procedure that gets the only parameter - TralStep. If there is no open order, the trailing parameters are reset. This is necessary because the order may close upon triggering of the Stop level and the Expert Advisor, in contrast to manual closing, will not know that these parameters need to be reset.

//trailing trigger price exists as long as there is an open order!!!
if ( SelectedOrder <= 0 ) { ZeroLossTral = false; HalfProfitTral = false; PrevPrice = 0; TralStart = 0; } 
// trailing (checking and performing - within the TrailingStop)
else TrailingStop ( TralStep );

We further check whether the control command is intended for our window. If this is a command of another script, the Expert Advisor will simply display updated information on the account and open orders. If this is a relevant command, the Expert Advisor will read its parameters and immediately delete the command global variables (e.g. not to accidentally open multiple orders).

// if the script is intended for another window, only update the information.
if ( GlobalVariableGet ( "#Trader-Chart" ) != WindowHandle( Symbol(), Period()) ) { ShowInfo(); return; }
// if it is intended for the right window, execute the commands
// get the code and price of the command from the global variables and immediately delete them
if ( GlobalVariableCheck( "#Trader-Command" ) == false ) { CmdID = 0; CmdPrice = 0; ShowInfo(); return; } 
// there is a command, so we execute it
CmdTime  = GlobalVariableGet ( "#Trader-Time" );
CmdID    = GlobalVariableGet ( "#Trader-Command" );
CmdPrice = NormalizeDouble ( GlobalVariableGet ( "#Trader-Price" ), PriceDigits );
GlobalVariableDel ( "#Trader-Command" );
GlobalVariableDel ( "#Trader-Price" );
GlobalVariableDel ( "#Trader-Time" );
// if the command was generated earlier than the permissible execution delay, do not do anything!
if ( CmdTime+ExecDelaySec*60 < TimeCurrent() )
  SetError("Igore command from " + TimeToStr(CmdTime,TIME_DATE+TIME_SECONDS) 
         + " delayed > " + ExecDelaySec + "sec");

This is followed by the command execution and display of execution results. The source code of the Expert Advisor is provided along with comments, where necessary, and you should not face any difficulty reading and understanding it. However, there are a few points that require further clarification.

Please note:

  1. We cannot trade without Stop levels.

    An order is always opened with a preset Stop level. The lot size is selected taking into account the Stop level so that losses (if it triggers) do not exceed the preset percentage of the funds available for trading. This rule is also checked when modifying the Stop level. So you will not be able to open at a low risk and then set the Stop level in such a way that you will lose the rest of your deposit. This rule indirectly leads to another one.

  2. We only open Limit orders.

    Some dealing centers do not allow to open market orders with preset Stop Loss and Take Profit on the ground that an order may open with a slippage and it is (theoretically) possible that e.g. in a buy case your Stop level may turn out to be higher than the opening price. This situation is indeed very likely in a volatile market and a low Stop level. To avoid this restriction, we open a Limit order with a small offset from the current price (the above-mentioned slippage). It allows us to place an order exactly where it is required with the Stop level not exceeding the specified risk percentage. If the market moves in our direction, the order will open in just a couple of ticks. If the market is against us, we will have the opportunity to close the unused Limit order without losses or move it to a better spot.


Opening an Order

Having analyzed the current situation and selected the trading direction, decide on the position of the Stop level for the order to be opened and drop the #OPEN script onto that Stop Loss point. Its position is also used to determine the trading direction. A Stop Loss for a buy order should always be below the opening price and if, by dropping the script, you specified the Stop level price below the current price, it means that you are going to buy. Similarly, a Stop Loss above the current price will suggest the opening of a sell order. The only exception to this rule is a Stop Loss within the spread. As a rule, however, this will not be allowed by the dealing center trading policy whereby you cannot set a Stop Loss closer than it is specified in the StopLevel parameter.

Despite a lot of text describing the rules of opening, the respective code is only three lines long:

// get dynamic parameters
Spread      = MarketInfo ( Symbol(), MODE_SPREAD );
StopLevel   = MarketInfo ( Symbol(), MODE_STOPLEVEL );
// update prices (instead of Ask and Bid, the Strategy Tester uses Close[0])
if ( IsTesting() ) { CurBid = Close[0]; CurAsk = Close[0]+Spread*Point; } 
else { CurAsk = Ask; CurBid = Bid; }
// determine the trading direction
if ( CurBid <= CmdPrice && CmdPrice <= CurAsk ) 
{ SetError("Stop inside spread. Undefined trade direction."); ShowInfo(); return; }
if ( CmdPrice < CurBid ) 
  Operation = OP_BUYSTOP;  
  PriceForOrder = CurAsk+(StopLevel+OpenSlippage)*Point; 
  MarkOpenColor = MarkBuyColor; 
if ( CmdPrice > CurAsk ) 
  Operation = OP_SELLSTOP; 
  PriceForOrder = CurBid-(StopLevel+OpenSlippage)*Point; 
  MarkOpenColor = MarkSellColor; 

In opening an order, there is yet another mechanism allowing you to speed up the trading process - the possibility to "automatically" reverse a position. If you have a buy order open and see that the market is getting bearish, you will need to close the buy and open a sell order. You can have it all done by simply dropping the opening script. The Expert Advisor will analyze the situation and in case of a position reversal, it will first close the open order (provided that it was open) and then open the opposite one.

// if this is the opening in the opposite direction, we first close the current order (if it exists)
if ( SelectedOrder > 0 )
  if ( ( Operation == OP_BUYSTOP  && ( OrderType() == OP_BUY  || OrderType() == OP_BUYSTOP  ) ) ||
       ( Operation == OP_SELLSTOP && ( OrderType() == OP_SELL || OrderType() == OP_SELLSTOP ) ) )
    SetError("Only one order per symbol is allowed"); 
  // if orders are differently directed, close the previous one
    if ( OrderType() == OP_BUY || OrderType() == OP_SELL )
      // update with the current prices. in the Strategy Tester, Close[0] is used instead of Ask and Bid
      if ( IsTesting() ) { CurBid = Close[0]; CurAsk = Close[0]+Spread*Point; } 
      else { CurAsk = Ask; CurBid = Bid; }
      if ( OrderType() == OP_BUY ) PriceForOrder = CurBid; else PriceForOrder = CurAsk;
      OK = OrderClose ( OrderTicket(), OrderLots(), PriceForOrder, CloseSlippage, MarkCloseColor );
      OK = OrderDelete ( OrderTicket(), MarkCloseColor );      
      // clear lines and arrows of deleted Limit orders (not to overload the screen with information)
      for(i = ObjectsTotal()-1; i >= 0 ; i--) 
      if ( StringFind(ObjectName(i),"#"+SelectedOrder) == 0 ) ObjectDelete(ObjectName(i)); 
    if ( OK == false) { SetLastError("Close on reverse"); return; } // failed to delete - do not do anything else 


Money Management and Permissible Risk Percentage

After determining the trading direction, we need to determine the trading volume. The lot for an order to be opened is calculated by the Expert Advisor so that the permissible percentage of losses is not exceeded. The calculation algorithm is very simple: it determines the point value for a standard lot which is further used to calculate losses that will be incurred at the Stop level specified by the script. Further, following the pro rata rule, the lot size is decreased by as much as the value by which standard lot losses exceed the maximum permissible percentage of risk associated with the available funds.

// calculate the required lot based on the specified losses at the given Stop level
Loss = AccountBalance() * MaxAllowedRisk / 100.0; // amount of permissible losses expressed as a percentage of the balance
PriceInPoint = MathAbs ( PriceForOrder - CmdPrice ) / Point;
SL1lot = PriceInPoint * TickValue;   // Stop level size expressed in monetary value for a single lot transaction.
Lot = Loss / SL1lot;         // permissible risk / SL1lot
Lot = NormalizeDouble ( MathFloor ( Lot / LotStep ) * LotStep, LotDigits );

If the Stop level is set too high, whereas the risk percentage is low, the required lot size may appear to be less than the minimum permissible value. It can easily be checked, having noted the impossibility of opening and the maximum possible Stop Loss value.

if ( Lot < MinLot )
  OnePipCost = TickValue * MinLot; // recalculate the point value for the minimum possible lot
  SetError("Stoploss "+DoubleToStr(PriceInPoint,0)
          +" > max available "+DoubleToStr(MathAbs (Loss / OnePipCost),0));

If the lot exceeds the maximum permissible lot size (if you set the Stop Loss very close, while having specified a quite high percentage of risk), you will be given the relevant alert message:

if ( MaxLot < Lot )
  OnePipCost = TickValue * MaxLot; // recalculate the point value for the maximum possible lot
  SetError("Stoploss "+DoubleToStr(PriceInPoint,0)
          +" < min available "+DoubleToStr(MathAbs (Loss / OnePipCost),0));


Order Modification

If you need to set the Take Profit or move the Stop Loss to another price level, drop the #MODIFY script onto the appropriate price point in the chart. The Expert Advisor will figure out what exactly has to be modified (Stop Loss or Take Profit) and modify the order accordingly. Here, we use the same technique as when opening an order: if the modification price is less than the current price for a buy, it means the modification of the Stop Loss. Higher modification price is associated with the Take Profit modification.

// determine what needs to be modified
if ( ( (OrderType() == OP_BUY  || OrderType() == OP_BUYSTOP)  && CmdPrice >= CurAsk ) ||
     ( (OrderType() == OP_SELL || OrderType() == OP_SELLSTOP) && CmdPrice <= CurBid ) ) TP = CmdPrice;
else // modify the Stop Loss
  SL = CmdPrice;

Since losses are strictly controlled, you will not be able to set the Stop Loss higher than you did when opening the order, and unreasonable risks will thus be avoided.

  // Stop Loss and risk percentage control! 
  Loss = AccountBalance() * MaxAllowedRisk / 100.0; // losses set as a percentage of the balance
  OnePipCost = TickValue * OrderLots(); // recalculate the point value for the order lot
  if ( OrderType() == OP_BUY  ) NewLoss = ( OrderOpenPrice() - SL ) / Point * OnePipCost;
  if ( OrderType() == OP_SELL ) NewLoss = ( SL - OrderOpenPrice() ) / Point * OnePipCost;
  if ( NewLoss > Loss ) 
    SetError("Stoploss "+DoubleToStr(NewLoss/OnePipCost,0)
            +" > max available "+DoubleToStr(Loss/OnePipCost,0)); 


Closing an Order

When you decide to close the current order, drop the #CLOSE script onto any price point in the chart as it is of no importance in closing. Following this action the order will be closed.


Trailing Stop

Just like the standard Trailing Stop mechanism, the Trailing Stop algorithm built in the Expert Advisor also trails the price at a distance you specify (if the price goes in the "right" direction). But unlike the standard mechanism, it has a more flexible operation control.

First of all, it allows you to set the price at which you want to start trailing. You can do it right after opening an order by dropping the #TRAL-START script onto the price at which trailing needs to be activated. The Expert Advisor will "remember" that value and as soon as the price breaks through the corresponding level, the Trailing Stop mechanism will kick in, with the Expert Advisor pulling the Trailing Stop after the moving price. To avoid modifying the order too often, the Expert Advisor has a Trailing Stop step discreteness parameter - TralStep. A new Trailing Stop will only be set if the price moves the distance of at least TralStep points in the "right" direction.

The second difference from the standard Trailing Stop is that you can set the Trailing Stop size at the same time as setting its initial point. The #TRAL-START0LOSS script will indicate the initial trailing point and ones it triggers it will automatically move the Stop Loss to the breakeven point at the distance of Tral0LossGap points from the order opening price. Another modification of the same script, #TRAL-START50PROFIT, will move the Stop level at the beginning of the trailing process to the middle line between the trailing trigger price and the order opening price which will automatically save at least 50% of the accumulated profit at the trailing start.

Trailing Stop setting commands pass the parameters of the future Trailing Stop to the Expert Advisor.


// set the trailing trigger price. 
// before the triggering TralStart < 0 as a sign that it is still inactive
if ( CmdID == COMMAND_TRALSTART && CmdPrice > 0) 
  ZeroLossTral = false; HalfProfitTral = false; TralStart = CmdPrice; PrevPrice = 0; ShowInfo(); 
// set the trailing trigger price. and simultaneously move the Stop level to the breakeven point
if ( CmdID == COMMAND_TRALSTART0LOSS && CmdPrice > 0) 
  ZeroLossTral = true; HalfProfitTral = false; TralStart = CmdPrice; PrevPrice = 0; ShowInfo(); 
// set the trailing trigger price. and simultaneously move the Stop level to 50% of the earned profit
if ( CmdID == COMMAND_TRALSTART50PROFIT && CmdPrice > 0) 
  ZeroLossTral = false; HalfProfitTral = true; TralStart = CmdPrice; PrevPrice = 0; ShowInfo(); 
// zero out, which means that trailing stops
  ZeroLossTral = false; HalfProfitTral = false; PrevPrice = 0; TralStart = 0; ShowInfo(); 

This mechanism will allow you to reduce the intraday trading stress and can guarantee trading which is at least loss-free if the price reaches the trailing trigger point. Since all of the above will be done by the Expert Advisor automatically, you will not have to constantly monitor current prices, waiting for the Trailing Stop to kick in.

During the operation of the Expert Advisor in the trailing mode, the position of the Trailing Stop can at any time be modified by a standard modification command using the #MODIFY script. After moving the Stop level to another level, the active Trailing Stop mechanism will further keep that distance. Since everything is done in the chart and can visually be addressed, it is much easier and more convenient than the standard trailing mechanism that requires values in points. Just as in modifying the standard Stop Loss, the Trailing Stop also provides for control of permissible losses that will not allow you to lose more than specified:

void TrailingStop(int Step)
  double OnePipCost, NewLoss, SL=0;
  if ( OrderSelect ( SelectedOrder, SELECT_BY_TICKET ) == false ) return; 
  if ( OrderCloseTime() > 0 ) return; // the order has already been closed - there is nothing to trail
  // check if the data is valid
  if ( TralStart <= 0 || Step < 1 ) return(-1); 

  // Get the data for the Stop level size and risk percentage control!  
  Loss = AccountBalance() * MaxAllowedRisk / 100.0; // losses are set as a percentage of the balance
  OnePipCost = TickValue * OrderLots(); // recalculate the point value for the order lot

  if ( OrderType() == OP_BUY && CurBid >= TralStart ) 
    if ( PrevPrice <= 0 ) 
      if ( ZeroLossTral   ) SL = NormalizeDouble(OrderOpenPrice() + Tral0LossGap*Point, Digits); 
      if ( HalfProfitTral ) SL = NormalizeDouble(OrderOpenPrice() + (CurBid - OrderOpenPrice())/2.0, Digits); 
      else                  SL = NormalizeDouble(OrderStopLoss(), Digits);
    else SL = NormalizeDouble(OrderStopLoss() + (CurBid - PrevPrice), Digits);
    if ( SL < OrderStopLoss() ) return;
    NewLoss = ( OrderOpenPrice() - SL ) / Point * OnePipCost;
  if ( OrderType() == OP_SELL && CurAsk <= TralStart )  
    if ( PrevPrice <= 0 ) 
      if ( ZeroLossTral   ) SL = NormalizeDouble(OrderOpenPrice() - Tral0LossGap*Point, Digits); 
      if ( HalfProfitTral ) SL = NormalizeDouble(OrderOpenPrice() - (OrderOpenPrice() - CurAsk)/2.0, Digits); 
      else                  SL = NormalizeDouble(OrderStopLoss(), Digits);
    else SL = NormalizeDouble(OrderStopLoss() - (PrevPrice - CurAsk), Digits);
    if ( SL > OrderStopLoss() ) return;
    NewLoss = ( SL - OrderOpenPrice() ) / Point * OnePipCost;
  if ( SL <= 0 ) return; // the price has not yet crossed the trailing trigger level
  if ( NewLoss > Loss ) 
    SetError("Trailing Stoploss "+DoubleToStr(NewLoss/OnePipCost,0)
            +" > max available "+DoubleToStr(Loss/OnePipCost,0)); 


  if ( ( OrderType() == OP_BUY && SL - OrderStopLoss() >= Step*Point ) || 
       ( OrderType() == OP_SELL && OrderStopLoss() - SL >= Step*Point ) )
    TXT = "• Tralingstop order. Please wait..."; 
    bool OK = OrderModify(OrderTicket(),OrderOpenPrice(),SL,OrderTakeProfit(),OrderExpiration(),Blue);
    if ( OK ) SetWarning("Traling stop moved to " + DoubleToStr(SL, Digits) 
                        +" at " + TimeToStr(TimeLocal(),TIME_SECONDS)); 
    else SetLastError("Tralingstop");
  if ( PrevPrice <= 0 || OK )
    // only if we have moved the Trailing Stop, store the prices for the Trailing Stop on the next tick
    if ( OrderType() == OP_BUY  ) PrevPrice = CurBid;
    if ( OrderType() == OP_SELL ) PrevPrice = CurAsk;


Displaying Information on the Current Situation

Within the scope of this article, we have only considered the issues related to trading as such. In the course of operation of the Expert Advisor, the code provided in this article simply displays a short comment containing data on the current status of orders and the trading account. It all could certainly be done in a finer and more informative way. As a matter of fact, this is how I did it. My working version of the system outputs very detailed visual information regarding its operation to the standard widget window of Windows 7 operating system. This will be the focus of my second article as I intend to talk about things that are not directly related to trading. But their use makes the process of trade monitoring more convenient. Assume, you have opened an order, calculated the Trailing Stop position and are now simply waiting for it to trigger. While waiting, you do not need to keep your terminal window open. A small widget window that always stays on top will steadily monitor the current situation, while you can do other things, checking it every now and then.

But even without the widget, the Expert Advisor is fully-functional and ready for real work. Whatever is lacking, can easily be added to the code for displaying the current information.

The only thing that is worthwhile noting when speaking of this block of the Expert Advisor is the mechanism underlying the display of error messages and normal operation of the Expert Advisor. Every exception case and every error message is stored in the special string variable - LastMsgTxt. This text is displayed on the screen for up to MessageShowSec seconds set in the indicator parameters.


Trading Based on External Signals

The Expert Advisor trades based on external commands. And the source of these commands is of absolutely no importance to the Expert Advisor. All of the above can be applied to the case where the Expert Advisor is controlled by a person. But global variables can be set in the terminal not only by a person. It can be done by an indicator attached to the same window. Let's have a look at how this can be implemented using the standard RSI indicator as an example.

We will modify the source code of the indicator calculation by adding an "analytical block" controlling the Expert Advisor, at the end of the code, after all indicator buffer data is calculated. This example implements the following order opening rules: a new order opens if the RSI crosses level 52 in the downward direction or level 48 in the upward direction.

It will show that we do not need to worry about having to forcibly close orders due to the built-in blocking mechanism that prevents multiple openings and automatic closing of orders in case of position reversal. The automatic trailing mechanism with respect to 50% of profit kicks in at the intersection of level 45 and 55. This system can certainly not be called profitable. It is used here for demonstration purposes only, to show what and how needs to be done in the code of the indicator to train it to control the operation of the Expert Advisor.

// addition to #Trader ======================================================================
double DefaultStop = 150 * Point; // shifting the Stop level when opening an order

// the Buy condition
if( RSIBuffer[3] <= 50 && RSIBuffer[2] <= 52 && RSIBuffer[1] > 52 )
   GlobalVariableSet ( "#Trader-Chart", WindowHandle( Symbol(), Period()) ); 
   GlobalVariableSet ( "#Trader-Command", 1 );
   GlobalVariableSet ( "#Trader-Price", NormalizeDouble ( Close[0] - DefaultStop, Digits ) );
   GlobalVariableSet ( "#Trader-Time", TimeCurrent() );

// the Sell condition
if( RSIBuffer[3] >= 50 && RSIBuffer[2] >= 48 && RSIBuffer[1] < 48 ) 
   GlobalVariableSet ( "#Trader-Chart", WindowHandle( Symbol(), Period()) ); 
   GlobalVariableSet ( "#Trader-Command", 1 );
   GlobalVariableSet ( "#Trader-Price", NormalizeDouble ( Close[0] + DefaultStop, Digits ) );
   GlobalVariableSet ( "#Trader-Time", TimeCurrent() );

// 50% trailing start
if( ( RSIBuffer[2] >= 45 && RSIBuffer[1] < 45 ) || ( RSIBuffer[2] <= 55 && RSIBuffer[1] > 55 ) ) 
   GlobalVariableSet ( "#Trader-Chart", WindowHandle( Symbol(), Period()) );
   GlobalVariableSet ( "#Trader-Command", 7 );
   GlobalVariableSet ( "#Trader-Price", NormalizeDouble ( Close[0], Digits ) );
   GlobalVariableSet ( "#Trader-Time", TimeCurrent() );
// addition to #Trader ======================================================================

Important note: such indicators can only be tested in the visualization mode and cannot be optimized.

Another interesting possibility that opens up when trading based on external signals is the possibility of implementing mirror trading in several terminals on different trading accounts. Issues of cross-program interaction lie beyond the scope of this series of articles. However, the idea behind such implementation is quite obvious: upon handling the command or generating it in the script, one of the terminals that is currently involved in trading should pass the obtained parameters to another terminal where they will be received by another Expert Advisor or indicator. The same control global variables will then be created in another terminal and the corresponding Expert Advisor from the second terminal will execute them the same way they are executed by the Expert Advisor from the first terminal.


Let's now summarize the key points implemented in the proposed manual trading automation approach:

  • Command and execution components of the trading system are divided into two parts: control scripts give commands and set their parameters based on which the Expert Advisor operates. It allows to easily expand the functionality by adding new features. To do this, we need to add a new operation code, a script that will pass this code to the Expert Advisor and arrange the appropriate handling in the Expert Advisor.
  • One and the same command is used for both setting the parameters for order handling and determining the action type to be performed. The Stop level price at the opening defines the trading direction and in cases where modification is required, determines whether it should be applied to the Stop Loss or Take Profit.
  • The mechanisms responsible for opening and modifying orders, as well as money management procedures built in the Expert Advisor will keep you from losing more than the specified initial value of the risk expressed as a percentage of the available balance.
  • Instead of the standard Take Profit mechanism that cuts profit, the Expert Advisor employs the algorithm "letting your profits run", whereby instead of closing a winning order at the Take Profit level, it trails growing profits.
  • The use of standard terminal global variables for the exchange of data between scripts and the Expert Advisor allows you to easily use one and the same code for working on live accounts, as well as when practicing in the visualization mode of the Strategy Tester.
  • Trade commands can be passed to the Expert Advisor not only by the command script but also by any indicator whose source code can be modified by adding a block for creating command global variables at the points of indicator signal generation. Trading based on such indicator signals can even be tested in the standard tester of the terminal. This can be very useful in identifying quasi-profitable redrawing indicators.


The attached archive contains the source codes of the #Trader Expert Advisor, along with all control scripts and the RSI indicator example featuring the built-in mechanism responsible for passing control commands to the Expert Advisor.

Translated from Russian by MetaQuotes Software Corp.
Original article:

Attached files | (40.86 KB)
Last comments | Go to discussion (2)
benzlnw | 17 Jun 2013 at 23:03
Thank you . for Know How ^_^     Someday I will be like same you.
MQL4 Comments
MQL4 Comments | 11 Nov 2014 at 03:40

Hi  Сергей,

Thank you for sharing. Much appreciated.

The Random Sandbox The Random Sandbox

The article includes an interactive "sandbox" as an Excel file which simulates randomized Expert Advisor backtest data. Readers can use this to help explore and more deeply understand the EA performance metrics offered by default with MetaTrader. The text of the article is designed to guide the user through this experience.

MetaTrader 4 Expert Advisor exchanges information with the outside world MetaTrader 4 Expert Advisor exchanges information with the outside world

A simple, universal and reliable solution of information exchange between МetaТrader 4 Expert Advisor and the outside world. Suppliers and consumers of the information can be located on different computers, the connection is performed through the global IP addresses.

Mechanical Trading System "Chuvashov's Triangle" Mechanical Trading System "Chuvashov's Triangle"

Let me offer you an overview and the program code of the mechanical trading system based on ideas of Stanislav Chuvashov. Triangle's construction is based on the intersection of two trend lines built by the upper and lower fractals.

LibMatrix: Library of Matrix Algebra (Part One) LibMatrix: Library of Matrix Algebra (Part One)

The author familiarizes the readers with a simple library of matrix algebra and provides descriptions and peculiarities of the main functions.