MQL5 Cookbook: ОСО Orders

12 February 2015, 16:21
Denis Kirichenko
6 187


This article focuses on dealing with such type of order pair as OCO. This mechanism is implemented in some trading terminals competing with MetaTrader 5. I pursue two aims through the example of creation of an EA with a panel for OCO orders processing. On the one hand, I wish to describe features of the Standard Library, on the other hand I would like to extend a trader's tool set.

1. Essence of OCO Orders

OCO orders (one-cancels-the-other order) represent a pair of two pending orders.

They are connected by mutual cancellation function: if the first one triggers, the second one should be removed, and vice versa.

Fig. 1 Pair of OCO orders

Fig. 1 Pair of OCO orders

Fig.1 shows a simple order interdependence scheme. It reflects an essential definition: a pair exists for so long as both orders exist. In terms of logic any [one] order of the pair is an essential but not sufficient condition for the pair existence.

Some sources say that the pair must have one limit order and one stop order, moreover orders must have one direction (either buy or sell). To my mind such restriction cannot aid in creation of flexible trading strategies. I suggest that various OCO orders shall be analyzed in the pair, and most importantly we shall try to program this pair.

2. Programming Order Pair

In my thinking, ООP toolset is suited for programming tasks connected with control over OCO orders in the best possible way.

Following sections are devoted to new data types which will serve our purpose. CiOcoObject class comes first.

2.1. CiOcoObject Class

So, we need to come up with some software object responsible for control over two interconnected orders.

Traditionally, let's create a new object on the basis of CObject abstract class.

This new class can look as follows:

//| Class CiOcoObject                                                |
//| Purpose: a class for OCO orders                                  |            
class CiOcoObject : public CObject
   //--- === Data members === --- 
   //--- tickets of pair
   ulong             m_order_tickets[2];
   //--- initialization flag
   bool              m_is_init;
   //--- id
   uint              m_id;

   //--- === Methods === --- 
   //--- constructor/destructor
   void              CiOcoObject(void){m_is_init=false;};
   void             ~CiOcoObject(void){};
   //--- copy constructor
   void              CiOcoObject(const CiOcoObject &_src_oco);
   //--- assignment operator
   void              operator=(const CiOcoObject &_src_oco);

   //--- initialization/deinitialization
   bool              Init(const SOrderProperties &_orders[],const uint _bunch_cnt=1);
   bool              Deinit(void);
   //--- get id
   uint              Id(void) const {return m_id;};

   //--- types of orders
   ENUM_ORDER_TYPE   BaseOrderType(const ENUM_ORDER_TYPE _ord_type);
   //--- set id
   void              Id(const uint _id){m_id=_id;};

Each pair of OCO orders will have its own identifier. Its value is set by means of the generator of random numbers (object of CRandom class).

Methods of pair initialization and deinitialization are of concern in the context of interface. The first one creates (initializes) the pair, and the second one removes (deinitializes) it.

CiOcoObject::Init() method accepts array of structures of SOrderProperties type as argument. This type of structure represents properties of the order in the pair, i.e. OCO order.

2.2 SOrderProperties Structure

Let us consider fields of the aforementioned structure.

//| Order properties structure                                       |
struct SOrderProperties
   double                  volume;           // order volume   
   string                  symbol;           // symbol
   ENUM_PENDING_ORDER_TYPE order_type;       // order type   
   uint                    price_offset;     // offset for execution price, points
   uint                    limit_offset;     // offset for limit price, points
   uint                    sl;               // stop loss, points
   uint                    tp;               // take profit, points
   ENUM_ORDER_TYPE_TIME    type_time;        // expiration type
   datetime                expiration;       // expiration
   string                  comment;          // comment

So, to make the initialization method work we should previously fill the structures array consisting of two elements. In simple words, we need to explain the program which orders it will be placing.

Enumeration of ENUM_PENDING_ORDER_TYPE type is used in the structure:

//| Pending order type                                               |
   PENDING_ORDER_TYPE_BUY_LIMIT=2,       // Buy Limit
   PENDING_ORDER_TYPE_SELL_LIMIT=3,      // Sell Limit
   PENDING_ORDER_TYPE_BUY_STOP=4,        // Buy Stop
   PENDING_ORDER_TYPE_SELL_STOP=5,       // Sell Stop

Generally speaking, it looks the same as the ENUM _ORDER_TYPE standard enumeration, but it allows selecting only pending orders, or, more truly, types of such orders.

It protects from errors when selecting the corresponding order type in the Input parameters (Fig.2).

Fig. 2. The "Type" field with a drop-down list of available order types

Fig. 2. The "Type" field with a drop-down list of available order types

