Diskussion zum Artikel "Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil X): Kompatibilität mit MQL4" - Seite 3

 
Alexey Viktorov:

Artem, sagen Sie mir bitte, welche Rolle der hervorgehobene Codeabschnitt spielt.

Wie kann dieser Code ausgeführt werden, wenn der Timer nicht aktiviert ist?

Aber wenn dieser Codeabschnitt gelöscht wird, werden die Ereignismeldungen nicht gedruckt. Aber alles funktioniert mit ihm.

Und ich würde gerne in der Lage sein, ein Ticket, Preise und vielleicht einige andere Eigenschaften von Positionen und Aufträgen zusammen mit der Ereignismeldung zu erhalten.

Ich glaube, Sie haben den Kommentar geändert. In OnTimer() wird der EA nicht im Tester geprüft:

/************************Expert tick function************************/
void OnTick()
{
//--- Initialisierung des letzten Handelsereignisses
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Wenn Sie im Prüfgerät arbeiten
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
     }
//--- Wenn sich das letzte Handelsereignis geändert hat
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
      Comment("last_event: ",EnumToString(last_event));
      Print(__FUNCTION__, " last_event: ",EnumToString(last_event));
      //engine.ResetLastTradeEvent();
      //Drucken("letztes_Ereignis: ",EnumToString(letztes_Ereignis));
     }

}
/***************************Timer function***************************/
void OnTimer()
{
//--- Initialisierung des letzten Handelsereignisses
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Wenn der Auftrag nicht  в тестере
   if(!MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
     }
}
/*******************************************************************/

Und vergleichen Sie zwei Handler: in OnTick() wird der Bibliothekstimer nur im Tester gestartet, und in OnTimer() wird der Bibliothekstimer nur nicht im Tester gestartet - denn in MQL4 funktioniert der Timer nicht im Tester, und wir arbeiten mit Ticks im Tester.

Ich werde jetzt ein Beispiel machen.

 
Artyom Trishkin:

Ich glaube, du hast den Kommentar geändert.

Der Kommentar hat sich von selbst geändert, ich habe ihn nicht angefasst.))))))

Ich habe nur an einer Stelle kopiert und an verschiedenen Stellen eingefügt und nicht darauf geachtet. Aber dann eine Negation in Form von ! und nicht wichtig nicht berührt.

Aber die Frage bleibt offen: Wenn der Timer nicht in OnInit() gestartet wird, was ist dann der Sinn des OnTimer() Handlers und warum wird der Code darin mindestens einmal ausgeführt.

Im Grunde habe ich die Meldungen im Protokoll erhalten. Es bleibt, die Eigenschaften der Elemente zu erhalten. Typ, Ticket, Preise und Zeitpunkt des Öffnens, Schließens, Änderns.

 
Alexey Viktorov:

Und ich möchte in der Lage sein, Ticket, Preise und vielleicht einige andere Eigenschaften von Positionen und Aufträge zusammen mit der Ereignismeldung zu erhalten.

Nicht im Tester können Sie das Order-Ticket des letzten Ereignisses in OnChartEvent() - Parameter lparam erhalten. In dparam wird der Preis gespeichert. In sparam - Symbol.

Um Daten im Tester zu erhalten, sollten Sie derzeit den Ereigniscode verwenden, den Sie mit engine.LastTradeEvent() erhalten - weil alles vom Ereignis abhängt - wenn es eine Änderung ist, müssen Sie eine Liste der Änderungen erhalten, und wenn es eine Änderung in der Anzahl der Aufträge ist, müssen Sie Listen dieser neuen Aufträge erhalten.

Wir müssen CEngine hinzufügen, um die erforderlichen Werte an das Programm zurückzugeben. Ich bin noch nicht an dem Punkt angelangt, an dem die Informationen an das Programm gesendet werden - ich beschreibe noch die Vorbereitung der notwendigen Daten. In zukünftigen Artikeln werden wir die Klasse erreichen, die einen einfachen Zugang zu allen notwendigen Daten ermöglicht. Und jetzt - wenn Sie es dringend brauchen, müssen Sie zu CEngine die Rückgabe der Liste aus der Klasse-Sammlung von Ereignissen hinzufügen - die Klasse selbst hat den Empfang dieser Liste, hier sind sie - alle Listen im öffentlichen Abschnitt CEventCollection:

