
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXI): Handelsklassen - Plattformübergreifendes Basis-Handelsobjekt
Inhalt
- Konzept
- Anlegen des Basis-Handelsobjekts
- Testen des Basisobjekts des Handels
- Was kommt als Nächstes?
Konzept
Es ist schön, jederzeit einen einfachen Zugriff auf verschiedene Daten zu haben. Diese Daten sind jedoch nutzlos, wenn wir sie nicht beim Handeln verwenden können. Das bedeutet, dass wir neben der bereits vorhandenen Handelsfunktionen auch eine weitere benötigen.
Dieser Abschnitt wird relativ groß sein, und wir werden alles Schritt für Schritt tun.
- Wir sollten in der Lage sein, alle Handelsanfragen von jeder Plattform aus zu senden, sei es MetaTrader 5 oder MetaTrader 4, ohne auch nur an die Unterschiede zwischen ihnen zu denken. Alles sollte vereinheitlicht werden.
- Erstens müssen wir Handelsanforderungen überprüfen, um den Server nicht mit absichtlich fehlerhaften Anforderungen zu belasten.
- Wir müssen die Antworten des Handelsservers berücksichtigen und korrekt behandeln. Was macht ein EA, wenn er eine Anfrage an den Server sendet? Er bestreitet einen den Dialog aus "Frage und Antwort" mit dem Server. Unsere Aufgabe ist es, diesen "Kommunikationskanal" richtig zu gestalten, d.h. die Methoden zur Behandlung der Antworten von Handelsservern zu schaffen.
- Wir müssen mehrere Optionen für die Behandlung der Serverantworten schaffen, da wir manchmal eine Position "vorzugsweise um jeden Preis" eröffnen müssen. Dazu müssen wir eine wiederholte Versendung einer Anfrage an den Server veranlassen, falls eine Order nicht erteilt werden kann — wir können entweder die Parameter der Handelsanfrage anpassen oder erneut senden, oder alle Parameter belassen wie sie sind, und auf den richtigen Moment warten, wenn die Anfrage mit diesen Parametern weitergeleitet wird, um sie sofort zu senden. Außerdem müssen wir das Preisniveau berücksichtigen, um eine Bestellung nicht zu einem bewusst schlechteren Preis erneut zu versenden.
Manchmal müssen wir eine Handelsanfrage senden und die Arbeit unabhängig von einem Anfrageergebnis fortsetzen.
- Außerdem müssen wir die Arbeit mit den Handelsklassen so gestalten, dass Probleme bei der Platzierung eines bibliotheksbasierten Programms auf MQL5 Market vermieden werden. Das Programm sollte alle Prüfungen reibungslos bestehen.
Dies ist mein aktueller Plan bezüglich der Handelsklassen.
In diesem Artikel werden wir die Entwicklung des Basisobjekts des Handels besprechen. Dies ist eine Klasse, die unabhängig von der Plattform eine Handelsanforderung in gleicher Weise an den Server sendet. Wenn ein Auftrag an den Server gesendet wird, bedeutet ein solches Handelsobjekt, dass verifizierte und korrekte Parameter der Handelsanfrage an den Server übergeben werden. Das Objekt weist keine Überprüfung der Parameter auf. Stattdessen werden sie in der Basisklasse des Handels verifiziert, die später entwickelt wird.
Es ist zu beachten, dass die Auswahl eines Auftrags oder einer Position nach der Ticketnummer zunächst im aktuellen Handelsobjekt implementiert werden soll. Nach der Erstellung der Basisklasse des Handels wird das Feature dorthin verlagert.
Da der gesamte Handel direkt an ein Symbol gebunden ist, wird das Basisobjekt des Handels Teil eines Symbolobjekts sein, das wir im Artikel 14 besprochen haben. Der Zugriff auf Symbolhandelsobjekte wird später geregelt — in der Basisklasse des Handels. In diesem Artikel werden wir einen temporären Zugriff auf Symbolhandelsobjekte aus der Basisklasse CEngine Bibliothek organisieren, die wir im Artikel 3 besprochen haben. Dies ist die Klasse, in der alle Umgebungsdaten gesammelt werden. Es verfügt über alle Konto- und Symboleigenschaften, die für die Arbeit mit Handelsklassen erforderlich sind.
Erstellen eines Basisobjekts des Handels
Um die Arbeit von Handelsklassen zu protokollieren, müssen wir eine Enumeration von Protokollebenen in der Bibliotheksdatei Defines.mqh erstellen.
Hinzufügen der benötigen Enumerationen am Ende des Auflistung:
//+------------------------------------------------------------------+ //| Data for working with trading classes | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Logging level | //+------------------------------------------------------------------+ enum ENUM_LOG_LEVEL { LOG_LEVEL_NO_MSG, // Trading logging disabled LOG_LEVEL_ERROR_MSG, // Only trading errors LOG_LEVEL_ALL_MSG // Full logging }; //+------------------------------------------------------------------+
Um Nachrichten im Journal anzuzeigen, benötigen wir Nachrichtentexte und deren Indizes in der Liste der Bibliotheksnachrichten.
Hinzufügen der notwendigen Indizes zur Datei Datas.mqh:
MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER, // Error. No such symbol on server MSG_LIB_SYS_NOT_SYMBOL_ON_LIST, // Error. No such symbol in the list of used symbols: MSG_LIB_SYS_FAILED_PUT_SYMBOL, // Failed to place to market watch. Error: MSG_LIB_SYS_ERROR_NOT_POSITION, // Error. Not a position: MSG_LIB_SYS_ERROR_NO_OPEN_POSITION_WITH_TICKET, // Error. No open position with ticket # MSG_LIB_SYS_ERROR_NO_PLACED_ORDER_WITH_TICKET, // Error. No placed order with ticket # MSG_LIB_SYS_ERROR_FAILED_CLOSE_POS, // Failed to closed position. Error MSG_LIB_SYS_ERROR_FAILED_MODIFY_ORD, // Failed to modify order. Error MSG_LIB_SYS_ERROR_UNABLE_PLACE_WITHOUT_TIME_SPEC, // Error: Cannot place order without explicitly specified expiration time MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ, // Error. Failed to get trading object MSG_LIB_SYS_ERROR_FAILED_GET_POS_OBJ, // Error. Failed to get position object MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ, // Error. Failed to get order object MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ, // Error. Failed to get symbol object MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE, // Return code out of range of error codes MSG_LIB_TEXT_FAILED_ADD_TO_LIST, // failed to add to list MSG_LIB_TEXT_TIME_UNTIL_THE_END_DAY, // Order lifetime till the end of the current day to be used MSG_LIB_TEXT_SUNDAY, // Sunday MSG_ACC_MARGIN_MODE_RETAIL_EXCHANGE, // Exchange markets mode MSG_ACC_UNABLE_CLOSE_BY, // Close by is available only on hedging accounts MSG_ACC_SAME_TYPE_CLOSE_BY, // Error. Positions for close by are of the same type //--- CEngine MSG_ENG_NO_TRADE_EVENTS, // There have been no trade events since the last launch of EA MSG_ENG_FAILED_GET_LAST_TRADE_EVENT_DESCR, // Failed to get description of the last trading event MSG_ENG_FAILED_GET_MARKET_POS_LIST, // Failed to get the list of open positions MSG_ENG_FAILED_GET_PENDING_ORD_LIST, // Failed to get the list of placed orders MSG_ENG_NO_OPEN_POSITIONS, // No open positions MSG_ENG_NO_PLACED_ORDERS, // No placed orders };
Hier werden nur die Teile der Datei mit der "Geolokalisierung" (wo die Aufzählungskonstanten der Indizes hinzugefügt werden sollen) angezeigt.
Fügen Sie nun die benötigten Nachrichten, deren Indizes wir gerade definiert haben, dem Array der Textnachrichten hinzu:
{"Ошибка. Такого символа нет на сервере","Error. No such symbol on server"}, {"Ошибка. Такого символа нет в списке используемых символов: ","Error. This symbol is not in the list of symbols used: "}, {"Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in market watch. Error: "}, {"Ошибка. Не позиция: ","Error. Not position: "}, {"Ошибка. Нет открытой позиции с тикетом #","Error. No open position with ticket #"}, {"Ошибка. Нет установленного ордера с тикетом #","Error. No placed order with ticket #"}, {"Не удалось закрыть позицию. Ошибка ","Could not close position. Error "}, {"Не удалось модифицировать ордер. Ошибка ","Failed to modify order. Error "}, {"Ошибка: невозможно разместить ордер без явно заданного его времени истечения","Error: Unable to place order without explicitly specified expiration time"}, {"Ошибка. Не удалось получить торговый объект","Error. Failed to get trade object"}, {"Ошибка. Не удалось получить объект-позицию","Error. Failed to get position object"}, {"Ошибка. Не удалось получить объект-ордер","Error. Failed to get order object"}, {"Ошибка. Не удалось получить объект-символ","Error. Failed to get symbol object"}, {"Код возврата вне заданного диапазона кодов ошибок","Return code out of range of error codes"}, {"не удалось добавить в список","failed to add to list"}, {"Будет использоваться время действия ордера до конца текущего дня","Order validity time until the end of the current day will be used"}, {"Воскресение","Sunday"}, {"Биржевой рынок","Exchange market mode"}, {"Закрытие встречным доступно только на счетах с типом \"Хеджинг\"","Close by opposite position iavailable only on \"Hedging\" accounts"}, {"Ошибка. Позиции для встречного закрытия имеют один и тот же тип","Error. Positions of the same type in counterclosure request"}, //--- CEngine {"С момента последнего запуска ЕА торговых событий не было","No trade events since the last launch of EA"}, {"Не удалось получить описание последнего торгового события","Failed to get description of the last trading event"}, {"Не удалось получить список открытых позиций","Failed to get open positions list"}, {"Не удалось получить список установленных ордеров","Failed to get pending orders list"}, {"Нет открытых позиций","No open positions"}, {"Нет установленных ордеров","No placed orders"}, };
Wie bei der Definition von Indexkonstanten werden hier nur bestimmte Bereiche zum Hinzufügen der notwendigen Nachrichtentexte angezeigt. In den Anlagen finden und analysieren Sie die Vollversion der verbesserten Datas.mqh.
Beim Senden von Aufträgen, Positionen zu schließen, müssen wir einen Auftragstyp kennen, der der Richtung der zu schließenden Position entgegengesetzt ist (in MQL5 erfolgt das Schließen durch Öffnen einer entgegengesetzten Position, während ein Auftragstyp (nicht Positionstyp) an ein Handelsauftrag gesendet wird).
Schreiben Sie in der Datei der Servicefunktionen der Bibliothek DELib.mqh zwei Funktionen zum Empfangen eines Auftragstyps durch eine Positionsrichtung und einen Auftragstyp entgegen der Positionsrichtung:
//+------------------------------------------------------------------+ //| Return an order type by a position type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE OrderTypeByPositionType(ENUM_POSITION_TYPE type_position) { return(type_position==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL); } //+------------------------------------------------------------------+ //| Return a reverse order type by a position type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE OrderTypeOppositeByPositionType(ENUM_POSITION_TYPE type_position) { return(type_position==POSITION_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); } //+------------------------------------------------------------------+
Nachdem wir nun alle Daten vorbereitet haben, wollen wir uns mit dem Klassenobjekt des Handels selbst befassen.
In \MQL5\Include\DoEasy\Objects\, erstellen Sie Trade\ Unterordner und die neue Klasse CTradeObj in der Datei TradeObj.mqh darin.
Einbinden der Datei der Servicefunktionen in die neu erstellte Datei:
//+------------------------------------------------------------------+ //| TradeObj.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+
Fügen Sie alle notwendigen Variablen und Methoden der Klasse zu ihrer Datei hinzu:
//+------------------------------------------------------------------+ //| TradeObj.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+ //| Trading object class | //+------------------------------------------------------------------+ class CTradeObj { private: MqlTick m_tick; // Tick structure for receiving prices MqlTradeRequest m_request; // Trade request structure MqlTradeResult m_result; // trade request execution result ENUM_ACCOUNT_MARGIN_MODE m_margin_mode; // Margin calculation mode ENUM_ORDER_TYPE_FILLING m_type_filling; // Filling policy ENUM_ORDER_TYPE_TIME m_type_expiration; // Order expiration type int m_symbol_expiration_flags; // Flags of order expiration modes for a trading object symbol ulong m_magic; // Magic number string m_symbol; // Symbol string m_comment; // Comment ulong m_deviation; // Slippage in points double m_volume; // Volume datetime m_expiration; // Order expiration time (for ORDER_TIME_SPECIFIED type order) bool m_async_mode; // Flag of asynchronous sending of a trade request ENUM_LOG_LEVEL m_log_level; // Logging level int m_stop_limit; // Distance of placing a StopLimit order in points public: //--- Konstructor CTradeObj();; //--- Set default values void Init(const string symbol, const ulong magic, const double volume, const ulong deviation, const int stoplimit, const datetime expiration, const bool async_mode, const ENUM_ORDER_TYPE_FILLING type_filling, const ENUM_ORDER_TYPE_TIME type_expiration, ENUM_LOG_LEVEL log_level); //--- (1) Return the margin calculation mode, (2) hedge account flag ENUM_ACCOUNT_MARGIN_MODE GetMarginMode(void) const { return this.m_margin_mode; } bool IsHedge(void) const { return this.GetMarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING; } //--- (1) Set, (2) return the error logging level void SetLogLevel(const ENUM_LOG_LEVEL level) { this.m_log_level=level; } ENUM_LOG_LEVEL GetLogLevel(void) const { return this.m_log_level; } //--- (1) Set, (2) return the filling policy void SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.m_type_filling=type; } ENUM_ORDER_TYPE_FILLING GetTypeFilling(void) const { return this.m_type_filling; } //--- (1) Set, (2) return order expiration type void SetTypeExpiration(const ENUM_ORDER_TYPE_TIME type) { this.m_type_expiration=type; } ENUM_ORDER_TYPE_TIME GetTypeExpiration(void) const { return this.m_type_expiration; } //--- (1) Set, (2) return the magic number void SetMagic(const ulong magic) { this.m_magic=magic; } ulong GetMagic(void) const { return this.m_magic; } //--- (1) Set, (2) return a symbol void SetSymbol(const string symbol) { this.m_symbol=symbol; } string GetSymbol(void) const { return this.m_symbol; } //--- (1) Set, (2) return a comment void SetComment(const string comment) { this.m_comment=comment; } string GetComment(void) const { return this.m_comment; } //--- (1) Set, (2) return slippage void SetDeviation(const ulong deviation) { this.m_deviation=deviation; } ulong GetDeviation(void) const { return this.m_deviation; } //--- (1) Set, (2) return volume void SetVolume(const double volume) { this.m_volume=volume; } double GetVolume(void) const { return this.m_volume; } //--- (1) Set, (2) return order expiration date void SetExpiration(const datetime time) { this.m_expiration=time; } datetime GetExpiration(void) const { return this.m_expiration; } //--- (1) Set, (2) return the flag of the asynchronous sending of a trading request void SetAsyncMode(const bool async) { this.m_async_mode=async; } bool GetAsyncMode(void) const { return this.m_async_mode; } //--- Last request data: //--- Return (1) executed action type, (2) magic number, (3) order ticket, (4) volume, //--- (5) open, (6) StopLimit order, (7) StopLoss, (8) TakeProfit price, (9) deviation, //--- type of (10) order, (11) execution, (12) lifetime, (13) order expiration date, //--- (14) comment, (15) position ticket, (16) opposite position ticket ENUM_TRADE_REQUEST_ACTIONS GetLastRequestAction(void) const { return this.m_request.action; } ulong GetLastRequestMagic(void) const { return this.m_request.magic; } ulong GetLastRequestOrder(void) const { return this.m_request.order; } double GetLastRequestVolume(void) const { return this.m_request.volume; } double GetLastRequestPrice(void) const { return this.m_request.price; } double GetLastRequestStopLimit(void) const { return this.m_request.stoplimit; } double GetLastRequestStopLoss(void) const { return this.m_request.sl; } double GetLastRequestTakeProfit(void) const { return this.m_request.tp; } ulong GetLastRequestDeviation(void) const { return this.m_request.deviation; } ENUM_ORDER_TYPE GetLastRequestType(void) const { return this.m_request.type; } ENUM_ORDER_TYPE_FILLING GetLastRequestTypeFilling(void) const { return this.m_request.type_filling; } ENUM_ORDER_TYPE_TIME GetLastRequestTypeTime(void) const { return this.m_request.type_time; } datetime GetLastRequestExpiration(void) const { return this.m_request.expiration; } string GetLastRequestComment(void) const { return this.m_request.comment; } ulong GetLastRequestPosition(void) const { return this.m_request.position; } ulong GetLastRequestPositionBy(void) const { return this.m_request.position_by; } //--- Data on the last request result: //--- Return (1) operation result code, (2) performed deal ticket, (3) placed order ticket, //--- (4) deal volume confirmed by a broker, (5) deal price confirmed by a broker, //--- (6) current market Bid (requote) price, (7) current market Ask (requote) price //--- (8) broker comment to operation (by default, it is filled by the trade server return code description), //--- (9) request ID set by the terminal when sending, (10) external trading system return code uint GetResultRetcode(void) const { return this.m_result.retcode; } ulong GetResultDeal(void) const { return this.m_result.deal; } ulong GetResultOrder(void) const { return this.m_result.order; } double GetResultVolume(void) const { return this.m_result.volume; } double GetResultPrice(void) const { return this.m_result.price; } double GetResultBid(void) const { return this.m_result.bid; } double GetResultAsk(void) const { return this.m_result.ask; } string GetResultComment(void) const { return this.m_result.comment; } uint GetResultRequestID(void) const { return this.m_result.request_id; } uint GetResultRetcodeEXT(void) const { return this.m_result.retcode_external;} //--- Positionseröffnung bool OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Close a position bool ClosePosition(const ulong ticket, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Close a position partially bool ClosePositionPartially(const ulong ticket, const double volume, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Close a position by an opposite one bool ClosePositionBy(const ulong ticket,const ulong ticket_by); //--- Modify a position bool ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE); //--- Place an order bool SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=0, const ulong magic=ULONG_MAX, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC, const string comment=NULL); //--- Remove an order bool DeleteOrder(const ulong ticket); //--- Modify an order bool ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double price_stoplimit=WRONG_VALUE, const datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE); }; //+------------------------------------------------------------------+
Lassen Sie uns einen genaueren Blick auf das werfen, was hier geschrieben wurde.
Um die aktuellen Preise zu erhalten, müssen wir auf die Eigenschaften eines Symbols zugreifen, auf dem eine Handelsanfrage gesendet werden soll. Da wir relevante Preise benötigen, sollten wir sie unmittelbar vor dem Senden einer Handelsanfrage erhalten. Deshalb platzieren wir die Variable m_tick mit dem Strukturtyp MqlTick direkt im Basisobjekt des Handels. Wir können es von einem Symbolobjekt übergeben, aber es wäre besser, auf redundante (wenn auch minimale) Kosten für die Übergabe einer Immobilie an ein Handelsobjekt zu verzichten.
Die Variable m_request mit der Handelsanforderungsstruktur vom Typ MqlTradeRequest werden alle Eigenschaften der Handelsanforderung ausgefüllt und an die Funktion OrderSend() gesendet. Die Variable m_result mit der Handelsanfrage-Ergebnisstruktur vom Typ MqlTradeResult wird ebenfalls der Funktion übergeben. Sie wird vom Server ausgefüllt, wenn er eine Antwort vom Handelsserver erhält. Wenn das Ergebnis des Sendens eines Auftrags an den Server falsch ist, können wir immer die Felder der Ergebnisstruktur der Handelsanfrage kontrollieren, um zu verstehen, was passiert ist.
Ich glaube, die anderen Klassenvariablen benötigen keine Erklärungen.
Lassen Sie uns einen Blick auf die Implementierung der Klassenmethoden werfen.
Die Methoden zum Setzen und Abrufen der Eigenschaften einer Handelsanfrage (Set- und Get-Methoden) werden in den Klassenkörper geschrieben. Alles was sie tun, ist den der Methode übergebenen Wert in die entsprechende Variable zu schreiben oder den Wert der entsprechenden Variable zurückzugeben. Diese Methoden arbeiten nur mit den Variablen, die die Standardwerte speichern. Mit anderen Worten, diese Methoden erlauben es, die gewünschte Eigenschaft einer Handelsanforderung zu setzen. Im weiteren Verlauf hat sie den definierten Wert als Standardwert. Wenn Sie einmal einen anderen Wert für eine Handelsaufforderung verwenden müssen, bieten die Methoden zum Senden von Handelsaufforderungen die Möglichkeit, die erforderlichen Werte für eine einmalige Verwendung des an die Methode übergebenen Wertes zu übergeben.
Die Methoden, die die Parameter der letzten Handelsanforderung zurückgeben, erlauben es zu definieren, welcher Wert an eine Eigenschaft der letzten Handelsanforderung übergeben wurde und Schritte zu unternehmen, um Fehler zu beseitigen oder diese Werte für die nächste Serveranforderung zu verwenden.
Die Methoden geben einfach den Inhalt der Felder der Handelsanforderungsstruktur zurück, die der Methode entsprechen. Bevor eine Anfrage gesendet wird, wird diese Struktur (einige ihrer Felder, die einer Handelsanfrage entsprechen) ausgefüllt und an die Funktion zum Senden einer Anfrage an den Server übergeben. Dies ist die Struktur, aus der wir die zuletzt gefüllten Werte erhalten.
Die Methoden, die das Ergebnis der Handelsanfrage zurückgeben, erlauben es, Daten über das Ergebnis der Bearbeitung der Handelsanfrage zu erhalten. Wenn sich die Anfrage als fehlerhaft herausstellt, können wir die Klärung des Fehlercodes in retcode sehen. Alternativ wird die Struktur mit Daten über eine offene Position oder eine platzierte Pending Order gefüllt, während request_id den Code einer Anfrage enthält, die dann in der Funktion OnTradeTransaction() analysiert werden kann, um die über OrderSendAsync() an den Server gesendete Handelsanfrage mit dem Anfrageergebnis zu verknüpfen.
In dieser Bibliothek verwenden wir OnTradeTransaction() aufgrund des Fehlens in MQL4 nicht. Wir werden das asynchrone Senden von Anfragen und deren Ergebnisse selbst analysieren.
Der Klassenkonstruktor:
//+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CTradeObj::CTradeObj(void) : m_magic(0), m_deviation(5), m_stop_limit(0), m_expiration(0), m_async_mode(false), m_type_filling(ORDER_FILLING_FOK), m_type_expiration(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"), m_log_level(LOG_LEVEL_ERROR_MSG) { //--- Margin calculation mode this.m_margin_mode= ( #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ); } //+------------------------------------------------------------------+
Setzen Sie Initialisierungswerte in seiner Initialisierungsliste:
- die Magicnummer ist Null,
- der Schlupf beträgt fünf Punkte,
- der Preis der StopLimit-Order ist Null (kein Preis),
- die Verfallszeit der Order ist Null (unbegrenzt),
- asynchrones Senden von Handelsanfragen ist deaktiviert,
- die Ausführungsart der Aufträge ist "Fill or Kill" (ganz oder gar nichts),
- die Verfallszeit der Orders — unbegrenzt
- im Auftragskommentar schreiben wir den Programmnamen + " by DoEasy",
- der Modus der Protokollierung des Handelsklassenbetriebs — nur Fehler.
Weisen Sie im Hauptteil der Klasse die für das Konto festgelegte Margenberechnungsmethode der Variablen m_margin_mode zu.
In MQL5 erhalten Sie den gewünschten Wert mit der Funktion AccountInfoInteger() und der Eigenschafts-ID ACCOUNT_MARGIN_MODE.
Für MQL4 stellen Sie sofort den Absicherungsmodus der Margenberechnung ein (ACCOUNT_MARGIN_MODE_RETAIL_HEDGING).
Wir werden in der Lage sein, die erforderlichen Werte der notwendigen Eigenschaften an die Handelsmethoden zum Ausfüllen einer Handelsanfrage zu übergeben. Häufig müssen wir jedoch nicht alle notwendigen Eigenschaften ausfüllen — sie sollten in der Regel bei jeder Handelsanfrage unverändert bleiben. Daher sollten wir in der Lage sein, die Standardvariablen zu initialisieren und die Werte auszuwählen, die für eine Handelsanfrage in den Handelsmethoden verwendet werden sollen — entweder die, die an die Methode zum Senden einer Anfrage an den Server übergeben werden oder die, die standardmäßig eingestellt sind.
Schreiben Sie die Methode zur Initialisierung der Standardparameter der Handelsanforderung:
//+------------------------------------------------------------------+ //| Set default values | //+------------------------------------------------------------------+ void CTradeObj::Init(const string symbol, const ulong magic, const double volume, const ulong deviation, const int stoplimit, const datetime expiration, const bool async_mode, const ENUM_ORDER_TYPE_FILLING type_filling, const ENUM_ORDER_TYPE_TIME type_expiration, ENUM_LOG_LEVEL log_level) { this.SetSymbol(symbol); this.SetMagic(magic); this.SetDeviation(deviation); this.SetVolume(volume); this.SetExpiration(expiration); this.SetTypeFilling(type_filling); this.SetTypeExpiration(type_expiration); this.SetAsyncMode(async_mode); this.SetLogLevel(log_level); this.m_symbol_expiration_flags=(int)::SymbolInfoInteger(this.m_symbol,SYMBOL_EXPIRATION_MODE); this.m_volume=::SymbolInfoDouble(this.m_symbol,SYMBOL_VOLUME_MIN); } //+------------------------------------------------------------------+
Die Methode erhält die erforderlichen Werte der Parameter der Handelsanforderung. Im Körper der Methode werden die übergebenen Werte für die entsprechenden Variablen mit Hilfe ihrer oben betrachteten Zuweisungsmethoden gesetzt. Die Flags der erlaubten Modi der Verfallszeit der Orders werden mit der Funktion SymbolInfoInteger() und der Eigenschafts-ID SYMBOL_EXPIRATION_MODE gesetzt. Das verwendete Volumen ist das Mindestvolumen, das für ein Symbol mit der Funktion SymbolInfoDouble() und der Eigenschafts-ID SYMBOL_VOLUME_MIN festgelegt ist.
Methode zum Öffnen einer Position:
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = this.m_symbol; this.m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); this.m_request.type = OrderTypeByPositionType(type); this.m_request.price = (type==POSITION_TYPE_BUY ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = volume; this.m_request.sl = sl; this.m_request.tp = tp; this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else (::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE)!=WRONG_VALUE) #endif ); } //+------------------------------------------------------------------+
Die Methode erhält die Positionsart, das Volumen, StopLoss- und TakeProfit-Kurse, die Magicnummer der Position, den Slippage-Wert und den Kommentar.
Die Standardwerte sind für StopLoss, TakeProfit, Magicnummer, Slippage und den Kommentar eingestellt. Wenn diese Werte beim Aufruf der Methode so belassen werden, dann werden für diese Werte die Werte verwendet, die standardmäßig in der Methode Init() oder direkt vom Programm aus durch die Methoden zum Setzen von Standardwerten, die wir oben untersucht haben, gesetzt werden. Die gesamte Methodenlogik ist in den Codekommentaren geschrieben.
Das Ergebnis, das von der Funktion OrderTypeByPositionType(), die wir in DELib.mqh für den Empfang eines Ordertyps durch eine Positionsart geschrieben haben, gesendet wird, wird in das Feld der Struktur einer Handelsanfrage gesendet, die den Ordertyp speichert. Die Methode verifiziert nicht die ihr übergebenen Parameter. Es wird angenommen, dass sie bereits verifiziert wurden.
Bei MQL4 ändern wir auch bei der Rückgabe des Ergebnisses der Anfrage an den Server noch nichts und füllen die Struktur des Ergebnisses der Handelsanfrage nicht aus. Aktuell müssen wir schnell Handelsmethoden zum Testen sammeln. Wir werden in den kommenden Artikeln alles in Ordnung bringen.
Die Methode, eine Position zu schließen:
//+------------------------------------------------------------------+ //| Close a position | //+------------------------------------------------------------------+ bool CTradeObj::ClosePosition(const ulong ticket, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- If failed to select a position. Write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position symbol string symbol=::PositionGetString(POSITION_SYMBOL); //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position type and an order type inverse of the position type ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Get a position volume and magic number double position_volume=::PositionGetDouble(POSITION_VOLUME); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = symbol; this.m_request.magic = magic; this.m_request.type = type; this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = position_volume; this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- In case of a hedging account, write the ticket of a closed position to the structure if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderClose((int)m_request.position,m_request.volume,m_request.price,(int)m_request.deviation,clrNONE) #endif ); } //+------------------------------------------------------------------+
Die Methode empfängt das Ticket einer geschlossenen Position, Slippage und den Kommentar.
Hier (wie auch bei anderen Handelsmethoden) ist alles ähnlich der oben besprochenen Methode der Positionseröffnung.
Die Methode für das teilweise Schließen von Positionen:
//+------------------------------------------------------------------+ //| Close a position partially | //+------------------------------------------------------------------+ bool CTradeObj::ClosePositionPartially(const ulong ticket, const double volume, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- If failed to select a position. Write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position symbol string symbol=::PositionGetString(POSITION_SYMBOL); //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position type and an order type inverse of the position type ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Get a position volume and magic number double position_volume=::PositionGetDouble(POSITION_VOLUME); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.position = ticket; this.m_request.symbol = symbol; this.m_request.magic = magic; this.m_request.type = type; this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = (volume<position_volume ? volume : position_volume); this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- In case of a hedging account, write the ticket of a closed position to the structure if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderClose((int)m_request.position,m_request.volume,m_request.price,(int)m_request.deviation,clrNONE) #endif ); } //+------------------------------------------------------------------+
Die Methode übergibt das Ticket einer geschlossenen Position, eines zu schließenden Volumens, eines Slippage und eines Kommentars.
Übersteigt ein an die Methode übergebenes Schließvolumen das vorhandene Positionsvolumen, wird die Position vollständig geschlossen.
Die Methode, um eine Position durch eine entgegengesetzte zu schließen:
//+------------------------------------------------------------------+ //| Close a position by an opposite one | //+------------------------------------------------------------------+ bool CTradeObj::ClosePositionBy(const ulong ticket,const ulong ticket_by) { #ifdef __MQL5__ //--- If this is not a hedging account. if(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { //--- Close by is available only on hedging accounts. //---Write the error code and description, send the message to the journal and return 'false' this.m_result.retcode=MSG_ACC_UNABLE_CLOSE_BY; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_ACC_UNABLE_CLOSE_BY)); return false; } #endif //--- Closed position //--- If failed to select a position, write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a type and magic of a closed position ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- entgegengesetzte Position //--- If failed to select a position, write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket_by)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket_by,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Get an opposite position type ENUM_POSITION_TYPE position_type_by=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); //--- If types of a closed and an opposite position match, write the error code and description, send the message to the journal and return 'false' if(position_type==position_type_by) { this.m_result.retcode=MSG_ACC_SAME_TYPE_CLOSE_BY; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(MSG_ACC_SAME_TYPE_CLOSE_BY)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_CLOSE_BY; this.m_request.position = ticket; this.m_request.position_by = ticket_by; this.m_request.magic = magic; //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderCloseBy((int)m_request.position,(int)m_request.position_by,clrNONE) #endif ); } //+------------------------------------------------------------------+
Die Methode erhält das Ticket einer geschlossenen und das einer entgegengesetzten Position.
Die Methode zur Änderung der Stopps einer Position:
//+------------------------------------------------------------------+ //| Modify a position | //+------------------------------------------------------------------+ bool CTradeObj::ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE) { //--- If all default values are passed, there is nothing to be modified if(sl==WRONG_VALUE && tp==WRONG_VALUE) { //--- There are no changes in the request - write the error code and description, send the message to the journal and return 'false' this.m_result.retcode= #ifdef __MQL5__ TRADE_RETCODE_NO_CHANGES #else 10025 #endif ; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(this.m_result.retcode),CMessage::Retcode(this.m_result.retcode)); return false; } //--- If failed to select a position, write the error code and description, send the message to the journal and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_SLTP; m_request.position= ticket; m_request.symbol = ::PositionGetString(POSITION_SYMBOL); m_request.magic = ::PositionGetInteger(POSITION_MAGIC); m_request.sl = (sl==WRONG_VALUE ? ::PositionGetDouble(POSITION_SL) : sl); m_request.tp = (tp==WRONG_VALUE ? ::PositionGetDouble(POSITION_TP) : tp); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderModify((int)m_request.position,::OrderOpenPrice(),m_request.sl,m_request.tp,::OrderExpiration(),clrNONE) #endif ); } //+------------------------------------------------------------------+
Die Methode erhält das Ticket der zu ändernden Position und die neun Preise für StopLoss und TakeProfit.
Die Methode für das Platzieren einer Pending-Order:
//+------------------------------------------------------------------+ //| Set an order | //+------------------------------------------------------------------+ bool CTradeObj::SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=0, const ulong magic=ULONG_MAX, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC, const string comment=NULL) { //--- If an invalid order type has been passed, write the error code and description, send the message to the journal and return 'false' if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL || type==ORDER_TYPE_CLOSE_BY #ifdef __MQL4__ || type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT #endif ) { this.m_result.retcode=MSG_LIB_SYS_INVALID_ORDER_TYPE; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_ORDER_TYPE),OrderTypeDescription(type)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_PENDING; m_request.symbol = this.m_symbol; m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); m_request.volume = volume; m_request.type = type; m_request.stoplimit = price_stoplimit; m_request.price = price; m_request.sl = sl; m_request.tp = tp; m_request.type_time = type_time; m_request.expiration = expiration; m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else (::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE)!=WRONG_VALUE) #endif ); } //+------------------------------------------------------------------+
Die Methode erhält den Typ einer Pending-Order, das Volumen, den Eröffnungspreis, StopLoss, TakeProfit und StopLimit-Orderpreise, die Magicnummer, die Verfallszeit der Order, den Verfallszeittyp der Order und den Kommentar.
Die Methode zum Entfernen einer Pending-Order:
//+------------------------------------------------------------------+ //| Remove an order | //+------------------------------------------------------------------+ bool CTradeObj::DeleteOrder(const ulong ticket) { //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_REMOVE; m_request.order = ticket; //--- Return the result of sending a request to the server return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderDelete((int)m_request.order,clrNONE) #endif ); } //+------------------------------------------------------------------+
Die Methode erhält das Ticket einer zu löschenden Order.
Die Methode zur Änderung einer Pending-Order:
//+------------------------------------------------------------------+ //| Modify an order | //+------------------------------------------------------------------+ bool CTradeObj::ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double price_stoplimit=WRONG_VALUE, const datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE) { //--- If failed to select an order, write the error code and description, send the message to the journal and return 'false' #ifdef __MQL5__ if(!::OrderSelect(ticket)) #else if(!::OrderSelect((int)ticket,SELECT_BY_TICKET)) #endif { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_ORD),CMessage::Text(this.m_result.retcode)); return false; } double order_price=::OrderGetDouble(ORDER_PRICE_OPEN); double order_sl=::OrderGetDouble(ORDER_SL); double order_tp=::OrderGetDouble(ORDER_TP); double order_stoplimit=::OrderGetDouble(ORDER_PRICE_STOPLIMIT); ENUM_ORDER_TYPE_TIME order_type_time=(ENUM_ORDER_TYPE_TIME)::OrderGetInteger(ORDER_TYPE_TIME); datetime order_expiration=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION); //--- If the default values are passed and the price is equal to the price set in the order, the request is unchanged //---Write the error code and description, send the message to the journal and return 'false' if(price==order_price && sl==WRONG_VALUE && tp==WRONG_VALUE && price_stoplimit==WRONG_VALUE && type_time==WRONG_VALUE && expiration==WRONG_VALUE) { this.m_result.retcode = #ifdef __MQL5__ TRADE_RETCODE_NO_CHANGES #else 10025 #endif ; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(this.m_result.retcode),CMessage::Retcode(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure m_request.action = TRADE_ACTION_MODIFY; m_request.order = ticket; m_request.price = (price==WRONG_VALUE ? order_price : price); m_request.sl = (sl==WRONG_VALUE ? order_sl : sl); m_request.tp = (tp==WRONG_VALUE ? order_tp : tp); m_request.stoplimit = (price_stoplimit==WRONG_VALUE ? order_stoplimit : price_stoplimit); m_request.type_time = (type_time==WRONG_VALUE ? order_type_time : type_time); m_request.expiration = (expiration==WRONG_VALUE ? order_expiration : expiration); //--- Return an order modification result return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderModify((int)m_request.order,m_request.price,m_request.sl,m_request.tp,m_request.expiration,clrNONE) #endif ); Print(DFUN); } //+------------------------------------------------------------------+
Die Methode erhält die Ticketnummer der zu ändernden Order, neue Preise, auch für StopLoss, TakeProfit und StopLimit-Order sowie die Order-Verfallszeit und den Verfallstyp.
Alle Methoden verfügen über identische Prüfungen der an die Methode übergebenen Standardwerte. Alle Aktionen sind im Code kommentiert. Die Kommentare sind vom gleichen Typ, so dass es keinen Sinn macht, bei ihnen zu verweilen.
Damit ist die Erstellung der grundlegenden Funktionen der Basishandelsklasse abgeschlossen.
Da wir alle Handelsanforderungen in Bezug auf ein Symbol senden, wollen wir das Basishandelsobjekt in das Symbolobjekt platzieren und von außen zugänglich machen.
Öffnen Sie die Datei des Symbolobjekts \MQL5\Einschließen\DoEasy\Objekte\Symbole\Symbol.mqh und binden Sie das Handelsobjekt TradeObj.mqh ein:
//+------------------------------------------------------------------+ //| Symbol.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" #property strict // Notwendig für mql4 //+------------------------------------------------------------------+ //| Include-Dateien | //+------------------------------------------------------------------+ #include "..\BaseObj.mqh" #include "..\Trade\TradeObj.mqh" //+------------------------------------------------------------------+
In der 'private' Sektion deklarieren Sie das variable Objekt der Handelsklasse:
//+------------------------------------------------------------------+ //| Abstract symbol class | //+------------------------------------------------------------------+ class CSymbol : public CBaseObj { private: struct MqlMarginRate { double Initial; // initial margin rate double Maintenance; // maintenance margin rate }; struct MqlMarginRateMode { MqlMarginRate Long; // MarginRate of long positions MqlMarginRate Short; // MarginRate of short positions MqlMarginRate BuyStop; // MarginRate of BuyStop orders MqlMarginRate BuyLimit; // MarginRate of BuyLimit orders MqlMarginRate BuyStopLimit; // MarginRate of BuyStopLimit orders MqlMarginRate SellStop; // MarginRate of SellStop orders MqlMarginRate SellLimit; // MarginRate of SellLimit orders MqlMarginRate SellStopLimit; // MarginRate of SellStopLimit orders }; MqlMarginRateMode m_margin_rate; // Margin ratio structure MqlBookInfo m_book_info_array[]; // Array of the market depth data structures long m_long_prop[SYMBOL_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[SYMBOL_PROP_STRING_TOTAL]; // String properties bool m_is_change_trade_mode; // Flag of changing trading mode for a symbol CTradeObj m_trade; // Trading class object //--- Return the index of the array the symbol's (1) double and (2) string properties are located at int IndexProp(ENUM_SYMBOL_PROP_DOUBLE property) const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_SYMBOL_PROP_STRING property) const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL; } //--- (1) Fill in all the "margin ratio" symbol properties, (2) initialize the ratios bool MarginRates(void); void InitMarginRates(void); //--- Reset all symbol object data void Reset(void); //--- Return the current day of the week ENUM_DAY_OF_WEEK CurrentDayOfWeek(void) const; public: //--- Standardmäßiger Konstruktor
Deklarieren Sie die beiden Methoden im 'public' Teil der Klasse:
die Methode, die die korrekte Ausführungspolitik und die Methode, die den korrekten Verfallstyp der Ordnung zurückgibt:
public: //--- Set (1) integer, (2) real and (3) string symbol properties void SetProperty(ENUM_SYMBOL_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_SYMBOL_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_SYMBOL_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return (1) integer, (2) real and (3) string symbol properties from the properties array long GetProperty(ENUM_SYMBOL_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_SYMBOL_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_SYMBOL_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of a symbol supporting the property virtual bool SupportProperty(ENUM_SYMBOL_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_SYMBOL_PROP_STRING property) { return true; } //--- Return the flag of allowing (1) market, (2) limit, (3) stop (4) and stop limit orders, //--- the flag of allowing setting (5) StopLoss and (6) TakeProfit orders, (7) as well as closing by an opposite order bool IsMarketOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_MARKET)==SYMBOL_ORDER_MARKET); } bool IsLimitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_LIMIT)==SYMBOL_ORDER_LIMIT); } bool IsStopOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_STOP)==SYMBOL_ORDER_STOP); } bool IsStopLimitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_STOP_LIMIT)==SYMBOL_ORDER_STOP_LIMIT); } bool IsStopLossOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_SL)==SYMBOL_ORDER_SL); } bool IsTakeProfitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_TP)==SYMBOL_ORDER_TP); } bool IsCloseByOrdersAllowed(void) const; //--- Return the (1) FOK and (2) IOC filling flag bool IsFillingModeFOK(void) const { return((this.FillingModeFlags() & SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK); } bool IsFillingModeIOC(void) const { return((this.FillingModeFlags() & SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC); } //--- Return the flag of order expiration: (1) GTC, (2) DAY, (3) Specified and (4) Specified Day bool IsExpirationModeGTC(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_GTC)==SYMBOL_EXPIRATION_GTC); } bool IsExpirationModeDAY(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_DAY)==SYMBOL_EXPIRATION_DAY); } bool IsExpirationModeSpecified(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED)==SYMBOL_EXPIRATION_SPECIFIED); } bool IsExpirationModeSpecifiedDay(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED_DAY)==SYMBOL_EXPIRATION_SPECIFIED_DAY); } //--- Return the description of allowing (1) market, (2) limit, (3) stop and (4) stop limit orders, //--- the description of allowing (5) StopLoss and (6) TakeProfit orders, (7) as well as closing by an opposite order string GetMarketOrdersAllowedDescription(void) const; string GetLimitOrdersAllowedDescription(void) const; string GetStopOrdersAllowedDescription(void) const; string GetStopLimitOrdersAllowedDescription(void) const; string GetStopLossOrdersAllowedDescription(void) const; string GetTakeProfitOrdersAllowedDescription(void) const; string GetCloseByOrdersAllowedDescription(void) const; //--- Return the description of allowing the filling type (1) FOK and (2) IOC, (3) as well as allowed order expiration modes string GetFillingModeFOKAllowedDescrioption(void) const; string GetFillingModeIOCAllowedDescrioption(void) const; //--- Return the description of order expiration: (1) GTC, (2) DAY, (3) Specified and (4) Specified Day string GetExpirationModeGTCDescription(void) const; string GetExpirationModeDAYDescription(void) const; string GetExpirationModeSpecifiedDescription(void) const; string GetExpirationModeSpecDayDescription(void) const; //--- Return the description of the (1) status, (2) price type for constructing bars, //--- (3) method of calculating margin, (4) instrument trading mode, //--- (5) deal execution mode for a symbol, (6) swap calculation mode, //--- (7) StopLoss and TakeProfit lifetime, (8) option type, (9) option rights //--- flags of (10) allowed order types, (11) allowed filling types, //--- (12) allowed order expiration modes string GetStatusDescription(void) const; string GetChartModeDescription(void) const; string GetCalcModeDescription(void) const; string GetTradeModeDescription(void) const; string GetTradeExecDescription(void) const; string GetSwapModeDescription(void) const; string GetOrderGTCModeDescription(void) const; string GetOptionTypeDescription(void) const; string GetOptionRightDescription(void) const; string GetOrderModeFlagsDescription(void) const; string GetFillingModeFlagsDescription(void) const; string GetExpirationModeFlagsDescription(void) const; //--- Return (1) execution type, (2) order expiration type equal to 'type' if it is available on a symbol, otherwise - the correct option ENUM_ORDER_TYPE_FILLING GetCorrectTypeFilling(const uint type=ORDER_FILLING_RETURN); ENUM_ORDER_TYPE_TIME GetCorrectTypeExpiration(uint expiration=ORDER_TIME_GTC); //+------------------------------------------------------------------+
Lasst uns ihre Implementierung außerhalb des Klassenkörpers schreiben:
//+------------------------------------------------------------------+ //| Return an order expiration type equal to 'type', | //| if it is available on a symbol, otherwise, the correct option | //| https://www.mql5.com/ru/forum/170952/page4#comment_4128864 | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE_FILLING CSymbol::GetCorrectTypeFilling(const uint type=ORDER_FILLING_RETURN) { const ENUM_SYMBOL_TRADE_EXECUTION exe_mode=this.TradeExecutionMode(); const int filling_mode=this.FillingModeFlags(); return( (filling_mode == 0 || (type >= ORDER_FILLING_RETURN) || ((filling_mode & (type + 1)) != type + 1)) ? (((exe_mode == SYMBOL_TRADE_EXECUTION_EXCHANGE) || (exe_mode == SYMBOL_TRADE_EXECUTION_INSTANT)) ? ORDER_FILLING_RETURN : ((filling_mode == SYMBOL_FILLING_IOC) ? ORDER_FILLING_IOC : ORDER_FILLING_FOK)) : (ENUM_ORDER_TYPE_FILLING)type ); } //+------------------------------------------------------------------+ //| Return order expiration type equal to 'expiration' | //| if it is available on Symb symbol, otherwise - the correct option| //| https://www.mql5.com/en/forum/170952/page4#comment_4128871 | //| Application: | //| Request.type_time = GetExpirationType((uint)Expiration); | //| 'Expiration' can be datetime | //| if(Expiration > ORDER_TIME_DAY) Request.expiration = Expiration; | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE_TIME CSymbol::GetCorrectTypeExpiration(uint expiration=ORDER_TIME_GTC) { #ifdef __MQL5__ const int expiration_mode=this.ExpirationModeFlags(); if((expiration > ORDER_TIME_SPECIFIED_DAY) || (((expiration_mode >> expiration) & 1) == 0)) { if((expiration < ORDER_TIME_SPECIFIED) || (expiration_mode < SYMBOL_EXPIRATION_SPECIFIED)) expiration=ORDER_TIME_GTC; else if(expiration > ORDER_TIME_DAY) expiration=ORDER_TIME_SPECIFIED; uint i=1 << expiration; while((expiration <= ORDER_TIME_SPECIFIED_DAY) && ((expiration_mode & i) != i)) { i <<= 1; expiration++; } } #endif return (ENUM_ORDER_TYPE_TIME)expiration; } //+------------------------------------------------------------------+
Um das Rad nicht neu zu erfinden, habe ich die Methodenlogik verwendet, die durch das Forumsmitglied fxsaber beschrieben wurde. Die Code-Header enthalten Links zu den Beiträgen, die die Codes enthalten.
Alle Feinheiten dieser Logik zu verstehen, ist nicht die einfachste Erfahrung, aber da ich denjenigen kannte, der die Funktionen als vertrauenswürdiger Entwickler veröffentlicht hat, entschied ich, dass ich mich auf ihn verlassen kann. Natürlich ist es möglich, die gesamte Logik der Methoden in einzelne Elemente zu zerlegen, umfangreiche Methoden zu erhalten und ihre gesamte Logik zu beschreiben. Aber es ist einfacher, Beschreibungen von Methoden zu implementieren:
Die Methoden erhalten die notwendige Ausführungspolitik und die Art des Auftragsablaufs. Wenn ein Symbol diese Richtlinie oder diesen Typ unterstützt, wird es zurückgegeben. Wenn die notwendigen Modi von einem Symbol nicht unterstützt werden, werden die erlaubten Modi zurückgegeben. Somit geben die Methoden immer unterstützte Modi zurück — korrekte Befüllungsrichtlinie oder Auftragsablaufmodi.
Im Block mit den Methoden eines vereinfachten Zugriffs auf die ganzzahligen Eigenschaften des Symbolobjekts der öffentlichen Sektion, Hinzufügen Deklaration der Methode, die ein normiertes Los zurückgibt:
//+------------------------------------------------------------------+ //| Methods of a simplified access to the order object properties | //+------------------------------------------------------------------+ //--- Integer properties long Status(void) const { return this.GetProperty(SYMBOL_PROP_STATUS); } int IndexInMarketWatch(void) const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW); } bool IsCustom(void) const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM); } color ColorBackground(void) const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR); } ENUM_SYMBOL_CHART_MODE ChartMode(void) const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE); } bool IsExist(void) const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST); } bool IsExist(const string name) const { return this.SymbolExists(name); } bool IsSelect(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT); } bool IsVisible(void) const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE); } long SessionDeals(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS); } long SessionBuyOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS); } long SessionSellOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS); } long Volume(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME); } long VolumeHigh(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH); } long VolumeLow(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW); } datetime Time(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME); } int Digits(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS); } int DigitsLot(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS); } int Spread(void) const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD); } bool IsSpreadFloat(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT); } int TicksBookdepth(void) const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH); } ENUM_SYMBOL_CALC_MODE TradeCalcMode(void) const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE); } ENUM_SYMBOL_TRADE_MODE TradeMode(void) const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE); } datetime StartTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME); } datetime ExpirationTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME); } int TradeStopLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL); } int TradeFreezeLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL); } ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void) const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE); } ENUM_SYMBOL_SWAP_MODE SwapMode(void) const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE); } ENUM_DAY_OF_WEEK SwapRollover3Days(void) const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS); } bool IsMarginHedgedUseLeg(void) const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG); } int ExpirationModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE); } int FillingModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE); } int OrderModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE); } ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void) const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE); } ENUM_SYMBOL_OPTION_MODE OptionMode(void) const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE); } ENUM_SYMBOL_OPTION_RIGHT OptionRight(void) const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT); } //--- Real properties double Bid(void) const { return this.GetProperty(SYMBOL_PROP_BID); } double BidHigh(void) const { return this.GetProperty(SYMBOL_PROP_BIDHIGH); } double BidLow(void) const { return this.GetProperty(SYMBOL_PROP_BIDLOW); } double Ask(void) const { return this.GetProperty(SYMBOL_PROP_ASK); } double AskHigh(void) const { return this.GetProperty(SYMBOL_PROP_ASKHIGH); } double AskLow(void) const { return this.GetProperty(SYMBOL_PROP_ASKLOW); } double Last(void) const { return this.GetProperty(SYMBOL_PROP_LAST); } double LastHigh(void) const { return this.GetProperty(SYMBOL_PROP_LASTHIGH); } double LastLow(void) const { return this.GetProperty(SYMBOL_PROP_LASTLOW); } double VolumeReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); } double VolumeHighReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL); } double VolumeLowReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL); } double OptionStrike(void) const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE); } double Point(void) const { return this.GetProperty(SYMBOL_PROP_POINT); } double TradeTickValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE); } double TradeTickValueProfit(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT); } double TradeTickValueLoss(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS); } double TradeTickSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE); } double TradeContractSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE); } double TradeAccuredInterest(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST); } double TradeFaceValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE); } double TradeLiquidityRate(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE); } double LotsMin(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); } double LotsMax(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); } double LotsStep(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); } double VolumeLimit(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); } double SwapLong(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG); } double SwapShort(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT); } double MarginInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL); } double MarginMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE); } double MarginLongInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL); } double MarginBuyStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL); } double MarginBuyLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL); } double MarginBuyStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL); } double MarginLongMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE); } double MarginBuyStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE); } double MarginBuyLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE); } double MarginBuyStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE); } double MarginShortInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL); } double MarginSellStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL); } double MarginSellLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL); } double MarginSellStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL); } double MarginShortMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE); } double MarginSellStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE); } double MarginSellLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE); } double MarginSellStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE); } double SessionVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME); } double SessionTurnover(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER); } double SessionInterest(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST); } double SessionBuyOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); } double SessionSellOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); } double SessionOpen(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); } double SessionClose(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); } double SessionAW(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_AW); } double SessionPriceSettlement(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); } double SessionPriceLimitMin(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); } double SessionPriceLimitMax(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); } double MarginHedged(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); } double NormalizedPrice(const double price) const; double NormalizedLot(const double volume) const; double BidLast(void) const; double BidLastHigh(void) const; double BidLastLow(void) const; //--- String properties
Ganz am Ende des Klassenkörpers, fügen wir die Methode, die ein Handelsobjekt zurückgibt, das zu einem Symbolobjekt gehört hinzu:
//--- The average weighted session price //--- setting the controlled session average weighted price (1) increase, (2) decrease and (3) control value //--- getting (4) the change value of the average weighted session price, //--- getting the flag of the average weighted session price change exceeding the (5) increase, (6) decrease value void SetControlSessionPriceAWInc(const double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_AW,::fabs(value)); } void SetControlSessionPriceAWDec(const double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_AW,::fabs(value)); } void SetControlSessionPriceAWLevel(const double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_AW,::fabs(value)); } double GetValueChangedSessionPriceAW(void) const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_AW); } bool IsIncreasedSessionPriceAW(void) const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_AW); } bool IsDecreasedSessionPriceAW(void) const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_AW); } //--- Return a trading object CTradeObj *GetTradeObj(void) { return &this.m_trade; } }; //+------------------------------------------------------------------+
Da ein Handelsobjekt sofort beim Anlegen eines Symbolobjektes generiert wird und das Handelsobjekt von allen seinen Feldern initialisierende Startwerte hat, sollte es mit den notwendigen Defaultwerten initialisiert werden. Um dies zu erreichen, rufen Sie ganz am Ende des CSymbol-Klassenkonstruktors die Methode Init() des Handelsobjekts mit den erforderlichen Standardwerten auf:
//--- Fill in the symbol current data for(int i=0;i<SYMBOL_PROP_INTEGER_TOTAL;i++) this.m_long_prop_event[i][3]=this.m_long_prop[i]; for(int i=0;i<SYMBOL_PROP_DOUBLE_TOTAL;i++) this.m_double_prop_event[i][3]=this.m_double_prop[i]; //--- Update the base object data and search for changes CBaseObj::Refresh(); //--- if(!select) this.RemoveFromMarketWatch(); //--- Initializing default values of a trading object this.m_trade.Init(this.Name(),0,this.LotsMin(),5,0,0,false,this.GetCorrectTypeFilling(),this.GetCorrectTypeExpiration(),LOG_LEVEL_ERROR_MSG); } //+------------------------------------------------------------------+
Beim Aufruf der Methode des Handelsobjekts wird übergeben:
- ein Symbolname,
- das zulässige Mindestvolumen des Symbols,
- Slippage von fünf Punkten,
- ein StopLoss von Null — kein StopLoss,
- ein TakeProfit von Null — kein TakeProfit,
- das Flag für asynchrones Senden von Handelsaufträgen als false — synchrones Senden,
- nach dem sofortigen Erhalt der richtigen Orderausführungspolitik soll diese für das Handelsobjekt ein getragen werden,
- den korrekten Modus der Order-Lebensdauer erhalten und für ein Handelsobjekt einstellen,
- die Protokollierungsstufe der Handelsmethoden auf "nur Fehler" einstellen.
Diese Werte sind standardmäßig für ein Handelsobjekt gesetzt, können aber jederzeit über die oben beschriebenen Set-Methoden für jede der Eigenschaften einzeln geändert werden. Alternativ können Sie die Standardwerte belassen, aber beim Aufruf einer Handelsmethode dann einen weiteren Parameter an diese übergeben, so dass sie beim Senden der Anfrage an den Server einmalig verwendet wird.
Implementieren Sie die Lot-Normalisierungsmethode außerhalb des Klassenkörpers:
//+------------------------------------------------------------------+ //| Return a normalized lot considering symbol properties | //+------------------------------------------------------------------+ double CSymbol::NormalizedLot(const double volume) const { double ml=this.LotsMin(); double mx=this.LotsMax(); double ln=::NormalizeDouble(volume,this.DigitsLot()); return(ln<ml ? ml : ln>mx ? mx : ln); } //+------------------------------------------------------------------+
Die Methode erhält den für die Normalisierung erforderlichen Losgröße bzw. Lotzahl. Als Nächstes erhalten wir die minimal und maximal zulässige Losgröße für das Symbol, normalisieren den an die Methode übergebenen Loswert und definieren den zurückzugebenden Wert, indem wir einfach den normalisierten Wert mit der minimalen und maximalen Losgröße vergleichen. Wenn die an die Methode übergebene Losgröße kleiner oder größer als das Min/Max-Los des Symbols ist, geben Sie das Min/Max-Los entsprechend zurück. Andernfalls geben Sie das normalisierte Lot unter Berücksichtigung der Anzahl der Dezimalstellen im Lot-Wert zurück (Methode DigitsLot().
Wir haben die Klasse CSymbol verbessert.
Jetzt müssen wir die Handelsmethoden testen. Da wir noch keine Basis-Handelsklasse haben, werden wir vorübergehend Methoden zur Basisobjektklasse CEngine der Bibliothek hinzufügen, um auf ein Handelsobjekt eines notwendigen Symbols zuzugreifen. Da wir hier vollen Zugriff auf alle wichtigen Bibliotheksbestände haben, werden wir hier die Methoden zum Testen des Handelsobjekts platzieren.
Beachten Sie, dass die Methoden in der Klasse vorläufig sind. Später werden wir eine vollwertige Handelsklasse implementieren, in der alle Methoden für die Prüfung von Werten und den Handel untergebracht werden sollen.
Alle Methoden, die derzeit für den Test des Handelsobjekts erforderlich sind, werden in den öffentlichen Bereich der Klasse CEngine aufgenommen:
//--- Set the following for the trading classes: //--- (1) correct filling policy, (2) filling policy, //--- (3) correct order expiration type, (4) order expiration type, //--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date, //--- (10) the flag of asynchronous sending of a trading request, (11) logging level void SetTradeCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL); void SetTradeTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL); void SetTradeCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL); void SetTradeTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL); void SetTradeMagic(const ulong magic,const string symbol_name=NULL); void SetTradeComment(const string comment,const string symbol_name=NULL); void SetTradeDeviation(const ulong deviation,const string symbol_name=NULL); void SetTradeVolume(const double volume=0,const string symbol_name=NULL); void SetTradeExpiration(const datetime expiration=0,const string symbol_name=NULL); void SetTradeAsyncMode(const bool mode=false,const string symbol_name=NULL); void SetTradeLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol_name=NULL); //--- Return a symbol trading object by (1) position, (2) order ticket CTradeObj *GetTradeObjByPosition(const ulong ticket); CTradeObj *GetTradeObjByOrder(const ulong ticket); //--- Open (1) Buy, (2) Sell position bool OpenBuy(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL); bool OpenSell(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL); //--- Modify a position bool ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE); //--- Close a position (1) fully, (2) partially, (3) by an opposite one bool ClosePosition(const ulong ticket); bool ClosePositionPartially(const ulong ticket,const double volume); bool ClosePositionBy(const ulong ticket,const ulong ticket_by); //--- Set (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit pending order bool PlaceBuyStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceBuyLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceBuyStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); //--- Set (1) SellStop, (2) SellLimit, (3) SellStopLimit pending order bool PlaceSellStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceSellLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceSellStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); //--- Modify a pending order bool ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double stoplimit=WRONG_VALUE, datetime expiration=WRONG_VALUE, ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE); //--- Remove a pending order bool DeleteOrder(const ulong ticket); //--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value ushort EventMSC(const long lparam) const { return this.LongToUshortFromByte(lparam,0); } ushort EventReason(const long lparam) const { return this.LongToUshortFromByte(lparam,1); } ushort EventSource(const long lparam) const { return this.LongToUshortFromByte(lparam,2); } //--- Constructor/Destructor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
Implementieren wir die deklarierten Methoden außerhalb des Klassenkörpers.
Die Methode zur Eröffnung einer Kaufposition:
//+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ bool CEngine::OpenBuy(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.OpenPosition(POSITION_TYPE_BUY,volume,sl,tp,magic,trade_obj.GetDeviation(),comment); } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Volumen einer geöffneten Position (erforderlich),
- das Symbol, mit dem die Position geöffnet werden soll (erforderlich),
- die Magicnummer, die einer eröffneten Position zugeordnet werden kann (der Standard ist 0),
- StopLoss (nicht standardmäßig eingestellt),
- TakeProfit (nicht standardmäßig eingestellt),
- einen Positionskommentar (die Vorgabe ist Programmname+" by DoEasy")
Holen wir uns ein Symbolobjekt durch einen Symbolnamen. Wenn wir das Objekt nicht erhalten haben, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Holen wir uns das Handelsobjekt von einem Symbolobjekt. Wenn wir das Objekt nicht bekommen haben, wird die entsprechende Meldung angezeigt und false zurückgegeben.
Rückgabe des Operationsergebnisses der oben betrachteten Methode zur Eröffnung einer Handelsobjektposition.
Die Methode zur Eröffnung einer Verkaufsposition:
//+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ bool CEngine::OpenSell(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.OpenPosition(POSITION_TYPE_SELL,volume,sl,tp,magic,trade_obj.GetDeviation(),comment); } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Volumen einer geöffneten Position (erforderlich),
- das Symbol, mit dem die Position geöffnet werden soll (erforderlich),
- die Magicnummer, die einer eröffneten Position zugeordnet werden kann (der Standard ist 0),
- StopLoss (nicht standardmäßig eingestellt),
- TakeProfit (nicht standardmäßig eingestellt),
- einen Positionskommentar (die Vorgabe ist Programmname+" by DoEasy")
Holen wir uns ein Symbolobjekt durch einen Symbolnamen. Wenn wir das Objekt nicht erhalten haben, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Holen wir uns das Handelsobjekt von einem Symbolobjekt. Wenn wir das Objekt nicht bekommen haben, wird die entsprechende Meldung angezeigt und false zurückgegeben.
Rückgabe des Operationsergebnisses der oben betrachteten Methode zur Eröffnung einer Handelsobjektposition.
Die Methode für die Änderung von StopLoss und TakeProfit einer Position:
//+------------------------------------------------------------------+ //| Modify a position | //+------------------------------------------------------------------+ bool CEngine::ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ModifyPosition(ticket,sl,tp); } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Ticket der zu modifizierenden Position (erforderlich),
- neuer StopLoss (der Standard ist keine Änderungen),
- neuer TakeProfit (der Standard ist keine Änderungen).
Holen Sie das Handelsobjekt durch ein Positionsticket mit der Methode GetTradeObjByPosition(), siehe unten.
Falls das Objekt nicht erhalten wurde, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Rückgabe des Operationsergebnisses der oben betrachteten Methode zur Modifikation einer Handelsobjektposition.
Die Methode zum Schließen der ganzen Position:
//+------------------------------------------------------------------+ //| Close a position in full | //+------------------------------------------------------------------+ bool CEngine::ClosePosition(const ulong ticket) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ClosePosition(ticket); } //+------------------------------------------------------------------+
Die Methode erhält das Ticket einer zu schließenden Position.
Holen Sie das Handelsobjekt durch ein Positionsticket mit der Methode GetTradeObjByPosition(), siehe unten.
Falls das Objekt nicht erhalten wurde, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Rückgabe des Operationsergebnisses der oben betrachteten Methode zum Schließen einer Handelsobjektposition.
Die Methode für das teilweise Schließen von Positionen:
//+------------------------------------------------------------------+ //| Close a position partially | //+------------------------------------------------------------------+ bool CEngine::ClosePositionPartially(const ulong ticket,const double volume) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } CSymbol *symbol=this.GetSymbolObjByName(trade_obj.GetSymbol()); return trade_obj.ClosePositionPartially(ticket,symbol.NormalizedLot(volume)); } //+------------------------------------------------------------------+
Die Methode erhält das Ticket und das zu schließenden Volumen.
Holen Sie das Handelsobjekt durch ein Positionsticket mit der Methode GetTradeObjByPosition(), siehe unten.
Falls das Objekt nicht erhalten wurde, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Holen Sie sich das Symbolobjekt durch den Symbolname eines Handelsobjekts.
Rückgabe des Operationsergebnisses der oben betrachteten Methode zur teilweisen Schließung einer Handelsobjektposition. Der Methode wird ein normiertes geschlossenes Volumen übergeben.
Die Methode, um eine Position durch eine entgegengesetzte zu schließen:
//+------------------------------------------------------------------+ //| Close a position by an opposite one | //+------------------------------------------------------------------+ bool CEngine::ClosePositionBy(const ulong ticket,const ulong ticket_by) { CTradeObj *trade_obj_pos=this.GetTradeObjByPosition(ticket); if(trade_obj_pos==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } CTradeObj *trade_obj_by=this.GetTradeObjByPosition(ticket_by); if(trade_obj_by==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj_pos.ClosePositionBy(ticket,ticket_by); } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Ticket der zu schließenden Position,
- ein Ticket einer entgegengesetzten Position
Holen Sie sich das Handelsobjekt durch das Ticket einer Position mit GetTradeObjByPosition(), siehe unten.
Falls das Objekt nicht erhalten wurde, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Holen Sie das Handelsobjekt durch das Ticket einer Entgegengesetzten Position mit Hilfe der unten besprochenen GetTradeObjByPosition().
Falls das Objekt nicht erhalten wurde, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Rückgabe des Operationsergebnisses der Methode zum Schließen einer Handelsobjektposition durch eine entgegengesetzte Position.
Die Methode für die Platzierung der Pending-Order BuyStop:
//+------------------------------------------------------------------+ //| Place BuyStop pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_STOP,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Volumen der zu platzierenden Order (erforderlich)
- das Symbol der Order (erforderlich),
- Prei der Order (erforderlich),
- StopLoss (nicht standardmäßig eingestellt),
- TakeProfit (nicht standardmäßig eingestellt),
- die Magicnummer der zu platzierenden Order (standardmäßig 0),
- einen Kommentar der Order (die Vorgabe ist Programmname+" by DoEasy")
- die Verfallszeit der Order (standardmäßig unbegrenzt),
- der Verfallstyp der Order (standardmäßig bis zur expliziten Löschung)
Holen wir uns ein Symbolobjekt durch einen Symbolnamen. Wenn wir das Objekt nicht erhalten haben, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Holen wir uns das Handelsobjekt von einem Symbolobjekt. Wenn wir das Objekt nicht bekommen haben, wird die entsprechende Meldung angezeigt und false zurückgegeben.
Rückgabe des Operationsergebnisses der Methode zum Platzieren einer Pending-Order eines Handelsobjekts, siehe oben.
Die Methode für die Platzierung der Pending-Order BuyLimit:
//+------------------------------------------------------------------+ //| Place BuyLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_LIMIT,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Volumen der zu platzierenden Order (erforderlich)
- das Symbol der Order (erforderlich),
- Prei der Order (erforderlich),
- StopLoss (nicht standardmäßig eingestellt),
- TakeProfit (nicht standardmäßig eingestellt),
- die Magicnummer der zu platzierenden Order (standardmäßig 0),
- einen Kommentar der Order (die Vorgabe ist Programmname+" by DoEasy")
- die Verfallszeit der Order (standardmäßig unbegrenzt),
- der Verfallstyp der Order (standardmäßig bis zur expliziten Löschung)
Holen wir uns ein Symbolobjekt durch einen Symbolnamen. Wenn wir das Objekt nicht erhalten haben, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Holen wir uns das Handelsobjekt von einem Symbolobjekt. Wenn wir das Objekt nicht bekommen haben, wird die entsprechende Meldung angezeigt und false zurückgegeben.
Rückgabe des Operationsergebnisses der Methode zum Platzieren einer Pending-Order eines Handelsobjekts, siehe oben.
Die Methode für die Platzierung der Pending-Order BuyStopLimit:
//+------------------------------------------------------------------+ //| Place BuyStopLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { #ifdef __MQL5__ CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_STOP_LIMIT,volume,price_stop,sl,tp,price_limit,magic,expiration,type_time,comment); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Volumen der zu platzierenden Order (erforderlich)
- das Symbol der Order (erforderlich),
- der Preis für BuyStop (erforderlich),
- der Preis der BuyLimit-Order nach der Aktivierung der BuyStop-Order (erforderlich),
- StopLoss (nicht standardmäßig eingestellt),
- TakeProfit (nicht standardmäßig eingestellt),
- die Magicnummer der zu platzierenden Order (standardmäßig 0),
- einen Kommentar der Order (die Vorgabe ist Programmname+" by DoEasy")
- die Verfallszeit der Order (standardmäßig unbegrenzt),
- der Verfallstyp der Order (standardmäßig bis zur expliziten Löschung)
Für MQL5:
Holen wir uns ein Symbolobjekt durch einen Symbolnamen. Wenn wir das Objekt nicht erhalten haben, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Holen wir uns das Handelsobjekt von einem Symbolobjekt. Wenn wir das Objekt nicht bekommen haben, wird die entsprechende Meldung angezeigt und false zurückgegeben.
Rückgabe des Operationsergebnisses der Methode zum Platzieren einer Pending-Order eines Handelsobjekts, siehe oben.
Für MQL4:
Nichts zu tum — Rückgabe von true.
Die Methoden zur Platzierung der Pending-Orders SellStop, SellLimit und SellStopLimit:
//+------------------------------------------------------------------+ //| Place SellStop pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceSellStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_STOP,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+ //| Place SellLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceSellLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_LIMIT,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+ //| Place SellStopLimit pending order | //+------------------------------------------------------------------+ bool CEngine::PlaceSellStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { #ifdef __MQL5__ CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_STOP_LIMIT,volume,price_stop,sl,tp,price_limit,magic,expiration,type_time,comment); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+
Diese Methoden sind ähnlich denen, die die Pending-Orders für einen Kauf platzieren.
Die Methode zur Änderung einer Pending-Order:
//+------------------------------------------------------------------+ //| Modify a pending order | //+------------------------------------------------------------------+ bool CEngine::ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double stoplimit=WRONG_VALUE, datetime expiration=WRONG_VALUE, ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE) { CTradeObj *trade_obj=this.GetTradeObjByOrder(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ModifyOrder(ticket,price,sl,tp,stoplimit,expiration,type_time); } //+------------------------------------------------------------------+
Der Methode wird übergeben:
- das Ticket der zu modifizierenden Order (erforderlich),
- der neue Preis der Pending-Order (der Standard ist keine Änderungen),
- der neue Preis für StopLoss der Order (der Standard ist keine Änderungen),
- der neue Preis für TakeProfit der Order (der Standard ist keine Änderungen),
- er neue Preis für StopLimit der Order (der Standard ist keine Änderungen),
- der neue Zeitpunkt der Verfallszeit einer Pending-Order (der Standard ist keine Änderung),
- der neue Modus der Verfallszeit einer Pending Order (der Standard ist keine Änderungen).
Holen Sie das Handelsobjekt mit dem Ticket der zu modifizierenden Order mit der Methode GetTradeObjByOrder(), siehe unten.
Falls das Objekt nicht erhalten wurde, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Rückgabe des Operationsergebnisses der Methode zur Änderung der Pending-Order des Handelsobjektes wie oben besprochen.
Die Methode zum Entfernen einer Pending-Order:
//+------------------------------------------------------------------+ //| Remove a pending order | //+------------------------------------------------------------------+ bool CEngine::DeleteOrder(const ulong ticket) { CTradeObj *trade_obj=this.GetTradeObjByOrder(ticket); if(trade_obj==NULL) { //--- Error. Failed to get trading object ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.DeleteOrder(ticket); } //+------------------------------------------------------------------+
Die Methode erhält das Ticket einer zu löschenden Order.
Holen Sie ein Handelsobjekt durch ein Order-Ticket mit der unten besprochenen Methode GetTradeObjByOrder().
Falls das Objekt nicht erhalten wurde, zeigen Sie die entsprechende Meldung an und geben Sie false zurück.
Rückgabe des Operationsergebnisses der Methode zum Entfernen der Pending-Order des Handelsobjekts wie oben besprochen.
Die Methoden, die das Handelssymbol des Objekts einer Position und das Order-Ticket zurückgeben:
//+------------------------------------------------------------------+ //| Return a symbol trading object by a position ticket | //+------------------------------------------------------------------+ CTradeObj *CEngine::GetTradeObjByPosition(const ulong ticket) { //--- Get the list of open positions CArrayObj *list=this.GetListMarketPosition(); //--- If failed to get the list of open positions, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_MARKET_POS_LIST)); return NULL; } //--- If the list is empty (no open positions), display the message and return NULL if(list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_ENG_NO_OPEN_POSITIONS)); return NULL; } //--- Sort the list by a ticket list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); //--- If failed to get the list of open positions, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_MARKET_POS_LIST)); return NULL; } //--- If the list is empty (no required ticket), display the message and return NULL if(list.Total()==0) { //--- Error. No open position with #ticket ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_NO_OPEN_POSITION_WITH_TICKET),(string)ticket); return NULL; } //--- Get a position with #ticket from the obtained list COrder *pos=list.At(0); //--- If failed to get the position object, display the message and return NULL if(pos==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_POS_OBJ)); return NULL; } //--- Get a symbol object by name CSymbol * symbol_obj=this.GetSymbolObjByName(pos.Symbol()); //--- If failed to get the symbol object, display the message and return NULL if(symbol_obj==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return NULL; } //--- Get and return the trading object from the symbol object CTradeObj *obj=symbol_obj.GetTradeObj(); return obj; } //+------------------------------------------------------------------+ //| Return a symbol trading object by an order ticket | //+------------------------------------------------------------------+ CTradeObj *CEngine::GetTradeObjByOrder(const ulong ticket) { //--- Get the list of placed orders CArrayObj *list=this.GetListMarketPendings(); //--- If failed to get the list of placed orders, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_PENDING_ORD_LIST)); return NULL; } //--- If the list is empty (no placed orders), display the message and return NULL if(list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_ENG_NO_PLACED_ORDERS)); return NULL; } //--- Sort the list by a ticket list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); //--- If failed to get the list of placed orders, display the message and return NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_PENDING_ORD_LIST)); return NULL; } //--- If the list is empty (no required ticket), display the message and return NULL if(list.Total()==0) { //--- Error. No placed order with #ticket ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_NO_PLACED_ORDER_WITH_TICKET),(string)ticket); return NULL; } //--- Get an order with #ticket from the obtained list COrder *ord=list.At(0); //--- If failed to get an object order, display the message and return NULL if(ord==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ)); return NULL; } //--- Get a symbol object by name CSymbol *symbol_obj=this.GetSymbolObjByName(ord.Symbol()); //--- If failed to get the symbol object, display the message and return NULL if(symbol_obj==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return NULL; } //--- Get and return the trading object from the symbol object CTradeObj *obj=symbol_obj.GetTradeObj(); return obj; } //+--------------------------------------------------------------------------------------+
Beide Methoden sind fast identisch, außer dass in der ersten Methode , wir die Liste aller offenen Positionen erhalten, während wir durch die zweite Methode die Liste aller platzierten Pending-Orders erhalten. Die übrige Logik ist für beide Methoden völlig identisch und in den Codekommentaren kommentiert.
Die Methoden für das Setzen von Ausführungsart und der gültige Ausführungsart in Handelsobjekten aller Symbole, die sich in der Kollektionsliste der Symbole befinden, oder für ein einzelnes spezifiziertes Symbol:
//+------------------------------------------------------------------+ //| Set the valid filling policy | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL) { //--- Declare the empty pointer to a symbol object CSymbol *symbol=NULL; //--- If a symbol name passed in the method inputs is not set, specify a filling policy for all symbols if(symbol_name==NULL) { //--- get the list of all used symbols CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); //--- In a loop by the list of symbol objects for(int i=0;i<total;i++) { //--- get the next symbol object symbol=list.At(i); if(symbol==NULL) continue; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; //--- set correct filling policy to the trading object (the default is "fill or kill") obj.SetTypeFilling(symbol.GetCorrectTypeFilling(type)); } } //--- If a symbol name is specified in the method inputs, set the filling policy only for the specified symbol else { //--- Get a symbol object by a symbol name symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; //--- set correct filling policy to the trading object (the default is "fill or kill") obj.SetTypeFilling(symbol.GetCorrectTypeFilling(type)); } } //+------------------------------------------------------------------+ //| Set the filling policy | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL) { //--- Declare the empty pointer to a symbol object CSymbol *symbol=NULL; //--- If a symbol name passed in the method inputs is not set, specify a filling policy for all symbols if(symbol_name==NULL) { //--- get the list of all used symbols CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); //--- In a loop by the list of symbol objects for(int i=0;i<total;i++) { //--- get the next symbol object symbol=list.At(i); if(symbol==NULL) continue; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; //--- for the trading object, set a filling policy passed to the method in the inputs (the default is "fill or kill") obj.SetTypeFilling(type); } } //--- If a symbol name is specified in the method inputs, set the filling policy only for the specified symbol else { //--- Get a symbol object by a symbol name symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; //--- get a trading object from a symbol object CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; //--- for the trading object, set a filling policy passed to the method in the inputs (the default is "fill or kill") obj.SetTypeFilling(type); } } //+------------------------------------------------------------------+
Die Methoden erhalten die Ausführungsart (die Vorgabe ist "fill or kill", "Alles oder Nichts") und ein Symbol (standardmäßig werden alle Symbole aus der Symbolsammlung verwendet).
Die Logik der Methoden wird in den Codekommentaren angezeigt und ist recht verständlich. Wenn Sie Fragen haben, können Sie diese gerne in den Kommentaren unten stellen.
Andere Methoden zum Setzen von Defaultwerten für Symbolhandelsobjekte haben die gleiche Logik und weisen keine Kommentare auf. In jedem Fall können Sie die Logik mit diesen beiden Methoden studieren.
Alle anderen Methoden zum Setzen von Standardwerten für Symbolhandelsobjekte:
//+------------------------------------------------------------------+ //| Set a correct order expiration type | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetTypeExpiration(symbol.GetCorrectTypeExpiration(type)); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetTypeExpiration(symbol.GetCorrectTypeExpiration(type)); } } //+------------------------------------------------------------------+ //| Set an order expiration type | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetTypeExpiration(type); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetTypeExpiration(type); } } //+------------------------------------------------------------------+ //| Set a magic number for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeMagic(const ulong magic,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetMagic(magic); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetMagic(magic); } } //+------------------------------------------------------------------+ //| Set a comment for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeComment(const string comment,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetComment(comment); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetComment(comment); } } //+------------------------------------------------------------------+ //| Set a slippage | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeDeviation(const ulong deviation,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetDeviation(deviation); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetDeviation(deviation); } } //+------------------------------------------------------------------+ //| Set a volume for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeVolume(const double volume=0,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetVolume(volume!=0 ? symbol.NormalizedLot(volume) : symbol.LotsMin()); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetVolume(volume!=0 ? symbol.NormalizedLot(volume) : symbol.LotsMin()); } } //+------------------------------------------------------------------+ //| Set an order expiration date | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeExpiration(const datetime expiration=0,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetExpiration(expiration); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetExpiration(expiration); } } //+------------------------------------------------------------------+ //| Set the flag of asynchronous sending of trading requests | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeAsyncMode(const bool mode=false,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetAsyncMode(mode); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetAsyncMode(mode); } } //+------------------------------------------------------------------+ //| Set a logging level of trading requests | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CEngine::SetTradeLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetLogLevel(log_level); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetLogLevel(log_level); } } //+------------------------------------------------------------------+
Wir haben alle temporären Hilfsmethoden in der Klasse CEngine für die Prüfung von Symbolhandelsobjekten vorbereitet.
Die bestehenden plattformübergreifenden Handelsmethoden (wenn auch noch in den Kinderschuhen) erlauben es uns, im Test EA eine bedingte Kompilierung für MQL5 oder MQL4 zu vermeiden. Nun bleiben alle Handelsfunktionen des Test-AA plattformübergreifend gleich. Weiterhin werden wir die Arbeit mit den Trading-Klassen der Bibliothek verbessern, um die gesamte Funktionalität für die effiziente Arbeit mit unseren Programmen zu erhalten.
Testen des Basisobjekts des Handels
Zum Testen von Symbol-Handelsobjekten werden wir den Test EA aus dem vorherigen Artikel verwenden und dessen Handelsfunktionen für die Arbeit mit Symbol-Handelsobjekten anpassen. Bitte beachten Sie, dass wir noch keine Überprüfung der Werte der Handelsanfrage haben, aber dies erlaubt uns, eine Antwort auf ungültige Parameter zu testen. Eine solche Antwort wird später implementiert werden.
Speichern Sie den EA in \MQL5\Experts\TestDoEasy\Part21\ unter dem Namen TestDoEasyPart21.mq5.
Zunächst wird die Aufnahme der Standardbibliothek CTrade-Handelsklasse und die Deklaration eines Handelsobjekts vom CTrade-Klassentyp entfernt:
//+------------------------------------------------------------------+ //| TestDoEasyPart20.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/de/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/de/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #ifdef __MQL5__ #include <Trade\Trade.mqh> #endif //--- Enumerationen enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) //--- Strukturen struct SDataButt { string name; string text; }; //--- Eingabeparameter input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) input ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list input string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) //--- Globale Variablen CEngine engine; #ifdef __MQL5__ CTrade trade; #endif SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; //+------------------------------------------------------------------+
In OnInit() entfernen Sie das Setzen der Parameter des 'Handelsobjekts' der Handelsklasse CTrade
//--- Set trailing activation button status ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Set CTrade trading class parameters #ifdef __MQL5__ trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); #endif //--- Create and check the resource files
Suchen Sie dann mit der Suche (Strg+F) nach dem String "trade" und ersetzen Sie den Aufruf der Handelsmethoden der Standardbibliothek durch die von Ihnen benötigten.
Ersetzen Sie zum Beispiel dies:
COrder* position=list_positions.At(index); if(position!=NULL) { //--- Get a ticket of a position with the highest profit and close the position by a ticket #ifdef __MQL5__ trade.PositionClose(position.Ticket()); #else PositionClose(position.Ticket(),position.Volume()); #endif }
durch
COrder* position=list_positions.At(index); if(position!=NULL) { //--- Get a ticket of a position with the highest profit and close the position by a ticket engine.ClosePosition(position.Ticket()); }
Während Sie die Aufrufe der Handelsmethoden in der Standardbibliothek finden, ersetzen Sie diese einfach durch die Aufrufe Ihrer Methoden.
Betrachten wir die resultierenden Panel-Buttons, die den Handler drücken. Alle Aufrufe der neuen Handelsmethoden sind farbig markiert:
//+------------------------------------------------------------------+ //| Handle pressing the buttons | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { string comment=""; //--- Konvertieren der Namen der Schaltflächen in die Zeichenketten-ID string button=StringSubstr(button_name,StringLen(prefix)); //--- Falls eine Taste gedrückt wurde if(ButtonState(button_name)) { //--- Wenn die Schaltfläche BUTT_BUY geklickt wurde: Eröffnen einer Kaufposition if(button==EnumToString(BUTT_BUY)) { //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- Eröffnen einer Kaufposition engine.OpenBuy(lot,Symbol(),magic_number,sl,tp); // No comment - the default comment is to be set } //--- Falls die Schaltfläche BUTT_BUY_LIMIT geklickt wurde: Platzieren von BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit); //--- Setzen einer BuyLimit-Order engine.PlaceBuyLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order")); } //--- Falls die Schaltfläche BUTT_BUY_STOP geklickt wurde: Platzieren von BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit); //--- Setzen einer BuyStop-Order engine.PlaceBuyStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyStop","Pending BuyStop order")); } //--- Falls die Schaltfläche BUTT_BUY_STOP_LIMIT geklickt wurde: Platzieren von BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- Abrufen der korrekten BuyStop-Order relativ zu StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Berechnen des Preises der BuyLimit-Order relativ zu BuyStop unter Berücksichtigung des StopLevels double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit); //--- Setzen von BuyStopLimit-Order engine.PlaceBuyStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный BuyStopLimit","Pending BuyStopLimit order")); } //--- Wenn die Schaltfläche BUTT_SELL geklickt wurde: Eröffnen einer Verkaufsposition else if(button==EnumToString(BUTT_SELL)) { //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zu StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- Eröffnen einer Verkaufsposition engine.OpenSell(lot,Symbol(),magic_number,sl,tp); // No comment - the default comment is to be set } //--- Falls die Schaltfläche BUTT_SELL_LIMIT geklickt wurde: Setzen von SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit); //--- Setzen von SellLimit-Order engine.PlaceSellLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellLimit","Pending SellLimit order")); } //--- Falls die Schaltfläche BUTT_SELL_STOP geklickt wurde: Platzieren von SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- Abrufen der korrekten Order-Platzierung relativ zu StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit); //--- Setzen von SellStop-Order engine.PlaceSellStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellStop","Pending SellStop order")); } //--- Falls die Schaltfläche BUTT_SELL_STOP_LIMIT geklickt wurde: Platzieren von SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- Abrufen des Preises von SellStop relativ zu StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Berechnen des Preises der SellLimit-Order relativ zu SellStop unter Berücksichtigung des StopLevels double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop); //--- Abrufen der korrekten Preise von StopLoss und TakeProfit relativ zur Level der Order-Platzierung unter Berücksichtigung von StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit); //--- Setzen der SellStopLimit-Order engine.PlaceSellStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный SellStopLimit","Pending SellStopLimit order")); } //--- Wenn die Schaltfläche BUTT_CLOSE_BUY geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Buy position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- Wenn die Schaltfläche BUTT_CLOSE_BUY2 geklickt wurde: Schließen der Hälfte der Kaufposition mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- If this is a hedge account, close the half of the Buy position by the ticket if(engine.IsHedge()) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- If this is a netting account, open a Sell position with the half of the Buy position volume else engine.OpenSell(NormalizeLot(position.Symbol(),position.Volume()/2.0),Symbol(),magic_number,position.StopLoss(),position.TakeProfit(),"Частичное закрытие Buy #"+(string)position.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_BUY_BY_SELL geklickt wurde: Schließen einer Kaufposition mit Maximalgewinn durch einen entgegengesetzten Verkauf else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- Im Falle eines Hedging-Kontos if(engine.IsHedge()) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Auswählen der Kaufposition mit Maximalgewinn COrder* position_buy=list_buy.At(index_buy); //--- Auswählen der Verkaufsposition mit Maximalgewinn COrder* position_sell=list_sell.At(index_sell); //--- Schließen der Kaufposition durch eine entgegengesetzte Verkaufsposition if(position_buy!=NULL && position_sell!=NULL) engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_SELL geklickt wurde: Schließen einer Verkaufsposition mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Sell position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- Wenn die Schaltfläche BUTT_CLOSE_SELL2 geklickt wurde: Schließen der Hälfte der Verkaufsposition mit Maximalgewinn else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- If this is a hedge account, close the half of the Sell position by the ticket if(engine.IsHedge()) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- If this is a netting account, open a Buy position with the half of the Sell position volume else engine.OpenBuy(NormalizeLot(position.Symbol(),position.Volume()/2.0),Symbol(),position.Magic(),position.StopLoss(),position.TakeProfit(),"Partial closure Buy #"+(string)position.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_SELL_BY_BUY geklickt wurde: Schließen einer Verkaufsposition mit Maximalgewinn durch einen entgegengesetzten Kauf else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Auswählen von nur Verkaufspositionen aus der Liste list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Verkaufsposition mit Maximalgewinn int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Abrufen der Liste aller offenen Positionen CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Auswählen von nur Kaufpositionen aus der Liste list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Abrufen des Index der Kaufposition mit Maximalgewinn int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Auswählen der Verkaufsposition mit Maximalgewinn COrder* position_sell=list_sell.At(index_sell); //--- Auswählen der Kaufposition mit Maximalgewinn COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- Schließen einer Verkaufsposition mit einer entgegengesetzten Kaufposition engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_CLOSE_ALL geklickt wurde: Schließen aller Positionen beginnend mit dem kleinsten Gewinn else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Abrufen der Liste aller offenen Positionen CArrayObj* list=engine.GetListMarketPosition(); if(list!=NULL) { //--- Sortieren der Liste nach Gewinn unter Berücksichtigung von Kommission und Swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- In der Schleife aller Positionen mit dem geringsten Gewinn for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- Schließen jeder Position mittels der Ticketnummer engine.ClosePosition((ulong)position.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_DELETE_PENDING geklickt wurde:: Entfernen der ersten Pending-Order else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Abrufen der Liste aller Aufträge CArrayObj* list=engine.GetListMarketPendings(); if(list!=NULL) { //--- Sortieren der neuen Liste nach der Platzierungszeit list.Sort(SORT_BY_ORDER_TIME_OPEN); int total=list.Total(); //--- In der Schleife aller Positionen mit der größten Zeitspanne for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- Löschen der Order mach dem Ticket engine.DeleteOrder((ulong)order.Ticket()); } } } //--- Wenn die Schaltfläche BUTT_PROFIT_WITHDRAWAL geklickt wurde: Gelder vom Konto abbuchen if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- Wenn das Programm im Tester gestartet wurde if(MQLInfoInteger(MQL_TESTER)) { //--- Emulieren eine Kontoabbuchung TesterWithdrawal(withdrawal); } } //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- Warten für 1/10 einer Sekunde Sleep(100); //--- "Unpress" the button (if this is not a trailing button) if(button!=EnumToString(BUTT_TRAILING_ALL)) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button is pressed else { //--- Set the color of the active button ButtonState(button_name,true); trailing_on=true; } //--- re-draw the chart ChartRedraw(); } //--- Return the inactive button color (if this is a trailing button) else if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+
Andere verbesserte EA-Funktionen, die CTrade-Handelsklassenmethoden aufrufen, wurden wir hier nicht berücksichtigen. Sie finden alle Daten in den unten angehängten Dateien.
Nun werden wir den EA einfach kompilieren und im Tester starten.
Klicken Sie auf verschiedene Panel-Buttons und stellen Sie sicher, dass die Handelsobjekte bedienbar sind:
Unser ersten Handelsobjekte der Symbole funktionieren wie geplant.
Zahlreiche Verbesserungen müssen noch umgesetzt werden, um die Arbeit mit ihnen effizient und komfortabel zu gestalten.
Was kommt als Nächstes?
Unser nächstes Ziel ist die Entwicklung einer vollwertigen Klasse, die beim Zugriff auf Symbolhandelsobjekte verwendet werden soll.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie testen und herunterladen können.
Stellen Sie Ihre Fragen, Kommentare und Vorschläge in den Kommentaren.
Frühere Artikel dieser Serie:
Teil 1. Konzept, Datenverwaltung.
Teil 2. Erhebung (Collection) historischer Aufträge und Deals.
Teil 3. Erhebung (Collection) von Marktorders und Positionen, Organisieren der Suche
Teil 4. Handelsereignisse. Konzept
Teil 5. Klassen und Kollektionen von Handelsereignissen. Senden von Ereignissen an das Programm
Teil 6. Ereignisse auf Netting-Konten
Teil 7. Ereignis der Aktivierung einer StopLimit-Order, Vorbereiten der Funktionsweise bei Änderungen von Orders und Positionen
Teil 8. Ereignisse von Änderungen von Orders und Positionen
Teil 9. Kompatibilität mit MQL4 - Datenvorbereitung
Teil 10. Kompatibilität mit MQL4 - Ereignisse der Positionseröffnung und Aktivierung von Pending-Orders
Teil 11. Kompatibilität mit MQL4 - Ereignisse des Schließens von Positionen
Teil 12. Objektklasse "Account" und die Kollektion von Konto-Objekten
Teil 13. Das Objekt der Kontoereignisse
Teil 14. Das Symbolobjekt
Teil 15. Die Kollektion der Symbolobjekte
Teil 16. Ereignisse der Kollektionssymbole
Teil 17. Interaktivität von Bibliotheksobjekten
Teil 18. Interaktivität des Kontos und aller anderen Bibliotheksobjekt
Teil 19. Klassenbibliothek für Nachrichten
Teil 20. Erstellen und Speichern von Programmressourcen
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7229





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.