Das MQL5-Kochbuch: ОСО-Orders

27 Juni 2016, 12:25
Denis Kirichenko
0
441

Einleitung

Dieser Beitrag beschäftigt sich mit dem Umgang einem derartigen Order-Paar wie OCO. Dieser Mechanismus ist in einigen Handelsterminals implementiert, die mit MetaTrader 5, im Wettbewerb stehen. Anhand des Beispiels der Erzeugung eines EAs mit einem Panel zur Verarbeitung von OCO-Orders verfolge ich zwei Ziele: ich möchte einerseits die Merkmale der Standard Library beschreiben, und andererseits das Toolset eines Händlers erweitern.


1. Das Wesentliche der OCO-Orders

OCO-Order (eine Order storniert die andere) stellen ein Paar zweier pending Orders dar.

Sie sind mittels einer gegenseitigen Stornierungsfunktion aneinander geknüpft - wird die erste ausgelöst, bleibt die zweite davon unberührt und umgekehrt.

Abb. 1 Paar an OCO-Orders

Abb. 1 Paar an OCO-Orders

Abb. 1 zeigt ein einfaches Schema der gegenseitigen Abhängigkeit von Orders. Damit wird eine grundlegende Definition klar: ein Paar existiert nur so lange, wie es beide Orders gibt. Unter logischen Gesichtspunkten ist jede [eine] Order des Paares eine wesentliche, aber nicht ausreichende Bedingung für das Vorhandensein des Paares.

Einige Quellen führen an, dass das Paar eine Limit-Order und eine Stop-Order haben muss, und dass Orders darüber hinaus eine Richtung besitzen müssen (entweder Kaufen oder Verkaufen). Meiner Ansicht nach helfen solche Einschränkungen bei der Erzeugung flexibler Handelsstrategien nicht. Ich schlage daher vor, dass unterschiedliche OCO-Orders im Paar analysiert werden, und dass wir, und das ist am wichtigsten, dieses Paar zu programmieren versuchen.


2. Programmierung eines Order-Paares

Meiner Meinung nach ist das Objekt-orientierte Programmierungs-Toolset für Programmierungsaufgaben in Zusammenhang mit der Kontrolle über OCO-Orders die bestmögliche Alternative.

Die folgenden Abschnitte beschäftigen sich mit neuen Datentypen, die unserem Ziel hier dienen. Die CiOcoObject-Klasse kommt zuerst.


2.1 CiOcoObject-Klasse

Also müssen wir irgendein Software-Objekt finden, das für die Kontrolle über zwei miteinander verbundene Orders zuständig ist.

Herkömmlicherweise erzeugen wir dazu ein neues Objekt auf Basis der abstrakten CObject-Klasse.

Diese neue Klasse kann folgendermaßen aussehen:

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

   //--- === Methods === --- 
public:
   //--- 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;};

private:
   //--- types of orders
   ENUM_ORDER_TYPE   BaseOrderType(const ENUM_ORDER_TYPE _ord_type);
   ENUM_BASE_PENDING_TYPE PendingType(const ENUM_PENDING_ORDER_TYPE _pend_type);
   //--- set id
   void              Id(const uint _id){m_id=_id;};
  };

Jedes Paar an OCO-Orders besitzt seinen eigenen Identifikator und sein Wert wird mit Hilfe des Generators von zufälligen Zahlen festgesetzt (Objekt der CRandom-Klasse).

Im Kontext einer Schnittstelle sind die Methoden der Initialisierung und De-Initialisierung des Paares ein Problem. Die erste erzeugt (initialisiert) das Paar und die zweite entfernt (de-initialisiert) es.

Die CiOcoObject::Init()-Methode akzeptiert Strukturen-Arrays vom Typ SOrderProperties als Begründung. Dieser Strukturtyp gibt die Eigenschaften der Order im Paar wieder - also eine OCO-Order.


2.2 SOrderProperties-Struktur

Betrachten wir uns nun die Felder der gerade angesprochenen Struktur.

//+------------------------------------------------------------------+
//| 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
  }

Damit die Initialisierungsmethode funktioniert, sollten wir das Strukturen-Array, das aus zwei Elementen besteht, zuvor füllen. Einfacher gesagt: Wir müssen dem Programm erklären, welche Orders es platzieren wird.

Die Aufzählung vom Typ ENUM_PENDING_ORDER_TYPE wird in der Struktur verwendet:

//+------------------------------------------------------------------+
//| Pending order type                                               |
//+------------------------------------------------------------------+
enum ENUM_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
   PENDING_ORDER_TYPE_BUY_STOP_LIMIT=6,  // Buy Stop Limit
   PENDING_ORDER_TYPE_SELL_STOP_LIMIT=7, // Sell Stop Limit
  };

