English Русский
preview
Fortgeschrittene Algorithmen für die Auftragsausführung in MQL5: TWAP, VWAP und Eisberg-Aufträge

Fortgeschrittene Algorithmen für die Auftragsausführung in MQL5: TWAP, VWAP und Eisberg-Aufträge

MetaTrader 5Handelssysteme |
29 6
N Soumik
N Soumik
  1. Einführung
  2. Verstehen von Ausführungsalgorithmen
  3. Die Implementation in MQL5
  4. Implementierung des Performance Analyzers
  5. Leistungsvergleich der Algorithmen
  6. Integration von Ausführungsalgorithmen in Handelsstrategien
  7. Beispiele für die Integration
  8. Integrierte Strategie
  9. Backtest-Ergebnisse
  10. Schlussfolgerung



Einführung

Stellen Sie sich vor, Sie stehen am Rande des Handelsparketts und sehen mit klopfendem Herzen die Kurse in Echtzeit vorbeiziehen. Eine falsche Bewegung, ein überdimensionierter Auftrag, und Ihr Vorsprung ist im Nu dahin. Willkommen in der Welt, in der die Qualität der Ausführung nicht nur ein Nice-to-have ist, sondern die Geheimwaffe, die die Gewinner von den anderen unterscheidet.

Jahrzehntelang haben institutionelle Schwergewichte in aller Stille ausgeklügelte Algorithmen eingesetzt, um ihre Aufträge zu zerschneiden, zu würfeln und heimlich zu verteilen, um Ausrutscher zu vermeiden und die Auswirkungen auf den Markt einzudämmen. Dank der Flexibilität von MQL5 kann jeder ehrgeizige Einzelhändler jetzt auf das gleiche leistungsstarke Spielbuch zugreifen.

Was ist daran so schlimm?

Stellen Sie sich Folgendes vor: Sie entdecken eine einmalige Gelegenheit und beschließen, groß einzusteigen. Sie erteilen einen Marktauftrag in voller Höhe und müssen dann mit ansehen, wie der Kurs unter dem Gewicht Ihres eigenen Handels nachgibt. In Sekundenschnelle wird aus dem idealen Einstieg ein wackeliger Kompromiss. Das ist der berüchtigte Nachteil der Marktauswirkungen, der selbst an den liquidesten Handelsplätzen zu spüren ist.

Ausführungsalgorithmen sind Ihr Gegenmittel. Indem sie einen großen Auftrag in eine Reihe kleinerer, strategisch zeitlich abgestimmter Abschnitte aufteilen, glätten sie Ihren Fußabdruck im Auftragsbuch. Das Ergebnis? Weniger Schlupf, engere Füllungen und eine allgemeine Verbesserung des durchschnittlichen Ausführungspreises.

Von den Elfenbeintürmen auf Ihren Desktop

„Sicher“, könnten Sie mit den Schultern zucken, „aber ich werde keine institutionellen Summen verschieben“. Der Clou: Das müssen Sie nicht. Ganz gleich, ob Sie ein halbes Los oder eine Handvoll Minilose einsetzen, die Volatilität kann Ihre Ausführung immer noch beeinträchtigen. Diese Werkzeuge helfen Ihnen:

  • Eingrenzen des Slippage: Selbst bescheidene Aufträge können sich in unruhigen Märkten verschieben.
  • Schärfen Sie Ihre Kanten: Bei gestaffelten Ausführungen erzielen Sie oft einen günstigeren Durchschnittspreis als bei einem einmaligen Glücksspiel.
  • Ruhig bleiben: Automatisierte Arbeitsabläufe nehmen der Versuchung, in Panik zu kaufen oder zu verkaufen, den Schrecken.
  • Geschmeidig skalieren: Auch wenn Ihr Konto wächst, bleibt die Ausführung knackig - egal wie umfangreich Ihre Aufträge werden.
  • Fliegen Sie unter dem Radar: Vor allem Eisberg-Aufträge verschleiern Ihre wahre Auftragsgröße und lassen neugierige Algos im Dunkeln.

Die heutige demokratisierte Landschaft bedeutet, dass die gleiche Ausführungstechnologie, die früher Millionenbudgets erforderte, jetzt auf Ihrer persönlichen Handelsstation laufen kann. Indem Sie den ausgefeilten MQL5-Code für TWAP-, VWAP- und Iceberg-Strategien in Ihre Plattform einfügen, rüsten Sie sich mit institutioneller Feuerkraft aus - ohne jemals den Einzelhandelsbereich zu verlassen.

Machen Sie sich bereit, das Drehbuch für Ihren Ausführungsprozess umzudrehen. Das Spiel ändert sich, und mit diesen Algorithmen in Ihrem Werkzeugkasten spielen Sie, um zu gewinnen.


Verstehen von Ausführungsalgorithmen

Bevor Sie sich mit den Implementierungsdetails befassen, sollten Sie die Theorie hinter den einzelnen Ausführungsalgorithmen verstehen und wissen, warum sie in verschiedenen Marktszenarien effektiv sind.

  1. Zeitlich gewichteter Durchschnittspreis (TWAP): TWAP ist ein einfacher Ausführungsalgorithmus, der einen großen Auftrag in gleiche Teile aufteilt und diese in festen Zeitabständen über einen bestimmten Zeitraum versendet. Sein Ziel ist es, den Durchschnittspreis des Instruments während dieses Zeitraums zu erreichen.
    • So funktioniert es:

      • Sende Aufträge in regelmäßigen Zeitabständen zwischen Start und Ende.
      • Normalerweise werden gleich große Aufträge verwendet (Sie können die Größen aber auch zufällig festlegen).
      • Folge einem vorbestimmten Zeitplan, unabhängig von den Kursbewegungen.
      • Verteile die Marktauswirkungen gleichmäßig über die Zeit, um die Abweichung gering zu halten.

    • Wann ist das zu verwenden:

      • Sie benötigen einen durchschnittlichen Ausführungskurs über einen bestimmten Zeitraum.
      • Die Liquidität ist während des gesamten Handelszeitraums konstant.
      • Sie haben ein festes Zeitfenster, um Ihre Aufträge abzuschließen.
      • Sie bevorzugen einen einfachen, vorhersehbaren Ansatz.

  2. Volumengewichteter Durchschnittspreis (VWAP): Der VWAP verbessert den TWAP, indem er die Auftragsgrößen nach dem erwarteten Volumen gewichtet. Anstelle von gleich großen Stücken werden größere Handelsgeschäfte gesendet, wenn das Volumen tendenziell höher ist.
    • So funktioniert es:

      • Verteile die Auftragsgröße im Verhältnis zu historischen Volumenmustern.
      • Analysiere vergangene Handelsvolumina, um die zukünftige Volumenverteilung vorherzusagen.
      • Es kann sich bei einigen Implementierungen an Volumenänderungen in Echtzeit anpassen.
      • Führt in Zeiten hohen Aufkommens mehr aus, um die Auswirkungen zu verringern.

    • Wann ist das zu verwenden:

      • Ihre Leistung wird am VWAP gemessen.
      • Volume follows a predictable daily pattern.
      • Sie handeln auf einem Markt, auf dem die Liquidität während der Sitzung schwankt.
      • Sie wollen sich dem natürlichen Fluss des Marktes anpassen.

  3. Eisberg-Aufträge: Eisberg-Aufträge zielen darauf ab, den wahren Umfang eines großen Auftrags zu verbergen. Es ist immer nur eine kleine „Spitze“ sichtbar; sobald sie sich füllt, erscheint der nächste Teil.
    • So funktioniert es:

      • Zeigt nur einen Teil des gesamten Auftrags an.
      • Gibt nach der Ausführung jedes Tipps neue sichtbare Teile frei.
      • Sie können die sichtbare Größe festlegen oder randomisieren, um die Erkennung zu reduzieren.
      • Sie werden häufig als Limitaufträge zur besseren Preiskontrolle erteilt.

    • Wann ist das zu verwenden:

      • Sie müssen den vollen Umfang Ihrer Auftrags verbergen.
      • Der Markt ist nicht sehr liquide, und große Transaktionen können die Preise beeinflussen.
      • Sie möchten die Ausführung auf einem bestimmten Preisniveau beibehalten.
      • Sie sind besorgt darüber, dass andere Händler Ihren Auftrag entdecken und ihm zuvorkommen könnten.