If however we use ENUM _ORDER_TYPE standard enumeration, then we could set a type of a market order (ORDER_TYPE_BUY or ORDER_TYPE_SELL), which is not required as we are dealing with pending orders only.

2.3. Initialization of Pair

As noted above, CiOcoObject::Init() method is engaged in order pair initialization.

In fact it places the order pair itself and records success or failure of new pair emergence. I should say that this is an active method, as it performs trading operations by itself. We can also create passive method as well. It will simply connect into a pair already active pending orders which have been placed independently.

I will not provide a code of the entire method. But I would like to note that it is important to calculate all prices (opening, stop, profit, limit), so the CTrade::OrderOpen() trade class method can perform a trade order. For that end we should consider two things: order direction (buy or sell) and position of an order execution price relative to a current price (above or under).

This method calls a couple of private methods: BaseOrderType() and PendingType(). The first one defines order direction, the second one determines pending order type.

If the order is placed, its ticket is recorded in the m_order_tickets[] array.

I used a simple Init_OCO.mq5 script to test this method.

#property script_show_inputs
#include "CiOcoObject.mqh"
//| Inputs                                                           |
sinput string Info_order1="+===--Order 1--====+";   // +===--Order 1--====+
input double InpOrder1Volume=0.02;                  // Volume
input uint InpOrder1PriceOffset=125;                // Offset for execution price, points
input uint InpOrder1LimitOffset=50;                 // Offset for limit price, points
input uint InpOrder1SL=250;                         // Stop loss, points
input uint InpOrder1TP=455;                         // Profit, points
input string InpOrder1Comment="OCO Order 1";        // Comment
sinput string Info_order2="+===--Order 2--====+";   // +===--Order 2--====+
input double InpOrder2Volume=0.04;                  // Volume    
input uint InpOrder2PriceOffset=125;                // Offset for execution price, points
input uint InpOrder2LimitOffset=50;                 // Offset for limit price, points
input uint InpOrder2SL=275;                         // Stop loss, points
input uint InpOrder2TP=300;                         // Profit, points
input string InpOrder2Comment="OCO Order 2";        // Comment

//--- globals
CiOcoObject myOco;
SOrderProperties gOrdersProps[2];
//| Script program start function                                    |
void OnStart()
//--- property of the 1st order

//--- property of the 2nd order

//--- initialization of pair
      PrintFormat("Id of new OCO pair: %I32u",myOco.Id());
      Print("Error when placing OCO pair!");

Here you can set various properties of future orders of the pair. MetaTrader 5 has six different types of pending orders.

With this context, there may be 15 variants (combinations) of pairs (provided that there are different orders in the pair).

C(k,N) = C(2,6) = 15

All variants have been tested with the aid of the script. I'll give an example for Buy Stop - Buy Stop Limit pair.

Types of orders should be specified in script parameters (Fig.3).

Fig. 3. Pair of "Buy Stop" order with "Buy Stop Limit" order

Fig. 3. Pair of "Buy Stop" order with "Buy Stop Limit" order

The following information will appear in the register "Experts":

QO      0       17:17:41.020    Init_OCO (GBPUSD.e,M15) Code of request result: 10009
JD      0       17:17:41.036    Init_OCO (GBPUSD.e,M15) New order ticket: 24190813
QL      0       17:17:41.286    Init_OCO (GBPUSD.e,M15) Code of request result: 10009
JH      0       17:17:41.286    Init_OCO (GBPUSD.e,M15) New order ticket: 24190814
MM      0       17:17:41.379    Init_OCO (GBPUSD.e,M15) Id of new OCO pair: 3782950319

But we cannot work with OCO orders to the utmost with the aid of the script without resorting to looping.

2.4. Deinitialization of Pair

This method is responsible for control over the order pair. The pair will "die" when any order leaves the list of active orders.

I suppose that this method should be placed in OnTrade() or OnTradeTransaction() handlers of the EA's code. In such a manner, the EA will be able to process activation of any pair order without delay.

//| Deinitialization of pair                                         |
bool CiOcoObject::Deinit(void)
//--- if pair is initialized
      //--- check your orders 
      for(int ord_idx=0;ord_idx<ArraySize(this.m_order_tickets);ord_idx++)
         //--- current pair order
         ulong curr_ord_ticket=this.m_order_tickets[ord_idx];
         //--- another pair order
         int other_ord_idx=!ord_idx;
         ulong other_ord_ticket=this.m_order_tickets[other_ord_idx];

         COrderInfo order_obj;

         //--- if there is no current order
            PrintFormat("Order #%d is not found in active orders list.",curr_ord_ticket);
            //--- attempt to delete another order                 
               CTrade trade_obj;
                  return true;
   return false;