public:
//--- Wählt Ereignisse aus der Sammlung aus, deren Zeiten von Anfang_Zeit bis Ende_Zeit reichen
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- Gibt die vollständige Listensammlung von Ereignissen "wie sie ist" zurück
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_events;                                           }
//--- Gibt eine Liste mit den ausgewählten (1) ganzzahligen, (2) reellen und (3) Zeichenketteneigenschaften zurück, die das zu vergleichende Kriterium erfüllen
   CArrayObj        *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }

Alle Ereignisse werden in der Liste m_list_events gespeichert, und diese Methoden geben entweder die gesamte Liste oder gefiltert nach einem bestimmten Kriterium zurück.

Um das letzte Ereignis zu erhalten, genügt es, in CEngine die Rückgabe dieser Liste an das Programm zu erstellen und im Programm das gewünschte Ereignis aus der Liste zu nehmen.

All dies wird bald automatisiert werden - es ist noch nicht in der Warteschlange.

Wenn Sie noch eine Krücke machen müssen, ist es Diskussion - in einem privaten. Es lohnt sich hier nicht - es gilt nicht für die Bibliothek, da sie sich noch in der Entwicklung befindet, und weitere normale und ordnungsgemäße Arbeit wird getan werden, um alle und alle erforderlichen Ereignisse in das Programm zu bekommen.

 
Alexey Viktorov:

Der Kommentar hat sich von selbst geändert, ich habe ihn nicht berührt. )))))

Ich habe nur an einer Stelle kopiert und an verschiedenen Stellen eingefügt und nicht darauf geachtet. Aber dann habe ich eine Negation in Form von ! und nicht wichtig nicht berührt.

Aber die Frage bleibt unbeantwortet: Wenn der Timer nicht in OnInit() gestartet wird, was ist dann der Sinn des OnTimer()-Handlers und warum wird der Code darin mindestens einmal ausgeführt.

Im Grunde habe ich die Meldungen im Protokoll erhalten. Es bleibt, die Eigenschaften der Elemente zu erhalten. Typ, Ticket, Preise und Zeitpunkt des Öffnens, Schließens, Änderns.

Erklären Sie, was Sie meinen? Sprechen Sie von der Erstellung eines Timers? Er wird im CEngine-Konstruktor erstellt:

//+------------------------------------------------------------------+
//| CEngine-Konstruktor|
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT)
  {
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
         ::Print(DFUN,"Timer konnte nicht erstellt werden. Fehler: ","Could not create timer. Error: ",(string)::GetLastError());
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
         ::Print(DFUN,"Timer konnte nicht erstellt werden. Fehler: ","Could not create timer. Error: ",(string)::GetLastError());
   #endif 
  }
//+------------------------------------------------------------------+
 
Artyom Trishkin:

Nicht im Tester können Sie das Auftragsticket des letzten Ereignisses in OnChartEvent() - Parameter lparam erhalten. In dparam wird der Preis gespeichert. In sparam - Symbol.

Ich habe es bereits gefunden, vielen Dank. Und wenn Sie ein Ticket bekommen, können Sie alles bekommen, was Sie brauchen. Außer von welchem Preis es eine Änderung gab. Oder machen Sie eine echte Krücke, um den Preis vor der Änderung zu wissen. Im Prinzip ist es noch nicht sehr notwendig.

 
Alexey Viktorov:

Im Grunde habe ich die Protokollmeldungen erhalten. Ich muss nur die Eigenschaften der Positionen erhalten. Typ, Ticket, Preise und Zeitpunkt der Eröffnung, Schließung, Änderung.

Allerdings, für eine schnelle Lösung, fügen Sie eine Ereignisliste Rückkehr in den öffentlichen Bereich der CEngine:

public:
   //--- Liefert eine Liste von (1) Marktpositionen, (2) schwebenden Aufträgen und (3) Marktaufträgen
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Liefert die Liste historischer (1) Aufträge, (2) gelöschter schwebender Aufträge, (3) Abschlüsse, (4) aller Marktaufträge einer Position anhand ihrer Kennung
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(const ulong position_id);
//--- Gibt eine Liste von Ereignissen zurück
   CArrayObj*           GetListAllEvents(void)                          { return this.m_events.GetList();      }