Generell gesagt, sieht sie genauso aus wie die ENUM _ORDER_TYPE Standard-Aufzählung, erlaubt aber, nur pending Orders auszuwählen, oder präziser - Typen solcher Orders.

Sie schützt bei der Auswahl des entsprechenden Ordertyps in den Eingabeparametern vor Fehlern (Abb. 2).

Abb. 2 Das "Typ"-Feld mit einer Dropdown-Liste der verfügbaren Order-Typen

Abb. 2 Das "Typ"-Feld mit einer Dropdown-Liste der verfügbaren Order-Typen

Wenn wir allerdings die ENUM _ORDER_TYPE Standard-Aufzählung verwenden, könnten wir einen Typ einer Marktorder einrichten (ORDER_TYPE_BUY oder ORDER_TYPE_SELL), der nicht nötig ist, das wir uns ja nur mit pending Orders beschäftigen.


2.3 Initialisierung eines Paares

Wie oben erwähnt, beschäftigt sich die CiOcoObject::Init()-Methode mit der Initialisierung eines Order-Paares und

platziert in der Tat das Order-Paar selbst und vermerkt den Erfolg oder Misserfolg des Auftauchens eines neuen Paares. Hier muss ich darauf hinweisen, dass dies eine aktive Methode ist und Handelsabläufe selbst ausführt. Wir können aber auch eine passive Methode erzeugen. Dazu muss man nur ein Paar bereits aktiver pending Orders miteinander verbinden, die unabhängig voneinander platziert wurden.

Den Code für die gesamte Methode spare ich mir, doch möchte ich anmerken, dass es wichtig ist, alle Kurse zu berechnen (Eröffnung, Stop, Profit, Limit), damit die CTrade::OrderOpen() Handelsklassen-Methode eine Handelsorder ausführen kann. Zu diesem Zweck sollten man sich zwei Dinge überlegen: 1) Richtung der Order (Kaufen oder Verkaufen) und 2) Position des Ausführungskurses einer Order in Relation zum aktuellen Kurs (darüber oder darunter).

Diese Methode ruft einige private Methoden auf: BaseOrderType() und PendingType(). Die erste definiert die Richtung der Order; die zweite legt den Typ der pending Order fest.

Wird die Order platziert, wird ihr Ticket im Array m_order_tickets[] aufgezeichnet.

Um diese Methode zu testen, habe ich ein einfaches Init_OCO.mq5 Script verwendet.

#property script_show_inputs
//---
#include "CiOcoObject.mqh"
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
sinput string Info_order1="+===--Order 1--====+";   // +===--Order 1--====+
input ENUM_PENDING_ORDER_TYPE InpOrder1Type=PENDING_ORDER_TYPE_SELL_LIMIT; // Type
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 ENUM_PENDING_ORDER_TYPE InpOrder2Type=PENDING_ORDER_TYPE_SELL_STOP; // Type
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
   gOrdersProps[0].order_type=InpOrder1Type;
   gOrdersProps[0].volume=InpOrder1Volume;
   gOrdersProps[0].price_offset=InpOrder1PriceOffset;
   gOrdersProps[0].limit_offset=InpOrder1LimitOffset;
   gOrdersProps[0].sl=InpOrder1SL;
   gOrdersProps[0].tp=InpOrder1TP;
   gOrdersProps[0].comment=InpOrder1Comment;

//--- property of the 2nd order
   gOrdersProps[1].order_type=InpOrder2Type;
   gOrdersProps[1].volume=InpOrder2Volume;
   gOrdersProps[1].price_offset=InpOrder2PriceOffset;
   gOrdersProps[1].limit_offset=InpOrder2LimitOffset;
   gOrdersProps[1].sl=InpOrder2SL;
   gOrdersProps[1].tp=InpOrder2TP;
   gOrdersProps[1].comment=InpOrder2Comment;

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

Hier kann man die verschiedenen Eigenschaften zukünftiger Orders des Paares einrichten. MetaTrader 5 verfügt über sechs verschiedene Typen von pending Orders.

In diesem Kontext kann es also 15 Varianten (Kombinationen) von Paaren geben (vorausgesetzt, die Orders in dem Paar sind auch unterschiedlich).

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

Alle Varianten wurden mit Hilfe des Scripts getestet. Ich zeige Ihnen hier ein Beispiel für das Buy Stop - Buy Stop Limit Paar.

Die Order-Typen sollten in den Script-Parametern spezifiziert werden (Abb. 3).