I'd like to mention one detail. Pair initialization flag is checked in the body of the class method. Attempt to check orders will not be made if flag is cleared. This approach prevents deleting one active order when another one has not been placed yet.

Let's add functionality to the script where a couple of orders have been placed. For this purpose, we will create Control_OCO_EA.mq5 test EA.

Generally speaking the EA will differ from the script only by the Trade() event handling block in its code:

//| Trade function                                                   |
void OnTrade()
//--- OCO pair deinitialization
      Print("No more order pair!");
      //--- clear  pair
      CiOcoObject new_oco;

The video shows work of both programs in MetaTrader 5 terminal.

However both test programs have weaknesses.

The first program (script) can only actively create the pair but then it looses control over it.

The second program (Expert Advisor) though controls the pair, but it can't repeatedly create other pairs after creation of the first one. To make OCO order program (script) full-featured, we need to expand its toolset with the opportunity to place orders. We will do that in the next section.

3. Controlling EA

Let us create OCO Order Management Panel on the chart for placing and setting parameters of pair orders.

It will be a part of the controlling EA (Fig.4). The source code is located in Panel_OCO_EA.mq5.

Fig. 4. Panel for creation OCO orders: initial state

Fig. 4. Panel for creation OCO orders: initial state

We should select a type of a future order and fill out the fields to place the pair of OCO orders.

Then the label on the only button on the panel will be changed (text property, Fig.5).

Fig. 5. Panel for creation OCO orders: new pair

Fig. 5. Panel for creation OCO orders: new pair

The following classes of the Standard Library were used to construct our Panel:

  • CAppDialog is the main application dialog;
  • CPanel is a rectangle label;
  • CLabel is a text label;
  • CComboBox is a field with a drop-down list;
  • CEdit is an input field;
  • CButton is a button.

Of course, parent class methods were called automatically.

Now we get down to the code. It has to be said that the part of the Standard Library which has been dedicated for creation of indication panels and dialogs is quite large.

For instance, if you want to catch a drop-down list closing event, you will have to delve deep into the stack of calls (Fig. 6).

Fig. 6. Stack of Calls

Fig. 6. Stack of Calls

A developer sets macros and a notation in %MQL5\Include\Controls\Defines.mqh file for specific events.

I have created ON_OCO custom event to create the OCO pair.

#define ON_OCO (101) // OCO pair creation event 

Parameters of future orders are filled and the pair is generated in OnChartEvent() handler body. 

//| ChartEvent function                                              |
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
//--- handling all chart events by main dialog

//--- drop-down list handling
      //--- if it is Panel list
         static ENUM_PENDING_ORDER_TYPE prev_vals[2];
         //--- list index
         int combo_idx=(int)StringToInteger(StringSubstr(sparam,7,1))-1;

         ENUM_PENDING_ORDER_TYPE curr_val=(ENUM_PENDING_ORDER_TYPE)(myCombos[combo_idx].Value()+2);
         //--- remember order type change

//--- handling input fields
      //--- if it is Panel's input field
         //--- find object
         for(int idx=0;idx<ArraySize(myEdits);idx++)
            string curr_edit_obj_name=myEdits[idx].Name();
            long curr_edit_obj_id=myEdits[idx].Id();
            //--- if names coincide
               //--- get current value of field
               double value=StringToDouble(myEdits[idx].Text());
               //--- define gOrdersProps[] array index
               int order_num=(idx<gEditsHalfLen)?0:1;
               //--- define gOrdersProps structure field number
               int jdx=idx;
               //--- fill up gOrdersProps structure field
                  case 0: // volume
                  case 1: // execution
                  case 2: // limit
                  case 3: // stop
                  case 4: // profit
         //--- OCO pair creation flag
         bool is_to_fire_oco=true;
         //--- check structure filling 
         for(int idx=0;idx<ArraySize(gOrdersProps);idx++)
            //---  if order type is set 
               //---  if volume is set  
                  //---  if offset for execution price is set
                     //---  if offset for limit price is set
                        //---  if stop loss is set
                           //---  if take profit is set

            //--- clear OCO pair creation flag 
         //--- create OCO pair?
            //--- complete comment fields
            for(int ord_idx=0;ord_idx<ArraySize(gOrdersProps);ord_idx++)
               gOrdersProps[ord_idx].comment=StringFormat("OCO Order %d",ord_idx+1);
            //--- change button properties
            myButton.Text("New pair");
            //--- respond to user actions 
//--- handling click on button
      //--- if it is OCO pair creation button
         //--- if to respond to user actions
            //--- generate OCO pair creation event
            Print("Command to create new bunch has been received.");
