Diskussion zum Artikel "Entwicklung eines Expertenberaters für mehrere Währungen (Teil 1): Zusammenarbeit von mehreren Handelsstrategien"

 

Neuer Artikel Entwicklung eines Expertenberaters für mehrere Währungen (Teil 1): Zusammenarbeit von mehreren Handelsstrategien :

Es gibt eine ganze Reihe von verschiedenen Handelsstrategien. Daher kann es sinnvoll sein, mehrere Strategien parallel anzuwenden, um Risiken zu diversifizieren und die Stabilität der Handelsergebnisse zu erhöhen. Wenn jedoch jede Strategie als separater Expert Advisor (EA) implementiert wird, wird die Verwaltung ihrer Arbeit auf einem Handelskonto sehr viel schwieriger. Um dieses Problem zu lösen, wäre es sinnvoll, den Betrieb verschiedener Handelsstrategien innerhalb eines einzigen EA zu implementieren.

Wir müssen entscheiden, was wir wollen und was wir haben.

Das haben wir (oder haben wir fast):

  • einige verschiedene Handelsstrategien, die auf verschiedenen Symbolen und Zeitrahmen funktionieren, in Form eines vorgefertigten EA-Codes oder eines formulierten Regelsatzes für die Durchführung von Handelsoperationen,
  • ein Startguthaben und
  • ein maximal zulässiges Drawdown.