Abb. 3 Paar einer "Buy Stop" Order mit "Buy Stop Limit" Order

Abb. 3. Paar einer "Buy Stop" Order mit "Buy Stop Limit" Order

Im "Experts" Register erscheint die folgende Information:

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

Doch wir können mit Hilfe des Scripts mit OCO-Orders nicht optimal und maximal arbeiten, ohne auf Schleifen zurückgreifen zu müssen.


2.4 De-Initialisierung eines Paares

Diese Methode ist für die Kontrolle über das Order-Paar verantwortlich. Sobald irgendeine Order die Liste der aktiven Orders verlässt, wird dieses Paar zu existieren aufhören.

Ich nehme an, dass diese Methode in die OnTrade() oder OnTradeTransaction() Handler des EA-Codes platziert werden sollte. Denn nur so kann der EA die Aktivierung jeder Paar-Order ohne Zeitverzögerung verarbeiten.

//+------------------------------------------------------------------+
//| Deinitialization of pair                                         |
//+------------------------------------------------------------------+
bool CiOcoObject::Deinit(void)
  {
//--- if pair is initialized
   if(this.m_is_init)
     {
      //--- 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
         if(!order_obj.Select(curr_ord_ticket))
           {
            PrintFormat("Order #%d is not found in active orders list.",curr_ord_ticket);
            //--- attempt to delete another order                 
            if(order_obj.Select(other_ord_ticket))
              {
               CTrade trade_obj;
               //---
               if(trade_obj.OrderDelete(other_ord_ticket))
                  return true;
              }
           }
        }
     }
//---
   return false;
  }

Lassen Sie mich hier auf ein Detail hinweisen: Die Flagge für Paar-Initialisierung wird im Korpus der Klassenmethode angekreuzt. Wird diese Flagge wieder "geleert", findet kein Versuch mehr statt, die Orders zu prüfen. Mit diesem Ansatz wird das Löschen einer aktiven Order vermieden, wenn die andere Order noch gar nicht platziert worden ist.

Fügen wir dem Script, wo einige Orders platziert wurden, nun Funktionalität hinzu. Dazu erzeugen wir den Control_OCO_EA.mq5 Test-EA.

Generell gesagt, unterscheidet sich dieser EA vom Script nur durch den Trade() Block in seinem Code zum Umgang mit Ereignissen:

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- OCO pair deinitialization
   if(myOco.Deinit())
     {
      Print("No more order pair!");
      //--- clear  pair
      CiOcoObject new_oco;
      myOco=new_oco;
     }
  }

Das Video zeigt die Arbeit beider Programme im MetaTrader 5 Terminal.



Beide Testprogramme haben jedoch auch ihre Schwächen.

Das erste Programm (Script) kann das Paar nur aktiv erzeugen, verliert jedoch dann Kontrolle darüber.

Das zweite Programm (Expert Advisor) kontrolliert zwar das Paar, kann jedoch keine weiteren anderen Paare nach Erzeugung des ersten anlegen. Damit ein OCO-Orderprogramm (Script) voll funktionsfähig wird, müssen wir sein Toolset um die Möglichkeit der Platzierung von Orders erweitern. Darum geht es im nächsten Abschnitt.


3 Der kontrollierende EA

Erzeugen wir also ein Panel zur OCO-Verwaltung auf dem Chart und zwar zur Platzierung und Einrichtung von Parametern von Paar-Orders.

Dieses Panel wird Teil des kontrollierenden EAs (Abb. 4). Der Quellcode befindet sich in Panel_OCO_EA.mq5.

Abb. 4 Panel zur Erzeugung einer OCO-Order: Ursprungsstadium

Abb. 4 Panel zur Erzeugung einer OCO-Order: Ursprungsstadium


Wir sollten einen Typ einer zukünftigen Order wählen und alle entsprechenden Felder zur Platzierung des Paares an OCO-Orders ausfüllen

Dann wird die Bezeichnung der einzigen Schaltfläche auf dem Panel geändert (Texteigenschaft, Abb. 5).

Abb. 5 Panel zur Erzeugung einer OCO-Order: neues Paar

Abb. 5 Panel zur Erzeugung einer OCO-Order: neues Paar


Die folgenden Klassen der Standard Library wurden zum Bau unseres Panels benutzt:

  • CAppDialog ist der hauptsächliche Anwendungsdialog;
  • CPanel ist eine rechteckige Bezeichnung;
  • CLabel ist eine Textbezeichnung;
  • CComboBox ist ein Feld mit der Dropdown-Liste;
  • CEdit ist ein Eingabefeld;
  • CButton ist eine Schaltfläche.