//--- Setzt das letzte Handelsereignis zurück
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Liefert (1) letztes Handelsereignis, (2) Hedge-Konto-Flagge, (3) Tester-Operations-Flagge
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;      }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;              }
   bool                 IsTester(void)                            const { return this.m_is_tester;             }
//--- Erzeugt einen Zeitzähler
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Zeitschaltuhr
   void                 OnTimer(void);
//--- Konstrukteur/Destrukteur
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

Fügen Sie in den EA diesen Code ein:

//+------------------------------------------------------------------+
//| Experten-Tick-Funktion|
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Initialisierung des letzten Handelsereignisses
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Wenn Sie im Prüfgerät arbeiten
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- Wenn sich das letzte Handelsereignis geändert hat
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
      Comment("\nlast_event: ",EnumToString(last_event));
      CArrayObj* list=engine.GetListAllEvents();
      if(list!=NULL)
        {
         if(list.Total()>0)
           {
            CEvent* event=list.At(list.Total()-1);
            if(event!=NULL)
              {
               event.Print();
              }
           }
        }
     }
//--- Wenn das Flag am Ende gesetzt ist
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+

Und das letzte Ereignis wird in das Protokoll gedruckt werden

 
Artyom Trishkin:

Erklären Sie, was Sie meinen? Meinen Sie die Erstellung eines Timers? Er wird im CEngine-Konstruktor erstellt:

Nun, dafür musstest du die ganze Bibliothek durchgehen. )))

 
Alexey Viktorov:

Das habe ich schon gefunden, danke. Und wenn Sie ein Ticket bekommen, können Sie alles bekommen, was Sie brauchen. Bis auf den Preis für den Umbau. Oder machen Sie eine richtige Krücke, um den Preis vor dem Umbau zu erfahren. Ich brauche es eigentlich noch nicht.

Ich habe Ihnen schon den Code gegeben - da steht alles drin, auch der Preis vor der Änderung.

 
Alexey Viktorov:

Nun, dafür musste man die ganze Bibliothek durchforsten. )))

Nein. Lesen Sie einfach die Artikel

 

Und das ist passiert:

Während ich diesen Code in der Demo laufen ließ, wurde eine Limit-Order gesetzt und gelöscht

443342388 2019.05.27 14:54:10 buy limit 0.01 eurusd 1.11835 0.00000 0.00000 2019.05.27 15:01:14 1.11972 cancelled 

Und plötzlich, während des nächsten Mockings, wurde eine Position geändert, eine Position wurde geöffnet und eine Position wurde geschlossen. Aber woher kam die Aufzeichnung der Löschung einer längst gelöschten Order?

2019.05.27 18:34:11.903 00 EURUSD,H1: OnChartEvent: id=1002, event=TRADE_EVENT_PENDING_ORDER_REMOVED, lparam=443342388, dparam=1.11835, sparam=EURUSD
2019.05.27 18:34:11.903 00 EURUSD,H1: OnChartEvent: id=1024, event=TRADE_EVENT_POSITION_CLOSED, lparam=443417294, dparam=1.11933, sparam=EURUSD
2019.05.27 18:34:11.903 00 EURUSD,H1: - Отложенный ордер удалён: 2019.05.27 14:54:10.000 -
EURUSD Удалён 0.01 Buy Limit #443342388  по цене 1.11835
2019.05.27 18:34:11.903 00 EURUSD,H1: - Позиция закрыта: 2019.05.27 18:33:02.000 -
EURUSD Закрыт Sell #443417294  по цене 1.11912, профит -0.21 USD
2019.05.27 18:33:02.755 00 EURUSD,H1: OnChartEvent: id=1022, event=TRADE_EVENT_POSITION_OPENED, lparam=443417294, dparam=1.11912, sparam=EURUSD
2019.05.27 18:33:02.755 00 EURUSD,H1: - Позиция открыта: 2019.05.27 18:33:02.000 -
EURUSD Открыт 0.01 Sell #443417294 [0.01 Market order Sell #443417294]  по цене 1.11912
2019.05.27 18:29:21.913 00 EURUSD,H1: OnChartEvent: id=1050, event=TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT, lparam=443218277, dparam=1.12218, sparam=EURUSD
2019.05.27 18:29:21.913 00 EURUSD,H1: - Модифицирован TakeProfit позиции: 2019.05.27 18:27:45.000 -
EURUSD Buy #443218277:  модифицирован TakeProfit: [1.12240 --> 1.12218]