Die Implementation in MQL5

    Nachdem wir nun die Theorie hinter diesen Ausführungsalgorithmen verstanden haben, wollen wir sie in MQL5 implementieren. Wir werden einen modularen, objektorientierten Rahmen schaffen, der es ermöglicht, diese Algorithmen einzeln oder kombiniert in einem einheitlichen Ausführungssystem zu verwenden.

    Basisklasse: CExecutionAlgorithm

    Wir beginnen mit der Definition einer Basisklasse, die gemeinsame Funktionen für alle Ausführungsalgorithmen bietet:

    //+------------------------------------------------------------------+
    //| Base class for all execution algorithms                          |
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;              // Symbol to trade
       double            m_totalVolume;         // Total volume to execute
       double            m_executedVolume;      // Volume already executed
       double            m_remainingVolume;     // Volume remaining to execute
       datetime          m_startTime;           // Start time for execution
       datetime          m_endTime;             // End time for execution
       bool              m_isActive;            // Flag indicating if the algorithm is active
       int               m_totalOrders;         // Total number of orders placed
       int               m_filledOrders;        // Number of filled orders
       double            m_avgExecutionPrice;   // Average execution price
       double            m_executionValue;      // Total value of executed orders
       int               m_slippage;            // Allowed slippage in points
       
    public:
       // Constructor
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage = 3);
       
       // Destructor
       virtual ~CExecutionAlgorithm();
       
       // Common methods
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate();
       
       // Utility methods
       bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price);
       bool              CancelOrder(ulong ticket);
       void              UpdateAverageExecutionPrice(double price, double volume);
       
       // Getters
       string            GetSymbol() const { return m_symbol; }
       double            GetTotalVolume() const { return m_totalVolume; }
       double            GetExecutedVolume() const { return m_executedVolume; }
       double            GetRemainingVolume() const { return m_remainingVolume; }
       datetime          GetStartTime() const { return m_startTime; }
       datetime          GetEndTime() const { return m_endTime; }
       bool              IsActive() const { return m_isActive; }
       int               GetTotalOrders() const { return m_totalOrders; }
       int               GetFilledOrders() const { return m_filledOrders; }
       double            GetAverageExecutionPrice() const { return m_avgExecutionPrice; }
    };
    

    Die Methode PlaceOrder ist besonders wichtig, da sie die eigentliche Auftragsausführung übernimmt und die Volumenverfolgung aktualisiert:

    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price)
    {
       // Prepare the trade request
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volume;
       m_remainingVolume -= volume;
       UpdateAverageExecutionPrice(price, volume);
       
       // Store the order ticket for future reference
       ulong ticket = result.order;
       
       return true;
    }
    

    Diese Funktion erstellt und sendet eine Marktorder, indem sie einen MqlTradeRequest und ein MqlTradeResult nullinitialisiert, Symbol, Volumen, Ordertyp, Preis, Slippage und eine magische Zahl eingibt und dann OrderSend aufruft. Wenn das Senden fehlschlägt oder der Rückgabecode des Brokers nicht TRADE_RETCODE_DONE ist, wird der Fehler protokolliert und false zurückgegeben. Bei Erfolg werden die internen Zähler (Gesamtzahl/Anzahl der Füllungen, ausgeführtes und verbleibendes Volumen) aktualisiert, der Durchschnittspreis neu berechnet, die Ticket-ID erfasst und „true“ zurückgegeben.

    Umsetzung des TWAP

    Der TWAP-Algorithmus unterteilt den Ausführungszeitraum in gleiche Zeitintervalle und platziert in jedem Intervall Aufträge von gleicher (oder zufälliger) Größe:

    //+------------------------------------------------------------------+
    //| Time-Weighted Average Price (TWAP) Algorithm                     |
    //+------------------------------------------------------------------+
    class CTWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_intervalVolume;      // Volume per interval
       bool              m_useRandomization;    // Whether to randomize order sizes
       double            m_randomizationFactor; // Factor for randomization (0-1)
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
             double randomizationFactor = 0.2, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // TWAP specific methods
       void              CalculateIntervalVolume();
       datetime          CalculateNextExecutionTime();
       double            GetRandomizedVolume(double baseVolume);
       bool              IsTimeToExecute();
    };
    

    Schlüssel Methode: CalculateNextExecutionTime

    Mit dieser Methode wird sichergestellt, dass die Aufträge zeitlich richtig verteilt werden, wobei der erste Auftrag mit einer gewissen Verzögerung erfolgt:
    datetime CTWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
       // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("TWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("TWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    Diese Methode teilt das Fenster von m_startTime bis m_endTime in m_intervals gleiche Segmente auf und gibt zurück, wann das nächste Handelsgeschäft ausgelöst werden soll: beim allerersten Aufruf ist es einfach m_startTime + m_initialDelay, und bei jedem weiteren Aufruf ist es TimeCurrent() + ein Intervall von Sekunden (aber nie über m_endTime hinaus).

    Execute-Methode:Die Execute-Methode prüft, ob es an der Zeit ist, einen Auftrag zu erteilen, und führt die eigentliche Auftragserteilung durch:

    bool CTWAP::Execute()
    {
       if(!m_isActive)
          return false;
       
       // Check if it's time to execute the next order
       if(!IsTimeToExecute())
          return true; // Not time yet
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomization ? 
                               GetRandomizedVolume(m_intervalVolume) : 
                               m_intervalVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       // Get current market price
       double price = 0.0;
       if(m_orderType == ORDER_TYPE_BUY)
          price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
       else
          price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          
       Print("TWAP: Placing order for interval ", m_currentInterval,
             ", Volume: ", DoubleToString(volumeToExecute, 2),
             ", Price: ", DoubleToString(price, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("TWAP: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("TWAP: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volumeToExecute;
       m_remainingVolume -= volumeToExecute;
       
       // Update interval counter
       m_currentInterval++;
       m_firstOrderPlaced = true;
       
       // Calculate the time for the next execution
       if(m_currentInterval < m_intervals && m_remainingVolume > 0)
          m_nextExecutionTime = CalculateNextExecutionTime();
       else
          m_isActive = false; // All intervals completed or no volume left
          
       Print("TWAP: Executed ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(price, _Digits), 
             ". Remaining: ", DoubleToString(m_remainingVolume, 2),
             ", Next execution: ", TimeToString(m_nextExecutionTime));
             
       return true;
    }
    

    Diese Execute-Methode verwaltet einen Teil Ihres TWAP-Laufs. Zunächst bricht er ab, wenn die Strategie nicht aktiv ist oder wenn es noch nicht an der Zeit ist, zu handeln. Wenn dies der Fall ist, wird entweder ein fester oder ein zufällig gewählter Teil des verbleibenden Volumens ausgewählt (der nie über das verbleibende Volumen hinausgeht) und dann das aktuelle Angebot (für Käufe) oder die aktuelle Nachfrage (für Verkäufe) abgefragt. Er protokolliert das Intervall, das Volumen und den Preis, erstellt einen MqlTradeRequest mit Ihrem Symbol, dem Volumen, dem Typ, dem Preis, der Slippage und der Magic Number und ruft OrderSend auf. Wenn das Senden fehlschlägt oder der Broker etwas anderes als TRADE_RETCODE_DONE zurückgibt, wird ein Fehler gedruckt und false zurückgegeben.

    Bei Erfolg werden die Auftragszähler erhöht, die ausgeführten und verbleibenden Volumina angepasst, die Intervallanzahl erhöht, markiert, dass der erste Auftrag ausgeführt wurde, und dann entweder die nächste Ausführungszeit geplant oder die Strategie deaktiviert, wenn keine Intervalle oder kein Volumen mehr vorhanden sind. Schließlich wird protokolliert, was passiert ist, und true zurückgegeben.

    Einführung von VWAP

    Der VWAP-Algorithmus ähnelt dem TWAP-Algorithmus, verteilt aber die Auftragsgrößen auf der Grundlage historischer Volumenmuster:
    //+------------------------------------------------------------------+
    //| Volume-Weighted Average Price (VWAP) Algorithm                   |
    //+------------------------------------------------------------------+
    class CVWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_volumeProfile[];     // Historical volume profile
       double            m_intervalVolumes[];   // Volume per interval based on profile
       bool              m_adaptiveMode;        // Whether to adapt to real-time volume
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       int               m_historyDays;         // Number of days to analyze for volume profile
       bool              m_profileLoaded;       // Flag indicating if profile was loaded
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
             bool adaptiveMode = true, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // VWAP specific methods
       bool              LoadVolumeProfile();
       void              CalculateIntervalVolumes();
       void              AdjustToRealTimeVolume();
       datetime          CalculateNextExecutionTime();
       double            GetCurrentVWAP();
       bool              IsTimeToExecute();
    };
    
    Wie TWAP implementiert auch VWAP die CalculateNextExecutionTime-Methode, um den richtigen Abstand zwischen den Aufträgen zu gewährleisten:
    datetime CVWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
          // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("VWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("VWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    Umsetzung der Eisberg-Aufträge

    Eisberg-Aufträge verbergen den wahren Umfang eines Auftrags, indem sie nur einen kleinen Teil zu einem bestimmten Zeitpunkt dem Markt zugänglich machen:

    //+------------------------------------------------------------------+
    //| Iceberg Order Implementation                                     |
    //+------------------------------------------------------------------+
    class CIcebergOrder : public CExecutionAlgorithm
    {
    private:
       double            m_visibleVolume;          // Visible portion of the order
       double            m_minVisibleVolume;       // Minimum visible volume
       double            m_maxVisibleVolume;       // Maximum visible volume
       bool              m_useRandomVisibleVolume; // Whether to randomize visible volume
       int               m_orderPlacementDelay;    // Delay between order placements (ms)
       bool              m_avoidRoundNumbers;      // Whether to avoid round numbers in price
       double            m_limitPrice;             // Limit price for the orders
       ulong             m_currentOrderTicket;     // Current active order ticket
       ENUM_ORDER_TYPE   m_orderType;              // Order type (buy or sell)
       bool              m_orderActive;            // Flag indicating if an order is currently active
       int               m_priceDeviation;         // Price deviation to avoid round numbers (in points)
       datetime          m_lastCheckTime;          // Last time order status was checked
       int               m_checkInterval;          // How often to check order status (seconds)
       int               m_maxOrderLifetime;       // Maximum lifetime for an order in seconds
       datetime          m_orderPlacementTime;     // When the current order was placed
       
    public:
       // Constructor
       CIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                     double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                     bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                     bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // Iceberg specific methods
       double            GetRandomVisibleVolume();
       double            AdjustPriceToAvoidRoundNumbers(double price);
       bool              CheckAndReplaceOrder();
       bool              IsOrderFilled(ulong ticket);
       bool              IsOrderPartiallyFilled(ulong ticket, double &filledVolume);
       bool              IsOrderCancelled(ulong ticket);
       bool              IsOrderExpired(ulong ticket);
       bool              IsOrderTimeout();
       ulong             GetCurrentOrderTicket() { return m_currentOrderTicket; }
       bool              IsOrderActive() { return m_orderActive; }
    };
    
    Die Execute-Methode platziert einen neuen sichtbaren Teil des Auftrags:
    bool CIcebergOrder::Execute()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Execute called but algorithm is not active");
          return false;
       }
          
       // If an order is already active, check its status
       if(m_orderActive)
       {
          Print("Iceberg: Execute called with active order ", m_currentOrderTicket);
          return CheckAndReplaceOrder();
       }
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomVisibleVolume ? 
                               GetRandomVisibleVolume() : 
                               m_visibleVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       Print("Iceberg: Placing order for ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(m_limitPrice, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_PENDING;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = m_limitPrice;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("Iceberg: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("Iceberg: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Store the order ticket
       m_currentOrderTicket = result.order;
       m_orderActive = true;
       m_orderPlacementTime = TimeCurrent();
       
       Print("Iceberg: Order placed successfully. Ticket: ", m_currentOrderTicket,
             ", Volume: ", DoubleToString(volumeToExecute, 2), 
             ", Remaining: ", DoubleToString(m_remainingVolume, 2));
             
       return true;
    }
    

    Wenn Execute ausgeführt wird, prüft es zunächst, ob der Algorithmus aktiv ist. Wenn es bereits einen aktiven Eisberg-Auftrag gibt, wird CheckAndReplaceOrder() aufgerufen, um festzustellen, ob diese storniert oder aufgefüllt werden muss. Andernfalls wird eine sichtbare Scheibe (entweder fest oder zufällig) ausgewählt, um die verbleibende Gesamtsumme gekappt und die Größe und der Preis protokolliert.

    Anschließend wird die Anfrage eines schwebenden Auftrags (TRADE_ACTION_PENDING) mit Symbol, Volumen, Limitpreis, Slippage und Magic Number erstellt und OrderSend aufgerufen. Bei Fehlern oder nicht erledigten Rückgabewerten wird ein Protokoll erstellt und ein falscher Wert zurückgegeben; bei Erfolg wird das neue Ticket gespeichert, der Auftrag als aktiv markiert, der Zeitpunkt der Platzierung aufgezeichnet, die Details protokolliert und der richtige Wert zurückgegeben.

    Die Aktualisierungsmethode umfasst eine Timeout-Erkennung, um sicherzustellen, dass die Aufträge nicht unbegrenzt aktiv bleiben:

    bool CIcebergOrder::Update()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Update called but algorithm is not active");
          return false;
       }
          
       // Check if all volume has been executed
       if(m_remainingVolume <= 0)
       {
          Print("Iceberg: All volume executed. Terminating algorithm.");
          return Terminate();
       }
       
       // Check if it's time to check order status
       datetime currentTime = TimeCurrent();
       if(currentTime >= m_lastCheckTime + m_checkInterval)
       {
          m_lastCheckTime = currentTime;
          
          // Log current market conditions
          double currentBid = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          double currentAsk = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
          
          Print("Iceberg: Market update - Bid: ", DoubleToString(currentBid, _Digits),
                ", Ask: ", DoubleToString(currentAsk, _Digits),
                ", Limit Price: ", DoubleToString(m_limitPrice, _Digits));
          
          // If an order is active, check its status
          if(m_orderActive)
          {
             // Check if the order has been active too long
             if(IsOrderTimeout())
             {
                Print("Iceberg: Order ", m_currentOrderTicket, " has timed out. Replacing it.");
                
                // Cancel the current order
                if(!CancelOrder(m_currentOrderTicket))
                {
                   Print("Iceberg: Failed to cancel timed out order ", m_currentOrderTicket);
                }
                
                // Reset order tracking
                m_orderActive = false;
                m_currentOrderTicket = 0;
                
                // Place a new order after a delay
                Sleep(m_orderPlacementDelay);
                return Execute();
             }
             
             return CheckAndReplaceOrder();
          }
          else
          {
             // If no order is active, execute a new one
             Print("Iceberg: No active order, executing new order");
             return Execute();
          }
       }
       
       return true;
    }
    

    Update fragt periodisch den Markt- und Auftragsstatus in den durch m_checkInterval definierten Intervallen ab. Wenn das gesamte Volumen aufgebraucht ist, wird das Programm beendet. Andernfalls wird bei Eintreffen der Prüfzeit der aktuelle Geld-/Briefkurs und der Limitkurs aufgezeichnet. Wenn eine Auftrag aktiv ist, wird geprüft, ob eine Zeitüberschreitung vorliegt: Wenn diese abgelaufen ist, wird der Auftrag abgebrochen, der Status zurückgesetzt, eine Pause für m_orderPlacementDelay eingelegt und der Auftrag erneut ausgeführt, um ein neues Slice zu platzieren; wenn keine Zeitüberschreitung vorliegt, wird CheckAndReplaceOrder() vorgezogen. Liegt kein aktiver Auftrag vor, wird einfach Execute aufgerufen, um den nächsten sichtbaren Teil zu senden.


    Implementierung des Performance Analyzers

    Unsere Klasse des Performance Analyzer verfolgt diese Metriken und bietet Methoden zur Analyse und zum Vergleich der Algorithmenleistung:
    //+------------------------------------------------------------------+
    //| Performance Analyzer for Execution Algorithms                    |
    //+------------------------------------------------------------------+
    class CPerformanceAnalyzer
    {
    private:
       string            m_symbol;              // Symbol being analyzed
       datetime          m_startTime;           // Analysis start time
       datetime          m_endTime;             // Analysis end time
       double            m_decisionPrice;       // Price at decision time
       double            m_avgExecutionPrice;   // Average execution price
       double            m_totalVolume;         // Total volume executed
       double            m_implementationShortfall; // Implementation shortfall
       double            m_marketImpact;        // Estimated market impact
       double            m_slippage;            // Average slippage
       int               m_executionTime;       // Total execution time in seconds
       double            m_priceImprovement;    // Total price improvement
       
    public:
       // Constructor
       CPerformanceAnalyzer(string symbol, double decisionPrice);
       
       // Analysis methods
       void              RecordExecution(datetime time, double price, double volume);
       void              CalculateMetrics();
       void              CompareAlgorithms(CPerformanceAnalyzer &other);
       
       // Reporting methods
       void              PrintReport();
       void              SaveReportToFile(string filename);
       
       // Getters
       double            GetImplementationShortfall() const { return m_implementationShortfall; }
       double            GetMarketImpact() const { return m_marketImpact; }
       double            GetSlippage() const { return m_slippage; }
       int               GetExecutionTime() const { return m_executionTime; }
       double            GetPriceImprovement() const { return m_priceImprovement; }
    };
    

    Der CPerformanceAnalyzer kapselt alle Ihre Post-Trade-Metriken an einem Ort. Bei der Konstruktion geben Sie ihm ein Symbol und den Benchmark-Preis zum Entscheidungszeitpunkt; er stempelt den aktuellen Zeitpunkt als Start. Wenn jeder untergeordnete Auftrag ausgeführt wird, rufen Sie RecordExecution(time, price, volume) auf, wodurch die laufenden Summen aktualisiert werden - kumulatives Volumen, gewichteter durchschnittlicher Ausführungspreis und Zeitstempel. Sobald die Strategie beendet ist (oder in regelmäßigen Abständen), rufen Sie CalculateMetrics() auf, das Berechnungen durchführt:

    • Umsetzungsdefizit (die GuV-Differenz zwischen Ihrem Entscheidungspreis und den tatsächlichen Ausführungszahlen),
    • Durchschnittlicher Slippage im Vergleich zu den notierten Preisen,
    • Geschätzte Marktauswirkungen durch Ihren Fußabdruck,
    • Gesamtausführungszeit (Ende minus Anfang),
    • Etwaige Preisverbesserungen gegenüber den Benchmarks.

    Sie können sogar zwei Durchläufe über CompareAlgorithms(otherAnalyzer) vergleichen, um zu sehen, welche Strategie besser abschneidet. Schließlich gibt PrintReport() die wichtigsten Statistiken zur schnellen Überprüfung in das Protokoll aus, und mit SaveReportToFile(Dateiname) können Sie einen vollständigen Bericht extern speichern. Leichtgewichtige Getter machen jede Metrik für nutzerdefinierte Dashboards oder weitere Analysen zugänglich.


    Leistungsvergleich der Algorithmen

    Unterschiedliche Marktbedingungen begünstigen unterschiedliche Ausführungsalgorithmen. Hier ist ein allgemeiner Vergleich bzw. eine Faustregel:
    1. TWAP:
      • Geeignet für: Stabile Märkte mit gleichbleibender Liquidität
      • Vorteile: Einfaches, vorhersehbares Ausführungsmuster
      • Nachteile: Passt sich nicht an veränderte Marktbedingungen an
    2. VWAP:
      • Geeignet für: Märkte mit vorhersehbaren Volumenmustern
      • Vorteile: Passt sich dem natürlichen Marktrhythmus an, erzielt oft bessere Preise
      • Nachteile: Erfordert historische Volumendaten, komplexere Implementierung
    3. Eisberg-Aufträge:
      • Geeignet für: Weniger liquide Märkte oder wenn die Preisempfindlichkeit hoch ist
      • Vorteile: Minimiert die Auswirkungen auf den Markt, unterstützt eine Preiskontrolle
      • Nachteile: Die Ausführungszeit kann unvorhersehbar sein, es besteht das Risiko einer teilweisen Ausführung.


    Integration von Ausführungsalgorithmen in Handelsstrategien

    Die wahre Stärke dieser Ausführungsalgorithmen zeigt sich, wenn sie in Handelsstrategien integriert werden. In diesem Abschnitt wird gezeigt, wie unsere Ausführungsalgorithmen in vollständige Handelssysteme integriert werden können.

    Ausführungsmanager

    Um die Integration zu vereinfachen, erstellen wir eine Klasse für den Execution Manager, die als Fassade für alle unsere Ausführungsalgorithmen dient:

    //+------------------------------------------------------------------+
    //| Execution Manager - Facade for all execution algorithms          |
    //+------------------------------------------------------------------+
    class CExecutionManager
    {
    private:
       CExecutionAlgorithm* m_algorithm;       // Current execution algorithm
       CPerformanceAnalyzer* m_analyzer;       // Performance analyzer
       
    public:
       // Constructor
       CExecutionManager();
       
       // Destructor
       ~CExecutionManager();
       
       // Algorithm creation methods
       bool              CreateTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
                                   double randomizationFactor = 0.2, int slippage = 3);
                                   
       bool              CreateVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
                                   bool adaptiveMode = true, int slippage = 3);
                                   
       bool              CreateIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                                           double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                                           bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                                           bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Execution methods
       bool              Initialize();
       bool              Execute();
       bool              Update();
       bool              Terminate();
       
       // Performance analysis
       void              EnablePerformanceAnalysis(double decisionPrice);
       void              PrintPerformanceReport();
       
       // Getters
       CExecutionAlgorithm* GetAlgorithm() { return m_algorithm; }
    };
    

    Der CExecutionManager fungiert als einfache Fassade für alle Ihre Ausführungsalgorithmen und bindet sie in einen einheitlichen Handelsablauf ein. Intern enthält es einen Zeiger auf den aktuell ausgewählten CExecutionAlgorithm (TWAP, VWAP oder Iceberg) sowie einen CPerformanceAnalyzer, um zu verfolgen, wie gut Ihre Aufträge abschneiden.

    Sie wählen Ihre Strategie aus, indem Sie eine der Methoden Create... aufrufen. Dabei geben Sie das Symbol, das Gesamtvolumen, die Start-/Endzeit (für TWAP/VWAP), die Intervallanzahl oder die Slice-Größe, den Auftragstyp und alle algorithmusspezifischen Regler (Randomisierung, Historienfenster, Grenzpreis usw.) an. Nach der Erstellung durchlaufen Sie den üblichen Lebenszyklus:

    1. Initialize() richtet alle benötigten Zustände oder Daten ein.
    2. Execute() löst den nächsten Slice- oder Child-Auftrag aus.
    3. Update() fragt Füllungen, Marktdaten oder Zeitüberschreitungen ab.
    4. Terminate() räumt auf, wenn Sie alles ausgefüllt haben oder aufhören wollen.

    Wenn Sie die Performance-Analyse mit EnablePerformanceAnalysis() aktivieren, zeichnet der Manager die Ausführungspreise im Vergleich zu einer Entscheidungs-Benchmark auf, und Sie können einen übersichtlichen P/L- und Slippage-Bericht über PrintPerformanceReport() ausgeben. Sie können jederzeit das rohe Algorithmusobjekt mit GetAlgorithm() für nutzerdefinierte Tests oder Metriken abrufen.


    Beispiele für die Integration

    Hier finden Sie Beispiele dafür, wie unsere Ausführungsalgorithmen in verschiedene Arten von Handelsstrategien integriert werden können:

    1. Trendfolgestrategie mit TWAP-Ausführung.

      //+------------------------------------------------------------------+
      //| Trend-Following Strategy with TWAP Execution                     |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int maPeriodFast = 20;
         int maPeriodSlow = 50;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double maFast = iMA(Symbol(), PERIOD_CURRENT, maPeriodFast, 0, MODE_SMA, PRICE_CLOSE, 0);
         double maSlow = iMA(Symbol(), PERIOD_CURRENT, maPeriodSlow, 0, MODE_SMA, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Fast MA crosses above Slow MA
            if(maFast > maSlow)
            {
               // Create TWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateTWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy signal detected. Starting TWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("TWAP execution completed.");
               }
            }
         }
      }
      
    2. Mean-Reversion-Strategie mit VWAP-Ausführung.

      //+------------------------------------------------------------------+
      //| Mean-Reversion Strategy with VWAP Execution                      |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int rsiPeriod = 14;
         int rsiOversold = 30;
         int rsiOverbought = 70;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double rsi = iRSI(Symbol(), PERIOD_CURRENT, rsiPeriod, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static bool isLong = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: RSI oversold
            if(rsi < rsiOversold)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = true;
                  Print("Buy signal detected. Starting VWAP execution.");
               }
            }
            // Sell signal: RSI overbought
            else if(rsi > rsiOverbought)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_SELL))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = false;
                  Print("Sell signal detected. Starting VWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("VWAP execution completed.");
               }
            }
            
            // Check for exit conditions
            if(isLong && rsi > rsiOverbought)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
            else if(!isLong && rsi < rsiOversold)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
         }
      }
      
    3. Breakout-Strategie mit Eisberg-Aufträgen.
      //+------------------------------------------------------------------+
      //| Breakout Strategy with Iceberg Orders                            |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int channelPeriod = 20;
         double volume = 1.0;
         double visibleVolume = 0.1;
         
         // Calculate indicators
         double upperChannel = iHigh(Symbol(), PERIOD_CURRENT, iHighest(Symbol(), PERIOD_CURRENT, MODE_HIGH, channelPeriod, 1));
         double lowerChannel = iLow(Symbol(), PERIOD_CURRENT, iLowest(Symbol(), PERIOD_CURRENT, MODE_LOW, channelPeriod, 1));
         
         double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Price breaks above upper channel
            if(currentPrice > upperChannel)
            {
               // Create Iceberg Order
               double limitPrice = upperChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_BUY,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy breakout detected. Starting Iceberg Order execution.");
               }
            }
            // Sell signal: Price breaks below lower channel
            else if(currentPrice < lowerChannel)
            {
               // Create Iceberg Order
               double limitPrice = lowerChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_SELL,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Sell breakout detected. Starting Iceberg Order execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("Iceberg Order execution completed.");
               }
            }
         }
      }
      

    In allen drei Beispielen folgt die Integration dem gleichen Muster:

    1. Zustand beibehalten

      • Der boolesche Wert inPosition zeigt an, ob Sie gerade einen Auftrag ausführen.
      • Ein statischer CExecutionManager executionManager lebt über Ticks hinweg, um den Lebenszyklus des gewählten Algorithmus zu verwalten.

    2. Eingabe-Logik

      • Rufen Sie bei Ihrem Einstiegssignal (MA-Crossover, RSI-Schwelle, Channel-Break) die entsprechende Erstellungsmethode im executionManager (TWAP, VWAP oder Iceberg) auf und übergeben Sie dabei Symbol, Gesamtvolumen, Zeitfenster oder Limitpreis, Slice-Parameter und Ordertyp.
      • Wenn die Erstellung erfolgreich war, rufen Sie sofort executionManager.Initialize() auf, setzen inPosition=true und protokollieren Ihren Start.

    3. Laufende Ausführung

      • Solange inPosition wahr ist, ruft OnTick() jedes Mal executionManager.Update() auf.
      • Innerhalb von Update() ruft der Manager intern Execute() nach Bedarf auf, fragt Füllungen ab, behandelt Timeouts oder Marktaktualisierungen und plant den nächsten Slice (oder storniert/ersetzt Child-Orders des Eisbergs).

    4. Abschluss & Ausstieg

      • Nach jeder Aktualisierung überprüfen Sie executionManager.GetAlgorithm()->IsActive(). Sobald der Wert false zurückgegeben wird (alle Intervalle sind abgeschlossen oder das Volumen ist erschöpft), setzen Sie inPosition=false und protokollieren, dass die Ausführung abgeschlossen ist.
      • Im Beispiel der VWAP-Mittelwertumkehr gibt es eine zusätzliche Ausstiegsprüfung: Wenn der Kurs während der Ausführung den RSI-Schwellenwert überschreitet, rufen Sie executionManager.Terminate() auf, um vorzeitig zu beenden.

    Die Regeln für die beiden sind wie folgt:
    1. Trend-Following + TWAP
      Eröffnung: Das Überschreiten des schnellen MA über den langsamen MA löst sie aus.
      Ausführung: Teilt Ihren 1-Lot-Kauf in 5 gleiche Abschnitte über die nächste Stunde auf

    2. Mittelwertumkehr + VWAP
      Eröffnung: RSI < 30 für Käufe, > 70 für Verkäufe
      Ausführung: Verteilt 1 Lot gegen historisches Volumen über 5 Teile in einer Stunde
      Frühzeitiges Ausscheiden: Wenn das Signal umschlägt (z. B. RSI > 70 während eines Kaufs), bricht executionManager.Terminate() die restlichen Teile ab.

    3. Breakout + Eisberg
      Eröffnung: Preis bricht Kanal nach oben (kaufen) oder nach unten (verkaufen)
      Ausführung: Platziert ein schwebenden Limit-Auftrag zum Ausbruchspreis, wobei jeweils nur ~0,1 Lots aufgedeckt und aufgefüllt werden, bis das volle 1 Lot erreicht ist

    Durch den Austausch von CreateTWAP, CreateVWAP oder CreateIcebergOrder können Sie jeden beliebigen Ausführungsalgorithmus in Ihre Signallogik einbinden, ohne dass Sie die Auftragsverwaltungsformulare duplizieren müssen.


    Integrierte Strategie

    Vollständiger Code:

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    
    // Input parameters
    input int FastMA = 20;                  // Fast moving average period
    input int SlowMA = 50;                  // Slow moving average period
    input double TradingVolume = 0.1;       // Trading volume
    input bool UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int g_maHandle1 = INVALID_HANDLE;
    int g_maHandle2 = INVALID_HANDLE;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
    {
       // Initialize execution manager
       g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
       
       // Initialize indicators
       g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
       g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
       
       if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE)
       {
          Print("Failed to create indicator handles");
          return INIT_FAILED;
       }
       
       return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
       // Clean up
       if(g_executionManager != NULL)
       {
          delete g_executionManager;
          g_executionManager = NULL;
       }
       
       // Release indicator handles
       IndicatorRelease(g_maHandle1);
       IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
    {
       // Update execution algorithms
       if(g_executionManager != NULL)
          g_executionManager.UpdateAlgorithms();
       
       // Only process at bar open
       if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
          return;
          
       // Get indicator values
       double fastMA[2], slowMA[2];
       
       if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
          CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0)
       {
          Print("Failed to copy indicator buffers");
          return;
       }
       
       // Check for trend signals
       bool buySignal = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
       bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
       
       // Execute signals using the execution manager
       if(buySignal)
       {
          Print("Buy signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_BUY, true);
          }
       }
       else if(sellSignal)
       {
          Print("Sell signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_SELL, true);
          }
       }
    }
    //+------------------------------------------------------------------+
    

    Der Expertenberater IntegratedStrategy.mq5 beginnt mit der Deklaration seiner Metadaten (Copyright, Link, Version) und enthält die Header für alle unsere Ausführungsalgorithmusklassen und den Performance-Analyzer. Anschließend werden vier vom Nutzer einstellbare Eingaben definiert: die schnellen und langsamen SMA-Perioden, das Gesamthandelsvolumen pro Signal und ein boolesches Flag zum Umschalten der „adaptiven“ Ausführung (bei der der Manager entscheidet, ob TWAP, VWAP oder Iceberg unter der Haube verwendet wird). Ein globaler Zeiger auf CExecutionManager und zwei Indikator-Handles werden ebenfalls deklariert, damit sie zwischen Ticks bestehen bleiben.

    In OnInit() instanziieren wir den Ausführungsmanager - und übergeben ihm das aktuelle Symbol, maximal drei gleichzeitige Algorithmen und unser adaptives Flag - und erstellen dann die beiden SMA-Indikator-Handles. Wenn eines der beiden Handles fehlschlägt, wird die Initialisierung abgebrochen. OnDeinit() räumt einfach auf, indem es den Manager löscht und die Indikator-Handles freigibt, um sicherzustellen, dass kein Speicher oder Handle-Lecks entstehen, wenn der EA entfernt oder die Plattform heruntergefahren wird.

    Die Hauptlogik befindet sich in OnTick(). Zunächst rufen wir UpdateAlgorithms() auf dem Ausführungsmanager auf, sodass alle bestehenden untergeordneten Aufträge (TWAP-Slices, VWAP-Eimer oder Iceberg-Legs) je nach Bedarf bearbeitet, storniert oder aufgefüllt werden. Dann warten wir auf einen neuen Balken (indem wir ihn überspringen, wenn sich das Tick-Volumen noch aufbaut). Wenn der Balken eröffnet, rufen wir die letzten beiden SMA-Werte für die schnelle und die langsame Periode ab. Ein Kreuzen von unten nach oben löst ein Kaufsignal aus, der umgekehrte Fall löst ein Verkaufssignal aus.

    Wenn die adaptive Ausführung aktiviert ist, übergeben wir das Signal und die Volumen an g_executionManager.ExecuteSignal() und überlassen der Methode die Auswahl des geeigneten Algorithmus. Andernfalls starten wir manuell eine TWAP-Instanz für ein einstündiges Fenster und sechs Teile. Dieses Muster trennt Ihre Einstiegslogik (Trenderkennung) sauber von Ihrer Auftragsverwaltungslogik, sodass dieselbe Fassade mehrere Ausführungsstile steuern kann, ohne dass die Vorlage dupliziert wird.

    Nach der Aufnahme von Take Profit ändert sich der Code in:

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    #include <Trade\Trade.mqh>
    
    // Input parameters
    input int    FastMA              = 20;    // Fast moving average period
    input int    SlowMA              = 50;    // Slow moving average period
    input double TradingVolume       = 0.1;   // Trading volume
    input bool   UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    input double EquityTPPercent     = 10.0;  // Equity Take Profit in percent
    input double EquitySLPercent     = 5.0;   // Equity Stop Loss in percent
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int                g_maHandle1       = INVALID_HANDLE;
    int                g_maHandle2       = INVALID_HANDLE;
    double             g_initialEquity   = 0.0;
    CTrade             trade;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit() {
        // Record initial equity
        g_initialEquity = AccountInfoDouble(ACCOUNT_EQUITY);
    
        // Initialize execution manager
        g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
    
        // Initialize indicators
        g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
        g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
    
        if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE) {
            Print("Failed to create indicator handles");
            return INIT_FAILED;
        }
    
        return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason) {
        // Clean up
        if(g_executionManager != NULL) {
            delete g_executionManager;
            g_executionManager = NULL;
        }
    
        // Release indicator handles
        IndicatorRelease(g_maHandle1);
        IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Check equity-based TP and SL, then reset baseline                |
    //+------------------------------------------------------------------+
    void CheckEquityTPandSL() {
        double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
        double tpEquity      = g_initialEquity * (1.0 + EquityTPPercent / 100.0);
        double slEquity      = g_initialEquity * (1.0 - EquitySLPercent / 100.0);
    
        if(currentEquity >= tpEquity) {
            Print("Equity Take Profit reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        } else if(currentEquity <= slEquity) {
            Print("Equity Stop Loss reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        }
    }
    
    //+------------------------------------------------------------------+
    //| Close all open positions                                         |
    //+------------------------------------------------------------------+
    void CloseAllPositions() {
        CPositionInfo  m_position;
        CTrade m_trade;
        for(int i = PositionsTotal() - 1; i >= 0; i--) // loop all Open Positions
            if(m_position.SelectByIndex(i)) { // select a position
                m_trade.PositionClose(m_position.Ticket()); // then delete it --period
            }
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick() {
        // Check and reset equity thresholds
        CheckEquityTPandSL();
    
        // Update execution algorithms
        if(g_executionManager != NULL)
            g_executionManager.UpdateAlgorithms();
    
        // Only process at bar open
        if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
            return;
    
        // Get indicator values
        double fastMA[2], slowMA[2];
    
        if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
                CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0) {
            Print("Failed to copy indicator buffers");
            return;
        }
    
        // Check for trend signals
        bool buySignal  = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
        bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
    
        // Execute signals using the execution manager
        if(buySignal) {
            Print("Buy signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_BUY, true);
            }
        } else if(sellSignal) {
            Print("Sell signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_SELL, true);
            }
        }
    }
    //+------------------------------------------------------------------+
    


    Backtest-Ergebnisse

    1. Kapital- & Saldenkurven

    Die grünen Treppenstufen des Saldos zeigen das buchhalterisch Kapital Ihres Kontos an, wenn der EA eine Position schließt; die blaue Kapitalkurve glättet nicht realisierte P&L zwischen den Handelsgeschäften. Von Januar bis Anfang März ist ein klarer Aufwärtstrend zu erkennen, mit einigen Rückschlägen, die jeweils einen Höchststand von 10-16 % erreichen, bevor die nächste Gewinnserie den Gewinn wiederherstellt. Dieses Muster deutet darauf hin, dass das System in einem Trend gedeiht, aber immer noch erträgliche Aktieneinbrüche erleidet.

    2. Volumen & Risikoauslastung

    Unten schrumpfen die Dreiecke der „Einzahlungslast“ im Laufe der Zeit allmählich - dies ist Ihre Positionsgröße in Prozent des Eigenkapitals. Es beginnt bei 10 % Ihres Guthabens und verringert sich mit steigendem Eigenkapital (bei fester Volumengröße), d. h. unser Risiko pro Handel nimmt mit steigendem Konto ab. Aus diesem Grund bleiben die Drawdowns proportional ähnlich, auch wenn Ihr Kapital in Dollar steigt.

    3. Wichtige Rentabilitätskennzahlen

    • Ersteinzahlung: $1.000

    • Nettogewinn: + 703 $ (eine Rendite von 70% über ~2 Monate)

    • Profit-Faktor: 2,34 (Sie verdienen 2,34 $ für jeden verlorenen $1)

    • Erwartete Auszahlung: durchschnittlich 2,34 $ pro Handel

    • Sharpe Ratio: 5,47 (sehr hohe risikoadjustierte Rendite)

    Diese Zahlen zeigen uns, dass die Strategie nicht nur rentabel ist, sondern auch einen gesunden Puffer über die eigene Volatilität hinaus erwirtschaftet.

    4. Drawdown und Recovery-Faktor

    • Maximaler Drawdown des Kapitals: 156 Punkte oder 9,99 %.

    • Maximaler Kapital-Drawdown: 228 Punkte oder 15,89 %.

    • Recovery-Faktor: 3,08 (Nettogewinn ÷ maximale Inanspruchnahme)

    Ein Erholungsfaktor von mehr als 2 gilt im Allgemeinen als gut, d. h. mit 3,08 erwirtschaften Sie mehr als das Dreifache Ihres schlimmsten Verlusts an Gewinn.

    5. Handelsverteilung

    • Gesamtpositionen: 300 (600 Handelsgeschäfte, d. h. jedes Eröffnen + Schließen zählt als zwei)

    • Gewinnrate: 76% (228 Gewinner gegenüber 72 Verlierer)

    • Durchschnittlicher Gewinn: $5,39

    • Durchschnittlicher Verlust: - $7,31

    Obwohl unsere Gewinnquote und unser Gewinnfaktor sehr hoch sind, müssen wir feststellen, dass unsere Verlierer im Durchschnitt größer sind als die Gewinner - etwas, das wir im Auge behalten sollten, wenn sich die Marktbedingungen ändern.

    6. Streifen und Beständigkeit

    • Maximale Anzahl an aufeinanderfolgenden Siegen: 87 Handelsgeschäfte, + $302

    • Max. aufeinanderfolgende Verluste: 23 Handelsgeschäfte, - $156

    • Durchschnittliche Siegesserie: 57 Handelsgeschäfte

    • Durchschnittliche Pechsträhne: 18 Handelsgeschäfte

    Lange Gewinnserien treiben den Aufwärtstrend voran, während die längste Verlustserie immer noch nur etwa 15 % des Kapitals kostet.


    Schlussfolgerung

    Stellen Sie sich vor, Sie sind ein Einzelhändler in einer überfüllten Marktarena - jeder Tick zählt, jeder Füllpreis verrät Gewinn oder Verlust. Indem Sie TWAP, VWAP und Eisberg-Aufträge in Ihr Toolkit einbinden, reagieren Sie nicht mehr nur auf Preisschwankungen, sondern steuern sie. Diese einst hochkarätigen, institutionellen Algorithmen stehen Ihnen jetzt zur Verfügung, um die Liquidität wie ein Laser zu durchschneiden und chaotische Orderbücher in Chancen zu verwandeln.

    TWAP wird zu Ihrem stetigen Metronom, das Ihre Größe gleichmäßig über ein festgelegtes Intervall verteilt - perfekt, wenn die Gezeiten ruhig sind und Sie einfach nur eine ruhige Fahrt wünschen. VWAP verwandelt Sie in einen versierten Volumen-Tracker, der die stärksten Handelszeiten des Tages angreift und den Puls des Marktes selbst bestimmt. Und wenn Sie Ihre Absichten verschleiern müssen, lassen die Eisberg-Aufträge Ihre wahre Größe unter der Oberfläche verschwinden und geben gerade genug preis, um gefüllt zu werden, ohne die großen Spieler zu verschrecken.

    Aber das sind nicht nur einzelne Tricks. Mit unserem modularen MQL5-Framework können Sie sie in jede beliebige Strategie einbinden - Trendfolger, Mean-Reverser, Ausbruchsjäger - und zwar so einfach wie das Aufsetzen eines neuen Objektivs. Mit einer einzigen ExecutionManager-Fassade können Sie Algorithmen während des Handels austauschen, kombinieren oder sogar überlagern, während der PerformanceAnalyzer wie ein Falke den Überblick behält und Slippage, Shortfall und Marktauswirkungen bis auf den letzten Pip misst.

    Was kommt als Nächstes? Betrachten Sie die Ausführung als ein Lebewesen, das sich anpasst. Lassen Sie Ihren TWAP aus Volatilitätsspitzen lernen. Leiten Sie Ihre VWAP-Teile an die tiefsten Pools weiter. Bringen Sie Ihrem Eisberg bei, zu spüren, wo Raubtiere lauern und sich tiefer zu verstecken. Und warum dort aufhören? Nutzen Sie das maschinelle Lernen, um die perfekte Mikrosekunde für den Abschuss vorherzusagen, oder mischen Sie Auftragstypen zu maßgeschneiderten Hybriden, die Ihrem einzigartigen Vorteil entsprechen.

    Die Welt des Handels steht niemals still - und Ihre Auftragsausführung sollte es auch nicht. Tauchen Sie ein, experimentieren Sie kühn und verwandeln Sie jedes Stückchen, jede Füllung in einen kalkulierten Vorteil. Ihr Vorteil wartet im Code.

    Zu Ihrer Erleichterung finden Sie hier eine Zusammenfassung der in diesem Artikel enthaltenen Dateien:

    Dateiname Beschreibung
    ExecutionAlgorithm.mqh Basisklasse für alle Ausführungsalgorithmen 
    TWAP.mqh Umsetzung des zeitgewichteten Durchschnittspreises
    VWAP.mqh Umsetzung des volumengewichteten Durchschnittspreises
    IcebergOrder.mqh Umsetzung der Eisberg-Aufträge
    PerformanceAnalyzer.mqh Werkzeuge zur Analyse der Ausführungsleistung
    ExecutionManager.mqh Fassade für die einfache Integration in Handelsstrategien
    IntegratedStrategy.mq5
    Beispiel EA mit Integration in eine Handelsstrategie
    IntegratedStrategy - Take Profit.mq5 Beispiel EA mit Integration einer Handelsstrategie mit Take Profit und Stop Loss in Prozent des Kontostandes
    Indem Sie diese fortschrittlichen Ausführungsalgorithmen in Ihr Trading-Toolkit aufnehmen, machen Sie einen bedeutenden Schritt in Richtung eines professionelleren und effizienteren Handels. Ganz gleich, ob Sie die Auswirkungen größerer Geschäfte minimieren, Ihre durchschnittlichen Ausführungspreise verbessern oder einfach Ihren Handelsansatz verfeinern möchten - diese Algorithmen bieten wertvolle Lösungen, die Ihre Handelsleistung auf den heutigen wettbewerbsintensiven Märkten verbessern können.

    Übersetzt aus dem Englischen von MetaQuotes Ltd.
    Originalartikel: https://www.mql5.com/en/articles/17934

    Letzte Kommentare | Zur Diskussion im Händlerforum (6)
    i_vergo
    i_vergo | 16 Mai 2025 in 07:44
    Great Article
    Testing your algo.
    IN file, ExecutionAlgorithm.mqh, added this line request.type_filling = ORDER_FILLING_IOC; in placing order to fix order placing issue.
    Zurück getestet es auf M5, es öffnete nur 1 Handel für 2 Monate Zeitraum, keine partielle Bestellung geöffnet.
    Getestet es auf H1, es nie angewendet, die SL oder TP und alle Trades geschlossen in Verlust.

    auch während der Kompilierung erzeugt es Warnungen
    möglicher Datenverlust aufgrund der Typkonvertierung von 'long' zu 'double' VWAP.mqh 271 41
    möglicher Datenverlust aufgrund der Typumwandlung von 'long' in 'double' VWAP.mqh 272 22
    möglicher Datenverlust durch Typkonvertierung von 'long' nach 'double' VWAP.mqh 449 17
    möglicher Datenverlust durch Typkonvertierung von 'long' nach 'double' PerformanceAnalyzer.mqh 222 17
    möglicher Datenverlust durch Typkonvertierung von 'long' in 'double' ExecutionManager.mqh 418 17


    schlagen Sie vor, wie der Algo getestet werden soll,
    Zeitrahmen. und andere Empfehlungen.
    i_vergo
    i_vergo | 16 Mai 2025 in 07:57
    um die Warnungen zu beheben
    auch beim Kompilieren werden Warnungen erzeugt
    möglicher Datenverlust aufgrund der Typkonvertierung von 'long' nach 'double' VWAP .mqh 271 41
    möglicher Datenverlust aufgrund der Typkonvertierung von 'long' nach 'double' VWAP .mqh 272 22
    möglicher Datenverlust durch Typkonvertierung von 'long' nach 'double' VWAP .mqh 449 17
    möglicher Datenverlust durch Typkonvertierung von 'long' nach 'double' PerformanceAnalyzer .mqh 222 17
    möglicher Datenverlust durch Typkonvertierung von 'long' nach 'double' ExecutionManager .mqh 418 17


    Ich habe die Codezeile
    m_volumeProfile[intervalIndex] += rates[i].tick_volu

    in
    geändert.
    m_volumeProfile[intervalIndex] += (double)rates[i].tick_volume;

    Es behebt die Warnungen
    Jetzt brauchen Sie guidence in Bezug auf meine anderen Fragen, wie
    Zeitrahmen
    und auch
    warum alle Trades während Backtest Ergebnis in Verlust
    wie man diese große Arbeit von Ihnen zu testen.
    Dominic Michael Frehner
    Dominic Michael Frehner | 16 Mai 2025 in 07:57
    i_vergo möglicher Datenverlust aufgrund der Typkonvertierung von 'long' zu 'double' VWAP.mqh 271 41
    möglicher Datenverlust aufgrund der Typumwandlung von 'long' in 'double' VWAP.mqh 272 22
    möglicher Datenverlust durch Typkonvertierung von 'long' nach 'double' VWAP.mqh 449 17
    möglicher Datenverlust durch Typkonvertierung von 'long' nach 'double' PerformanceAnalyzer.mqh 222 17
    möglicher Datenverlust durch Typkonvertierung von 'long' in 'double' ExecutionManager.mqh 418 17


    schlagen Sie vor, wie der Algo getestet werden soll,
    Zeitrahmen. und andere Empfehlungen.

    Die Warnungen sind nicht das Problem, könnten aber schnell behoben werden. Aber ja, es wäre toll, wenn der Autor Schritt für Schritt zeigen könnte, welche Einstellungen und Eingaben er für den Backtest verwendet hat.

    CapeCoddah
    CapeCoddah | 16 Mai 2025 in 12:01

    Ich stimme Dominic zu, da die Warnungen nur Warnungen sind. Die Ergebnisse von I_Virgo sind wahrscheinlich darauf zurückzuführen, dass er den falschen Zeitrahmen und das falsche Währungspaar verwendet hat. Aus dem Backtest-Bericht mit fast 2000 Bars geht hervor, dass es entweder M1 oder M5 als Zeitrahmen mit einem unbekannten Paar war.

    Es wäre schön, wenn MQ den Zeitrahmen und das Währungspaar oder die Währungspaare hinzufügen und auch die Paar-Ergebnisse in mehr Details zum Back-Test-Bericht trennen würde, so dass wir die Back-Test-Ergebnisse des Autors genauer replizieren und seine Anwendbarkeit auf die Forex-Paare bestimmen könnten. Außerdem wäre es äußerst hilfreich, wenn der EA Text auf das Diagramm schreiben könnte, während er läuft.


    Ich denke auch, dass es ein großartiger Artikel ist und plane, ihn gründlich zu studieren, um seine Techniken auf andere EAs zu übertragen.


    CapeCoddah

    Shashin Wijewardhane
    Shashin Wijewardhane | 11 Juli 2025 in 08:40
    //+------------------------------------------------------------------+
    //| Basisklasse für alle Ausführungsalgorithmen|
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;           // Handelssymbol
       double            m_totalVolume;      // Gesamtvolumen für die Ausführung
       double            m_executedVolume;   // Volumen bereits ausgeführt
       double            m_remainingVolume;  // Verbleibendes Volumen zur Ausführung
       datetime          m_startTime;        // Startzeitpunkt der Ausführung
       datetime          m_endTime;          // Endzeitpunkt der Ausführung
       int               m_slippage;         // Erlaubter Schlupf in Punkten
       bool              m_isActive;         // Ist der Algorithmus derzeit aktiv
       
       // Statistik
       double            m_avgExecutionPrice; // Durchschnittlicher Ausführungspreis
       int               m_totalOrders;       // Gesamtzahl der erteilten Aufträge
       int               m_filledOrders;      // Anzahl der ausgeführten Aufträge
       
    public:
       // Konstrukteur
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage);
       
       // Destruktor
       virtual ~CExecutionAlgorithm();
       
       // Virtuelle Methoden, die von abgeleiteten Klassen implementiert werden müssen
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate() = 0;
       
       // Gemeinsame Methoden
       bool              IsActive() { return m_isActive; }
       double            GetExecutedVolume() { return m_executedVolume; }
       double            GetRemainingVolume() { return m_remainingVolume; }
       double            GetAverageExecutionPrice() { return m_avgExecutionPrice; }
       
       // Hilfsmethoden
       bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0);
       bool              ModifyOrder(ulong ticket, double price, double sl, double tp);
       bool              CancelOrder(ulong ticket);
       void              UpdateAverageExecutionPrice(double price, double volume);
       
       // Hilfsmethode zur Ermittlung des geeigneten Füllmodus
       ENUM_ORDER_TYPE_FILLING GetFillingMode();
    };
    
    //+------------------------------------------------------------------+
    //| Konstruktor|
    //+------------------------------------------------------------------+
    CExecutionAlgorithm::CExecutionAlgorithm(string symbol, double volume, 
                                           datetime startTime, datetime endTime, 
                                           int slippage)
    {
       m_symbol = symbol;
       m_totalVolume = volume;
       m_executedVolume = 0.0;
       m_remainingVolume = volume;
       m_startTime = startTime;
       m_endTime = endTime;
       m_slippage = slippage;
       m_isActive = false;
       
       m_avgExecutionPrice = 0.0;
       m_totalOrders = 0;
       m_filledOrders = 0;
    }
    
    //+------------------------------------------------------------------+
    //| Destruktor|
    //+------------------------------------------------------------------+
    CExecutionAlgorithm::~CExecutionAlgorithm()
    {
       // Ressourcen bereinigen, falls erforderlich
    }
    
    //+------------------------------------------------------------------+
    //| Initialisierung des Algorithmus|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::Initialize()
    {
       // Eingaben validieren
       if(m_symbol == "" || m_totalVolume <= 0.0)
       {
          Print("Invalid inputs for execution algorithm");
          return false;
       }
       
       // Prüfen, ob das Symbol existiert
       if(!SymbolSelect(m_symbol, true))
       {
          Print("Symbol not found: ", m_symbol);
          return false;
       }
       
       // Statistik zurücksetzen
       m_executedVolume = 0.0;
       m_remainingVolume = m_totalVolume;
       m_avgExecutionPrice = 0.0;
       m_totalOrders = 0;
       m_filledOrders = 0;
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Geeigneten Füllmodus für das Symbol holen |
    //+------------------------------------------------------------------+
    ENUM_ORDER_TYPE_FILLING CExecutionAlgorithm::GetFillingMode()
    {
       // Abrufen von Symbolfüllmodi
       int filling_modes = (int)SymbolInfoInteger(m_symbol, SYMBOL_FILLING_MODE);
       
       // Prüfen Sie die verfügbaren Befüllungsmodi in der Reihenfolge ihrer Präferenz
       if((filling_modes & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
          return ORDER_FILLING_FOK;
       else if((filling_modes & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
          return ORDER_FILLING_IOC;
       else
          return ORDER_FILLING_RETURN;
    }
    
    //+------------------------------------------------------------------+
    //| Eine Bestellung aufgeben|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0)
    {
       // Eingaben validieren
       if(volume <= 0.0)
       {
          Print("Invalid order volume");
          return false;
       }
       
       // Vorbereiten der Anfrage
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.deviation = m_slippage;
       request.magic = 123456; // Magische Zahl zur Identifizierung
       
       // Geeignete Aktion und Preis je nach Auftragsart festlegen
       if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
       {
          // Marktauftrag
          request.action = TRADE_ACTION_DEAL;
          request.type_filling = GetFillingMode();
          
          if(orderType == ORDER_TYPE_BUY)
             request.price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
          else
             request.price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
       }
       else
       {
          // Schwebende Bestellung
          request.action = TRADE_ACTION_PENDING;
          if(price <= 0.0)
          {
             Print("Price must be specified for pending orders");
             return false;
          }
          request.price = price;
       }
       
       // Senden Sie die Bestellung
       if(!OrderSend(request, result))
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // Prüfen Sie das Ergebnis
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       // Statistiken aktualisieren
       m_totalOrders++;
       
       // Bei Marktaufträgen wird die Ausführungsstatistik sofort aktualisiert.
       if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
       {
          m_filledOrders++;
          UpdateAverageExecutionPrice(request.price, volume);
          m_executedVolume += volume;
          m_remainingVolume -= volume;
       }
       
       Print("Order placed successfully. Ticket: ", result.order, " Volume: ", volume, " Price: ", request.price);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Ändern einer bestehenden Bestellung|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::ModifyOrder(ulong ticket, double price, double sl, double tp)
    {
       // Vorbereiten der Anfrage
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_MODIFY;
       request.order = ticket;
       request.price = price;
       request.sl = sl;
       request.tp = tp;
       
       // Senden Sie die Änderungsanfrage
       if(!OrderSend(request, result))
       {
          Print("OrderModify error: ", GetLastError());
          return false;
       }
       
       // Prüfen Sie das Ergebnis
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderModify failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       Print("Order modified successfully. Ticket: ", ticket);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Stornieren einer bestehenden Bestellung|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::CancelOrder(ulong ticket)
    {
       // Vorbereiten der Anfrage
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_REMOVE;
       request.order = ticket;
       
       // Senden Sie die Stornierungsanfrage
       if(!OrderSend(request, result))
       {
          Print("OrderCancel error: ", GetLastError());
          return false;
       }
       
       // Prüfen Sie das Ergebnis
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderCancel failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       Print("Order cancelled successfully. Ticket: ", ticket);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Aktualisierung des durchschnittlichen Ausführungspreises|
    //+------------------------------------------------------------------+
    void CExecutionAlgorithm::UpdateAverageExecutionPrice(double price, double volume)
    {
       // Berechnen Sie den neuen durchschnittlichen Ausführungspreis
       if(m_executedVolume > 0.0)
       {
          // Gewogener Durchschnitt der alten und neuen Preise
          m_avgExecutionPrice = (m_avgExecutionPrice * m_executedVolume + price * volume) / 
                               (m_executedVolume + volume);
       }
       else
       {
          // Erste Ausführung
          m_avgExecutionPrice = price;
       }
    }
    //+------------------------------------------------------------------+
    Von der Grundstufe bis zur Mittelstufe: Überladen Von der Grundstufe bis zur Mittelstufe: Überladen
    Vielleicht wird dieser Artikel für Programmieranfänger am verwirrendsten sein. In der Tat werde ich hier zeigen, dass nicht immer alle Funktionen und Prozeduren im selben Code eindeutige Namen haben. Ja, wir können problemlos Funktionen und Prozeduren mit demselben Namen verwenden - und das nennt man Überladen.
    Von der Grundstufe bis zur Mittelstufe: Fließkommazahlen Von der Grundstufe bis zur Mittelstufe: Fließkommazahlen
    Dieser Artikel ist eine kurze Einführung in das Konzept der Fließkommazahlen. Da dieser Text sehr komplex ist, lesen Sie ihn bitte aufmerksam und sorgfältig. Erwarten Sie nicht, dass Sie das Fließkommasystem schnell beherrschen. Das wird erst mit der Zeit klar, wenn man Erfahrung damit hat. Aber dieser Artikel wird Ihnen helfen zu verstehen, warum Ihre Anwendung manchmal andere Ergebnisse liefert, als Sie erwarten.
    Von der Grundstufe bis zur Mittelstufe: Template und Typename (I) Von der Grundstufe bis zur Mittelstufe: Template und Typename (I)
    In diesem Artikel beginnen wir mit der Betrachtung eines der Konzepte, das viele Anfänger vermeiden. Das hängt damit zusammen, dass Templates kein einfaches Thema sind, da viele das Grundprinzip, das den Templates zugrunde liegt, nicht verstehen: die Überladung von Funktionen und Prozeduren.
    Portfolio-Optimierung am Devisenmarkt: Synthese von VaR und die Markowitz-Theorie Portfolio-Optimierung am Devisenmarkt: Synthese von VaR und die Markowitz-Theorie
    Wie funktioniert der Portfoliohandel im Forexmarkt? Wie lassen sich die Portfoliotheorie von Markowitz zur Optimierung des Portfolioanteils und das VaR-Modell zur Optimierung des Portfoliorisikos zusammenführen? Wir erstellen einen auf der Portfoliotheorie basierenden Code, der einerseits ein geringes Risiko und andererseits eine akzeptable langfristige Rentabilität gewährleistet.