Übergeordnete Klassen-Methoden wurden natürlich automatisch aufgerufen.

Jetzt kommen wir zum Code. Hier muss gesagt werden, dass der Teil der Standard Library, der für die Erzeugung der Hinweis-Panels und Dialoge extra zugewiesen wurde, ziemlich umfangreich ist.

Wenn man z.B. ein Schließungselement der Dropdown-Liste erfassen will, muss man sich tief in den Haufen der Aufrufe hineinbegeben (Abb. 6).

Abb. 6 Haufen der Aufrufe

Abb. 6 Haufen der Aufrufe


Ein Entwickler setzt für spezifische Ereignisse in der %MQL5\Include\Controls\Defines.mqh-Datei Makros und einen Vermerk.

Ich habe für die Erzeugung des OCO-Paares ein ON_OCO individuell angepasstes Ereignis angelegt.

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

Die Parameter für zukünftige Orders werden gefüllt und das Paar wird im OnChartEvent() Handler-Korpus generiert. 

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

//--- drop-down list handling
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE)
     {
      //--- if it is Panel list
      if(!StringCompare(StringSubstr(sparam,0,7),"myCombo"))
        {
         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
         if(prev_vals[combo_idx]!=curr_val)
           {
            prev_vals[combo_idx]=curr_val;
            gOrdersProps[combo_idx].order_type=curr_val;
           }
        }
     }

//--- handling input fields
   else if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      //--- if it is Panel's input field
      if(!StringCompare(StringSubstr(sparam,0,6),"myEdit"))
        {
         //--- 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
            if(!StringCompare(sparam,curr_edit_obj_name))
              {
               //--- 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;
               if(order_num)
                  jdx=idx-gEditsHalfLen;
               //--- fill up gOrdersProps structure field
               switch(jdx)
                 {
                  case 0: // volume
                    {
                     gOrdersProps[order_num].volume=value;
                     break;
                    }
                  case 1: // execution
                    {
                     gOrdersProps[order_num].price_offset=(uint)value;
                     break;
                    }
                  case 2: // limit
                    {
                     gOrdersProps[order_num].limit_offset=(uint)value;
                     break;
                    }
                  case 3: // stop
                    {
                     gOrdersProps[order_num].sl=(uint)value;
                     break;
                    }
                  case 4: // profit
                    {
                     gOrdersProps[order_num].tp=(uint)value;
                     break;
                    }
                 }
              }
           }
         //--- 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(gOrdersProps[idx].order_type!=WRONG_VALUE)
               //---  if volume is set  
               if(gOrdersProps[idx].volume!=WRONG_VALUE)
                  //---  if offset for execution price is set
                  if(gOrdersProps[idx].price_offset!=(uint)WRONG_VALUE)
                     //---  if offset for limit price is set
                     if(gOrdersProps[idx].limit_offset!=(uint)WRONG_VALUE)
                        //---  if stop loss is set
                        if(gOrdersProps[idx].sl!=(uint)WRONG_VALUE)
                           //---  if take profit is set
                           if(gOrdersProps[idx].tp!=(uint)WRONG_VALUE)
                              continue;

            //--- clear OCO pair creation flag 
            is_to_fire_oco=false;
            break;
           }
         //--- create OCO pair?
         if(is_to_fire_oco)
           {
            //--- 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");
            myButton.Color(clrDarkBlue);
            myButton.ColorBackground(clrLightBlue);
            //--- respond to user actions 
            myButton.Enable();
           }
        }
     }
//--- handling click on button
   else if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- if it is OCO pair creation button
      if(!StringCompare(StringSubstr(sparam,0,6),"myFire"))
         //--- if to respond to user actions
         if(myButton.IsEnabled())
           {
            //--- generate OCO pair creation event
            EventChartCustom(0,ON_OCO,0,0.0,"OCO_fire");
            Print("Command to create new bunch has been received.");
           }
     }
//--- handling new pair initialization command 
   else if(id==CHARTEVENT_CUSTOM+ON_OCO)
     {
      //--- OCO pair initialization
      if(gOco.Init(gOrdersProps,gOcoList.Total()+1))
        {
         PrintFormat("Id of new OCO pair: %I32u",gOco.Id());
         //--- copy pair
         CiOcoObject *ptr_new_oco=new CiOcoObject(gOco);
         if(CheckPointer(ptr_new_oco)==POINTER_DYNAMIC)
           {
            //--- add to list
            int node_idx=gOcoList.Add(ptr_new_oco);
            if(node_idx>-1)
               PrintFormat("Total number of bunch: %d",gOcoList.Total());
            else
               PrintFormat("Error when adding OCO pair %I32u to list!",gOco.Id());
           }
        }
      else
         Print("OCO-orders placing error!");

      //--- clear properties
      Reset();
     }
  }