Wir wollen:

  • eine Zusammenarbeit aller ausgewählten Strategien auf einem Konto für mehrere Symbole und Zeitrahmen,
  • eine Aufteilung des Startguthabens auf alle zu gleichen Teilen oder gemäß den festgelegten Verhältnissen,
  • die automatische Berechnung der Volumina der eröffneten Positionen zur Einhaltung der maximal zulässigen Inanspruchnahme,
  • eine korrekte Handhabung von Terminal-Neustarts und
  • die Fähigkeit, in MetaTrader 5 und 4 zu laufen.

    Wir werden einen objektorientierten Ansatz, MQL5 und einen Standard-Tester in MetaTrader 5 verwenden.

    Die Aufgabe ist ziemlich umfangreich, deshalb werden wir sie Schritt für Schritt lösen.

    Autor: Yuriy Bykov

     
    class CStrategy : public CObject {
    protected:
       ulong             m_magic;          // Magie
       string            m_symbol;         // Symbol (Handelsinstrument)
       ENUM_TIMEFRAMES   m_timeframe;      // Diagrammzeitraum (Zeitrahmen)
       double            m_fixedLot;       // Größe der offenen Positionen (fest)
    
    public:
       // Konstrukteur
       CStrategy(ulong p_magic,
                 string p_symbol,
                 ENUM_TIMEFRAMES p_timeframe,
                 double p_fixedLot);
    
       virtual int       Init() = 0; // Strategieinitialisierung - OnInit-Ereignisverarbeitung
       virtual void      Tick() = 0; // Hauptmethode - OnTick-Ereignisbehandlung
    };

    Warum brauchen wir die Init-Methode, wenn es einen Konstruktor gibt?

    Aus irgendeinem Grund hat man die TS-Klasse sofort auf ein Symbol und einen Zeitrahmen beschränkt.


    Das scheint logischer zu sein.

    class SYSTEM
    {
    public:
      virtual void OnTick() {}
    };
     
    fxsaber #:

    Warum brauchen Sie eine Init-Methode, wenn Sie einen Konstruktor haben?

    Aus irgendeinem Grund haben sie die TS-Klasse sofort auf ein Symbol und einen Zeitrahmen beschränkt.

    Der Ansatz des Autors hat mir gefallen. Es gibt eine solche Passage in dem Artikel:

    Die Methoden Init() und Tick() werden rein virtuell deklariert (nachdem der Methodenkopf = 0 ist). Das bedeutet, dass wir die Implementierung dieser Methoden nicht in die Klasse CStrategy schreiben werden. Auf der Grundlage dieser Klasse werden wir Nachfolgeklassen erstellen, in denen Init()- und Tick()-Methoden notwendigerweise vorhanden sein müssen und die die Implementierung spezifischer Handelsregeln enthalten.

    Dann wird die Klasse abstrakt sein, soweit ich das verstehe.....

     
    Denis Kirichenko #:

    Dann wird die Klasse abstrakt sein, soweit ich verstehe.....

    Das wird sie sein. Sie wird aus irgendeinem Grund in der Nachfolgeklasse verwendet. Wenn Deinit nicht gemacht wird (es gibt einen Destruktor), ist es logisch, Init nicht zu machen (es gibt einen Konstruktor).

    //+------------------------------------------------------------------+
    //| Konstruktor|
    //+------------------------------------------------------------------+
    CSimpleVolumeStrategy::CSimpleVolumeStrategy(
       ulong            p_magic,
       string           p_symbol,
       ENUM_TIMEFRAMES  p_timeframe,
       double           p_fixedLot,
       int              p_signalPeriod,
       double           p_signalDeviation,
       double           p_signaAddlDeviation,
       int              p_openDistance,
       double           p_stopLevel,
       double           p_takeLevel,
       int              p_ordersExpiration,
       int              p_maxCountOfOrders) :
       // Initialisierungsliste
       CStrategy(p_magic, p_symbol, p_timeframe, p_fixedLot), // Aufruf des Konstruktors der Basisklasse
       signalPeriod_(p_signalPeriod),
       signalDeviation_(p_signalDeviation),
       signaAddlDeviation_(p_signaAddlDeviation),
       openDistance_(p_openDistance),
       stopLevel_(p_stopLevel),
       takeLevel_(p_takeLevel),
       ordersExpiration_(p_ordersExpiration),
       maxCountOfOrders_(p_maxCountOfOrders)
    {}
    
    //+------------------------------------------------------------------+
    //| Initialisierungsfunktion des Experten |
    //+------------------------------------------------------------------+
    int CSimpleVolumeStrategy::Init() {
    // Laden Sie den Indikator, um Tick-Volumen zu erhalten
       iVolumesHandle = iVolumes(m_symbol, m_timeframe, VOLUME_TICK);
    
    // Einstellen der Größe des Array-Empfängers von Tick-Volumes und der erforderlichen Adressierung
       ArrayResize(volumes, signalPeriod_);
       ArraySetAsSeries(volumes, true);
    
    // Magische Zahl für die Auftragserteilung über den Handel festlegen
       trade.SetExpertMagicNumber(m_magic);
    
       return(INIT_SUCCEEDED);
    }

    Und eine künstliche starke Einschränkung der möglichen TCs ist eine seltsame Lösung.


    Man kann auch deutlich die Schwerfälligkeit bei der Eingabe aufgrund von OOP sehen. Es wäre gut, sie zu entfernen.

     

    Init() wurde vorerst weggelassen, weil es unmöglich ist, ein Ergebnis aus dem Konstruktor zurückzugeben. Es ist jedoch möglich, dass wir als Ergebnis der Strategieinitialisierung nie etwas anderes als INIT_SUCCESS zurückgeben müssen. Es ist also durchaus möglich, dass diese Methode in Zukunft abgeschafft wird.

    Die Zuweisung von obligatorischen Strategieeigenschaften in Form von Symbol und Zeitrahmen ist eine bewusste Einschränkung. Es ist vorgesehen, dass der Handel mit mehreren Symbolen durch die Arbeit vieler Instanzen von Erben dieser Klasse erfolgt, aber jede einzelne Instanz arbeitet mit einem einzigen Symbol. Ich habe noch keine Strategien gefunden, die durch eine solche Einschränkung behindert würden. Im Gegenteil, diese Parameter wurden in jeder in Betracht gezogenen Strategie gefunden, weshalb beschlossen wurde, sie sofort in die Basisklasse aufzunehmen.

    Für die Zukunft plane ich jedoch, einige Strategien mit mehreren Symbolen in Betracht zu ziehen, die nicht in mehrere unabhängige Strategien mit einem Symbol unterteilt werden können (falls vorhanden). Ich denke nicht, dass das Vorhandensein von Symbol- und Zeitrahmeneigenschaften in der Basisklasse die Implementierung einer Unterklasse, in der mehrere Symbole und mehrere Zeitrahmen verwendet werden, stark behindern wird.

     
    Yuriy Bykov Eigenschaften von Symbolen und Zeitrahmen in der Basisklasse Sie daran hindern werden, eine untergeordnete Klasse zu implementieren, die mehrere Symbole und mehrere Zeitrahmen verwenden wird.

    Es wird nicht stören - 100%. Es ist nur eine unnötige Einheit. OOP folgt architektonisch dem Prinzip vom Allgemeinen zum Besonderen. Sie haben das Allgemeine (die Basisklasse) "privat" gemacht. Obwohl alles, was dort aufgerufen wird, CStrategy::Tick() ist.

     
    Denis Kirichenko #:

    Und ich mochte den Ansatz des Autors. Es gibt eine solche Passage in dem Artikel:

    Dann wird die Klasse abstrakt sein, soweit ich das verstehe.....

    Das heißt, sie wird nur verwendet, um Unterklassen zu erhalten. Sie brauchen keine Objekte der Basisklasse CStrategy zu erstellen. Aber ein beliebiges Objekt der Unterklasse kann an das Expert Advisor-Objekt übergeben werden, das mit der Methode CAdvisor::AddStrategy(CStrategy &strategy) hinzugefügt werden soll.

     
    Yuriy Bykov #:

    Init() wurde vorerst weggelassen, weil es unmöglich ist, ein Ergebnis aus dem Konstruktor zurückzugeben. Es ist jedoch möglich, dass wir als Ergebnis der Strategieinitialisierung nie etwas anderes als INIT_SUCCESS zurückgeben müssen. Es ist also durchaus möglich, dass diese Methode in Zukunft entfernt wird.

    Für den Fall, dass im Konstruktor etwas schief gelaufen ist (der Indikator-Handle wird nicht geladen oder der Speicher wird nicht zugewiesen usw.), behalten einige Leute eine solche gemeinsame Variable.
    static int CStrategy::InitFlag = INIT_FAILED;
     
    fxsaber #:
    Für den Fall, dass im Konstruktor etwas schief geht (der Indikator-Handle wurde nicht geladen oder der Speicher wurde nicht zugewiesen), behalten manche Leute eine solche gemeinsame Variable.

    Ja, an so etwas habe ich schon gedacht. Ich werde versuchen, es auf diese Weise zu tun.

     
    Yuriy Bykov #:

    kann ein beliebiges Objekt einer Unterklasse an das EA-Objekt übergeben werden, das der Methode CAdvisor::AddStrategy(CStrategy &strategy) hinzugefügt werden soll.

    Es scheint ein Compiler-Bug zu sein, der diese Methodensignatur bei einem Aufruf wie diesem nicht beachtet.

       expert.AddStrategy(new CSimpleVolumeStrategy(
                             magic_ + 1, "EURGBP", PERIOD_H1,
                             NormalizeDouble(0.34 * depoPart_, 2),
                             130, 0.9, 1.4, 231, 3750, 50, 600, 3)

    Sie sollte so lauten.

    CAdvisor::AddStrategy(CStrategy* strategy)
     
    Erben von CObject.
    class CStrategy : public CObject {

    Sie verwenden es nicht.

    class CAdvisor : public CObject {
    protected:
       CStrategy         *m_strategies[];  // Palette von Handelsstrategien

    Ich habe festgestellt, dass es durchaus üblich ist, von CObject zu erben.