//--- handling new pair initialization command 
      //--- OCO pair initialization
         PrintFormat("Id of new OCO pair: %I32u",gOco.Id());
         //--- copy pair
         CiOcoObject *ptr_new_oco=new CiOcoObject(gOco);
            //--- add to list
            int node_idx=gOcoList.Add(ptr_new_oco);
               PrintFormat("Total number of bunch: %d",gOcoList.Total());
               PrintFormat("Error when adding OCO pair %I32u to list!",gOco.Id());
         Print("OCO-orders placing error!");

      //--- clear properties

Handler code isn't small. I would like to lay emphasis on several blocks.

First handling of all chart events is given to the main dialog.

Next are blocks of various events handling:

  • Changing drop-down lists for defining an order type;
  • Editing input fields for filling up properties of orders;
  • Click on button for ON_OCO event generation;
  • ON_OCO event response: order pair creation.

The EA does not verify the correctness of filling the panel's fields. That is why we have to check values by ourselves, otherwise the EA will show OCO orders placing error.

Necessity to remove the pair and close the remaining order is checked in OnTrade() handler body.


I tried to demonstrate the riches of the Standard Library classes which can be used for fulfillment of some specific tasks.

Particularly, we were dealing with a problem of OCO orders handling. I hope that the code of the EA with Panel for OCO orders handling will be a starting point for creation of more complicated order pairs.

Translated from Russian by MetaQuotes Software Corp.
Original article:

Attached files |
ciocoobject.mqh (27.71 KB)
crandom.mqh (5.19 KB)
init_oco.mq5 (6.42 KB)
control_oco_ea.mq5 (7.88 KB)
panel_oco_ea.mq5 (30.25 KB)
Last comments | Go to discussion (7)
theone964 | 21 Dec 2016 at 15:40

I know this is an old thread but I really want to use this. I downloaded the zip file but I have no idea how to make this work., Some assistance on compiling this would be great.

Thank You

Simalb | 8 Nov 2017 at 20:21

Your article is intéressant.  Thanks to people like you beginners can progress and maybe someday start trading . Thanks

vijanda | 7 Feb 2019 at 21:27

I just downloaded all the zip files but i need help with the instraction on  how to make them work or install

Denis Kirichenko
Denis Kirichenko | 7 Feb 2019 at 22:37

I just downloaded all the zip files but i need help with the instraction on  how to make them work or install

You have to create a folder where all the relevant files will reside. After creation just copy the files into the folder.  For example:


After compilation you will find the expert file in the MT5 Navigator.


Much time has elapsed since the article publication. But the code runs fine. Build 1981.

vijanda | 8 Feb 2019 at 15:06
I followed all the instructions but the OCO Folder is not showing on my MT5 Navigator I even tried refreshing it
Building an Interactive Application to Display RSS Feeds in MetaTrader 5 Building an Interactive Application to Display RSS Feeds in MetaTrader 5

In this article we look at the possibility of creating an application for the display of RSS feeds. The article will show how aspects of the Standard Library can be used to create interactive programs for MetaTrader 5.

Trader's Statistical Cookbook: Hypotheses Trader's Statistical Cookbook: Hypotheses

This article considers hypothesis - one of the basic ideas of mathematical statistics. Various hypotheses are examined and verified through examples using methods of mathematical statistics. The actual data is generalized using nonparametric methods. The Statistica package and the ported ALGLIB MQL5 numerical analysis library are used for processing data.

Principles of Exchange Pricing through the Example of Moscow Exchange's Derivatives Market Principles of Exchange Pricing through the Example of Moscow Exchange's Derivatives Market

This article describes the theory of exchange pricing and clearing specifics of Moscow Exchange's Derivatives Market. This is a comprehensive article for beginners who want to get their first exchange experience on derivatives trading, as well as for experienced forex traders who are considering trading on a centralized exchange platform.

Bi-Directional Trading and Hedging of Positions in MetaTrader 5 Using the HedgeTerminal Panel, Part 1 Bi-Directional Trading and Hedging of Positions in MetaTrader 5 Using the HedgeTerminal Panel, Part 1

This article describes a new approach to hedging of positions and draws the line in the debates between users of MetaTrader 4 and MetaTrader 5 about this matter. The algorithms making such hedging reliable are described in layman's terms and illustrated with simple charts and diagrams. This article is dedicated to the new panel HedgeTerminal, which is essentially a fully featured trading terminal within MetaTrader 5. Using HedgeTerminal and the virtualization of the trade it offers, positions can be managed in the way similar to MetaTrader 4.