Der Handler-Code ist dabei gar nicht klein. Ich möchte Ihre Aufmerksamkeit auf verschiedene Blöcke lenken.

Dem Hauptdialog ist der erste Umgang mit allen Chart-Ereignissen übertragen.

Dann folgen die Blöcke unterschiedlicher Umgänge mit Ereignissen:

  • Sich ändernde Dropdown-Listen zur Definition eines Order-Typs;
  • Bearbeitung der Eingabefelder zum Eintragen der Order-Eigenschaften;
  • Anklick-Schaltfläche zur ON_OCO Ereignis-Generierung;
  • ON_OCO Ereignis-Antwort: Erzeugung eines Order-Paares.

Der EA bestätigt das exakte Füllen der Panel-Felder nicht, und deshalb müssen wir die Werte selbst überprüfen, denn sonst liefert uns der EA eine Fehlermeldung bei der Platzierung von OCO-Orders.

Die Notwendigkeit der Entfernung des Paares und des Schließens der restlichen Order wird im OnTrade() Handler-Korpus geprüft.


Fazit

Ich habe hier die Reichhaltigkeit der Standard Library-Klassen zu zeigen versucht, die zur Erfüllung einiger ganz bestimmter Aufgaben verwendet werden können.

Genauer gesagt, ging es hier um die Lösung eines Problems im Umgang mit OCO-Orders. Ich hoffe, dass der Code des EAs mit seinem Panel zum Umgang mit OCO-Orders einen geeigneten Ausgangspunkt zur Erzeugung weitaus komplizierterer Order-Paare darstellt.


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/1582

Beigefügte Dateien |
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)
Bau einer interaktiven Anwendung zur Anzeige von RSS-Feeds in MetaTrader 5 Bau einer interaktiven Anwendung zur Anzeige von RSS-Feeds in MetaTrader 5

In diesem Beitrag betrachten wir die Möglichkeit, eine Anwendung zur Anzeige von RSS-Feeds zu erzeugen. Es wird gezeigt, wie Aspekte der Standard Library dazu verwendet werden können, interaktive Programme für MetaTrader 5 zu erstellen.

Sichern Ihres Expert Advisors beim Handel auf der Moskauer Börse Sichern Ihres Expert Advisors beim Handel auf der Moskauer Börse

Dieser Beitrag behandelt Handelsmethoden, mit denen die Sicherheit von Handelsoperationen auf Aktienbörsen und Märkten mit geringer Liquidität gewährleistet wird, anhand des Beispiels des Terminmarktes der Moskauer Börse. Er liefert einen praktischen Ansatz für die Handelstheorie aus dem Beitrag "Grundlagen der Preisbildung von Börsen anhand des Beispiels des Terminmarktes der Moskauer Börse".

Die Betrachtung der CCanvas-Klasse. Wie man transparente Objekte zeichnet Die Betrachtung der CCanvas-Klasse. Wie man transparente Objekte zeichnet

Sie wollen mehr als nur komische Grafiken von gleitenden Mittelwerten? Sie möchten etwas Schöneres in Ihrem Terminal abbilden, als nur ein schlichtes, gefülltes Rechteck? Das geht! Im Terminal kann man nämlich tatsächliche attraktive Grafiken zeichnen. Und zwar durch Implementierung der CСanvas-Klasse, die zur Erzeugung von individuell angepassten Grafiken benutzt wird. Mit dieser Klasse können Sie Transparenz umsetzen, Farben mischen und sogar den Anschein von Transparenz durch Überlappung und Ineinanderlaufen von Farben erreichen.

Das MQL5-Kochbuch: Implementierung eines Assoziativen Arrays oder eines Lexikons für raschen Datenzugriff Das MQL5-Kochbuch: Implementierung eines Assoziativen Arrays oder eines Lexikons für raschen Datenzugriff

Dieser Beitrag beschreibt einen speziellen Algorithmus mit dem auf Elemente mittels ihrer einmaligen Schlüssel zugegriffen werden kann. Als Schlüssel kann jeder einfache Datentyp verwendet werden. Er kann z.B. als String oder eine ganzzahlige Variable dargestellt werden. So einen Datenbehälter kennt man meistens als Lexikon oder ein assoziatives Array. Er bietet einen leichteren und effizienteren Weg der Problemlösung.