English Русский 日本語
preview
Quantitative Analyse in MQL5: Implementierung eines vielversprechenden Algorithmus

Quantitative Analyse in MQL5: Implementierung eines vielversprechenden Algorithmus

MetaTrader 5Handelssysteme | 19 April 2024, 13:27
196 0
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Was ist quantitative Analyse auf dem Finanzmarkt?

Was bedeutet quantitative Analyse auf dem Finanzmarkt? Die quantitative Analyse erschien als eine Art Vorläufer des maschinellen Lernens und ist eigentlich ein Teilbereich des statistischen Lernens. In den Tagen, als Computer gerade erst aufkamen, einen ganzen Raum einnahmen und mit Lochkarten arbeiteten, versuchten fortschrittliche Köpfe, sie für die Analyse großer Datenmengen und Statistiken anzupassen. Damals war die Zahl der statistischen Operationen und Funktionen, mit denen die Preise ermittelt werden konnten, äußerst gering, die Funktionen selbst waren recht einfach, und die gefundenen Muster waren nicht besonders komplex.

Bei diesen Studien handelte es sich um einfache Berechnungen zur Ermittlung bestimmter, meist linearer Beziehungen in den Daten.

Die einfachste und am leichtesten zu erlernende Methode der quantitativen Analyse auf den Finanzmärkten ist die Analyse des Spreads zwischen verbundenen Vermögenswerten. Wir können zum Beispiel eine Spanne zwischen zwei korrelierten Vermögenswerten aufzeichnen und mit Hilfe einer quantitativen Analyse den Durchschnitt, das Maximum und den Median dieser Spanne ermitteln. Anhand der quantitativen Beschreibung der Daten können wir nachvollziehen, wie stark ein Vermögenswert vom anderen abgewichen ist, und den Gleichgewichtszustand der beiden Vermögenswerte grob einschätzen, bei dem beide definitiv zurückkehren werden, wenn die Diskrepanz zwischen ihnen beseitigt ist (wenn sich die Vermögenswerte aufeinander zu bewegen). Generell ist der Einsatz der quantitativen Analyse im Pairs-Trading ein sehr interessantes Thema, auf das wir in zukünftigen Artikeln auf jeden Fall eingehen werden. 


Wie die quantitative Analyse von Hedge-Fonds genutzt wird

Der erste Versuch, die quantitative Analyse zu nutzen, war die Praxis von Edward O. Thorp, der in den 1970er Jahren lernte, den Spread zwischen einer Aktie und einem Optionsschein auf diese Aktie zu analysieren und zu berechnen, wie über- oder unterbewertet der Vermögenswert im Verhältnis zu seinem Optionsschein ist. Thorps Computer nahm damals einen ganzen Raum ein und arbeitete ebenfalls mit Lochkarten. Edward O. Thorp war im Allgemeinen der erste, der die quantitative Computeranalyse auf die Finanzmärkte anwendete. Es war ein Durchbruch in dieser Zeit, der von der ganzen Welt anerkannt wurde. Thorp gründete den weltweit ersten „quantitativen“ Hedgefonds. 

Wie Sie verstehen, ist das erste Beispiel für die quantitative Analyse auf dem Aktienmarkt, das uns in den Sinn kommt, ihre Anwendung im „Pair-Trading“ oder „Basket-Trading“. Wir werden diese Optionen auf jeden Fall in Betracht ziehen, aber der heutige Algorithmus für die quantitative Analyse wird auf anderen Grundsätzen beruhen.

Wie nutzen die großen Marktteilnehmer sonst noch die quantitative Analyse? 

Die statistische Arbitrage ermöglicht es ihnen, Preisunterschiede bei Finanzinstrumenten auf verschiedenen Märkten oder zu verschiedenen Zeitpunkten festzustellen. Dies ermöglicht es den Fonds, profitable Handelsmöglichkeiten auf einer Vielzahl von verwandten Märkten zu identifizieren und zu nutzen. Darüber hinaus helfen quantitative Modelle den Hedgefonds bei der Vorhersage künftiger Marktbewegungen auf der Grundlage statistischer Daten, was ihnen hilft, fundierte Handelsentscheidungen zu treffen.

Das Risikomanagement ist eine weitere äußerst wichtige Anwendung der quantitativen Analyse. Hedgefonds verwenden Modelle, um das Risiko in ihren Portfolios zu bewerten und zu steuern. Sie optimieren die Vermögensstruktur auf der Grundlage des Risikos, um mögliche Verluste zu minimieren. Dafür gibt es verschiedene Beispiele, wie die Portfoliooptimierung nach der Portfoliotheorie von Markowitz (die auf dem Risiko basiert, damit die Abweichung des Portfolios den potenziellen Gewinn nicht übersteigt) und das Risikomanagement nach dem VaR-System. Letzteres ist ein einzigartiges Modell, mit dem wir den Drawdown berechnen können, den wir mit einer Wahrscheinlichkeit von 99 % nicht überschreiten werden.

Natürlich lässt sich der reale Markt manchmal nur schwer mit mathematischen Mitteln beschreiben, sodass es auch negative Beispiele gibt. Der LTCM-Hedgefonds rechnete 1998 damit, dass seine Positionen keine großen Verluste bringen würden, und ging mit einer Arbitragestrategie ins Rennen, die auf der Grundlage einer quantitativen Analyse auf die Spanne zwischen langfristigen und kurzfristigen US-Anleihen abzielte. Russland geriet in Verzug, in Asien gab es eine Krise, und dies führte durch den Schmetterlingseffekt zu einer Panik auf dem Markt für US-Staatsanleihen. Der LTCM-Fonds verwendete Modelle, die darauf hindeuteten, dass die Spanne ungewöhnlich hoch war, dass der Kurs auf jeden Fall in die entgegengesetzte Richtung „rollen“ würde und dass die Positionen des Fonds auf jeden Fall mit Gewinn geschlossen werden würden.

Infolgedessen wandte der Fonds Mittelwertbildung an, gewann extrem aggressiv eine große Hebelwirkung, indem er sich mit Vermögenswerten verschuldete, und ging in die Luft, obwohl Nobelpreisträger unter den Mitarbeitern des Unternehmens von der Unmöglichkeit eines solchen Ergebnisses sprachen. Dies war der Fall, als ein quantitatives Analysemodell mit der Bezeichnung VaR fast den gesamten US-Markt zerstörte. Der Fed-Vorsitzende Alan Greenspan musste eilig die Chefs der größten US-Banken anrufen, um die marginalen Positionen des Fonds aufzukaufen, da sonst der Verkauf eines so riesigen Pools von Vermögenswerten „an den Markt“ einen sofortigen Reset des US-Aktienmarktes und eine Panik ausgelöst hätte, die schlimmer als die Große Depression gewesen wäre.

Daher ist es wichtig, bei der quantitativen Analyse und Mittelwertbildung von Indikatoren die Enden der Normalverteilung zu berücksichtigen. Die glockenförmige Wahrscheinlichkeitskurve hat im Falle der Finanzmärkte „fette Enden“, die erhebliche Abweichungen widerspiegeln, die auch als „schwarze Schwäne“ bezeichnet werden. Einerseits sind sie statistisch extrem unwahrscheinlich, andererseits können sie aufgrund ihres Ausmaßes und ihrer Macht die Portfolios von Anlegern und Hedge-Fonds zerstören, marginale Positionen eliminieren, Märkte zerstören und sie in jedem neuen Zyklus verändern. Das haben wir 1998, 2008, 2020 und 2022 erlebt. Außerdem werden wir dies in Zukunft noch viele Male erleben.

Die quantitative Analyse bringt den Hedgefonds sehr viel und wird von ihnen ständig bei ihrer täglichen Arbeit eingesetzt. Aber es ist wichtig, sich daran zu erinnern, dass es keine solchen Funktionen gibt, die in der Lage sind, die Entscheidungen von Millionen von Menschen, ihre Panik und ihre Reaktionen auf bestimmte Ereignisse zu berechnen. Es ist auch wichtig, sich an die Enden der Normalverteilung zu erinnern, die bei aggressiven Handelstaktiken die Einlage ruinieren können. 


Basis des Algorithmus: Zählen von Bewegungswellen

Die Grundlage unserer Idee wurde erstmals von dem Händler Artem Zvezdin formuliert, der die Größe der Kursbewegungswellen berechnet, um zu verstehen, wie über- oder unterbewertet ein Vermögenswert im Verhältnis zu sich selbst ist. Wir zählen zum Beispiel die Aufwärts- und Abwärtswellen der letzten 500-5000 Balken, um zu verstehen, wie weit sich der Preis in jedem seiner kleinen Zyklen bewegt hat. Jeder Zyklus der Preisbewegung spiegelt die Positionen, das Geld und die Kauf- oder Verkaufsentscheidungen anderer wider. Jeder neue Zyklus ist eine neue Geburt und ein neuer Tod des Marktes. Wir werden das Konzept der Analyse von Kursbewegungen ohne Rollback von oben nach unten anwenden. Dies ist eine separate Gruppe von Teilnehmern, die sich ungefähr gleich verhalten, sodass wir davon ausgehen, dass die Länge der Zyklen immer ungefähr gleich sein wird. Wir werden die durchschnittliche Kursbewegung mit Hilfe des ZigZag-Indikators berechnen, der im Standardpaket des MetaTrader 5-Terminals enthalten ist.

Schauen wir uns den Expert Advisor an, den ich im Rahmen dieses Artikels erstellt habe. Werfen wir zunächst einen Blick auf den Kopfbereich des EA. Die Einstellungen sind hier recht einfach. Für den Handel verwenden wir die Standard-Handelsbibliothek. Bei den Lot-Einstellungen kann entweder ein Losgröße angeben werden, um ein festes Volumen zu handeln, oder eine Losgrößen-Berechnung, die auf dem Saldo basiert. Wenn wir einen Gewinn beim Schließen größer als 0 angeben, schließt der EA die Position auf der Grundlage des Gesamtgewinns. Stop-Loss und Take-Profit werden auf Basis des ATR-Wertes berechnet, d.h. sie hängen von der aktuellen Volatilität des Instruments ab. Die ZigZag-Einstellungen für die Berechnungen des EA sind in der Regel Standardeinstellungen, auf die wir nicht näher eingehen werden. Bitte beachten Sie auch, dass unsere EA-Vorlage mehrwährungsfähig ist und mit einer Vielzahl von Vermögenswerten arbeiten kann. Wir benötigen dies, um das Gesamtrisiko durch den Handel mit „Baskets“ von in Zusammenhang stehender Vermögenswerte in zukünftigen Versionen des Expert Advisors zu reduzieren. Die aktuelle Version 0.90 funktioniert nur mit einem Symbol.

//+------------------------------------------------------------------+
//|                                          QuantAnalysisSample.mq5 |
//|                                                   Copyright 2023 |
//|                                                Evgeniy Koshtenko |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2023, Evgeniy Koshtenko"
#property link        "https://www.mql5.com"
#property version     "0.90"
#property strict

#include <Trade\Trade.mqh>
#include <Graphics\Graphic.mqh>
#include <Math\Stat\Normal.mqh>
#include <Math\Stat\Math.mqh>
CTrade trade;
//--- Inputs
input double Lots       = 0.1;      // lot
input double Risk       = 0.1;     // risk
input double Profit     = 0;     // profit
input int StopLoss      = 0;        // ATR stop loss
input int TakeProfit    = 0;        // ATR take profit
input string Symbol1    = "EURUSD";
input int    Magic      = 777;    // magic number
//--- Indicator inputs
input uint   InpDepth       =  120;   // ZigZag Depth
input uint   InpDeviation   =  50;    // ZigZag Deviation
input uint   InpBackstep    =  30;    // ZigZag Backstep
input uchar  InpPivotPoint  =  1;    // ZigZag pivot point
datetime t=0;
double last=0;
double countMovements;
double currentMovement;
// Global variable for storing the indicator descriptor
int zigzagHandle;

Schauen wir uns nun die übrigen Funktionen des EA an. Die Funktionen der Initialisierung und Deinitialisierung sind im Allgemeinen einfach und verständlich. Wir legen die magische Nummer des EA fest, eine eindeutige Kennung, die es dem EA ermöglicht, seine Aufträge von anderen zu unterscheiden. Gleichzeitig setzen wir das Handle in einer zusätzlichen selbst geschriebenen Funktion, denn wenn wir ein Multicurrency-Handle direkt über OnInit laden, wird der EA einen Fehler ausgeben. Deshalb verwenden wir diese recht einfache und unkomplizierte Lösung.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   trade.SetExpertMagicNumber(Magic);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert initialization function custom                            |
//+------------------------------------------------------------------+
int OnIniti(string symb)
  {// Loading the ZigZag indicator
   zigzagHandle = iCustom(symb, _Period, "ZigZag", InpDepth, InpDeviation, InpBackstep, InpPivotPoint);
   if (zigzagHandle == INVALID_HANDLE)
     {
      Print("Error loading the ZigZag indicator: ", GetLastError());
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Comment("");
  }

Schauen wir uns nun weitere Funktionen des Expert Advisors an. Als Nächstes haben wir Funktionen zur Berechnung des Gesamtgewinns für alle Positionen und eine Funktion zum vollständigen Schließen aller Aufträge:

//+------------------------------------------------------------------+
//|  Position Profit                                                 |
//+------------------------------------------------------------------+
double AllProfit(int type=-1)
  {
   double p=0;

    for(int i=PositionsTotal()-1; i>=0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)))
        {
         if(PositionGetInteger(POSITION_MAGIC)==Magic)
           {
            if(PositionGetInteger(POSITION_TYPE)==type || type==-1)
               p+=PositionGetDouble(POSITION_PROFIT);
           }
        }
     }

   return(p);
  }
//+------------------------------------------------------------------+
//|   CloseAll                                                       |
//+------------------------------------------------------------------+
void CloseAll(int type=-1)
  {
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)))
        {
         if(PositionGetInteger(POSITION_MAGIC)==Magic)
           {
            if(PositionGetInteger(POSITION_TYPE)==type || type==-1)
               trade.PositionClose(PositionGetTicket(i));
           }
        }
     }
  }

Dann gibt es die Funktion zur Berechnung der Losgröße und die Funktion zur Berechnung der Anzahl der offenen Positionen:

//+------------------------------------------------------------------+
//|     CountTrades                                                  |
//+------------------------------------------------------------------+
int CountTrades(string symb)
  {
   int count=0;

   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)))
        {
         if(PositionGetString(POSITION_SYMBOL)==symb)
           {
            count++;
           }
        }
     }
   return(count);
  }
//+------------------------------------------------------------------+
//|     Lot                                                          |
//+------------------------------------------------------------------+  
double Lot()
  {
   double lot=Lots;

   if(Risk>0)
      lot=AccountInfoDouble(ACCOUNT_BALANCE)*Risk/100000;

   return(NormalizeDouble(lot,2));
  }

Außerdem gibt es Funktionen zur Berechnung des letzten Verkaufspreises für Käufe und Verkäufe (die wir später verwenden werden) und eine Funktion zur Bestimmung der Richtung der Position.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double FindLastBuyPrice(string symb)
  {
   double pr=0;

   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)) && PositionGetInteger(POSITION_TYPE)==0)
        {
         if(PositionGetString(POSITION_SYMBOL)==symb)
           {
            pr=PositionGetDouble(POSITION_PRICE_OPEN);
            break;
           }
        }
     }
   return(pr);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double FindLastSellPrice(string symb)
  {
   double pr=0;

   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)) && PositionGetInteger(POSITION_TYPE)==1)
        {
         if(PositionGetString(POSITION_SYMBOL)==symb)
           {
            pr=PositionGetDouble(POSITION_PRICE_OPEN);
            break;
           }
        }
     }
   return(pr);
  }
//+------------------------------------------------------------------+
//|  PositionType                                                    |
//+------------------------------------------------------------------+
int PositionType(string symb)
  {
   int type=8;

   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)))
        {
         if(PositionGetString(POSITION_SYMBOL)==symb)
           {
            type=(int)PositionGetInteger(POSITION_TYPE);
            break;
           }
        }
     }
   return(type);
  }

Und natürlich ist unsere wichtigste Funktion die der Berechnung der durchschnittlichen und aktuellen Bewegung. Sie werden nicht in Punkten, sondern der Einfachheit halber in der Bewegung einer Preiseinheit berechnet. Es ist ganz einfach: Wir rufen unsere „nutzerdefinierte Initialisierung“ auf, kopieren die Puffer, und in der for-Schleife berechnen wir die Größe der Preisbewegung vom oberen Ende des ZigZag bis zu seinem letzten Extremwert. Die Funktion gibt die aktuelle Bewegung in Einheiten der Preisbewegung und die durchschnittliche Bewegung aus. 

//+------------------------------------------------------------------+
//|     CalculateAverageMovement                                     |
//+------------------------------------------------------------------+ 
void CalculateAverageMovement(string symb, double &averageMovement, double &currentMovement) {
    const int lookback = 500; // Number of bars for analysis
    double sumMovements = 0.0;
    int countMovements = 0;
    double lastExtremePrice = 0.0;
    double zigzagArray[500]; // Array to store ZigZag values
    OnIniti(symb);
    // Copy ZigZag values to array
    if (CopyBuffer(zigzagHandle, 0, 0, lookback, zigzagArray) <= 0) {
        Print("Error copying indicator data");
        averageMovement = -1;
        currentMovement = -1;
        return;
    }

    // Copy ZigZag values to array
    if (CopyBuffer(zigzagHandle, 0, 0, lookback, zigzagArray) <= 0) {
        Print("Error copying indicator data");
        averageMovement = -1;
        currentMovement = -1;
        return;
    }

    for (int i = 0; i < lookback; i++) {
        if (zigzagArray[i] != 0 && zigzagArray[i] != lastExtremePrice) {
            if (lastExtremePrice != 0) {
                // Determine the movement direction
                double movement = zigzagArray[i] - lastExtremePrice;
                sumMovements += movement;
                countMovements++;
            }
            lastExtremePrice = zigzagArray[i];
        }
    }

    // Calculate the current movement
    double lastMovement = iClose(symb, _Period, 0) - lastExtremePrice;
    currentMovement = lastMovement;

    // Calculate the average movement
    averageMovement = countMovements > 0 ? sumMovements / countMovements : 0.0;

    // Print the result
    Print("Average movement: ", averageMovement);
    Print("Current movement: ", currentMovement);

    // Release resources
    IndicatorRelease(zigzagHandle);
}

Eine weitere Funktion, die zu den wichtigsten gehört, ist die Funktion des Handels mit mehreren Währungen auf der Grundlage von Signalen, die zeigen, dass die aktuelle Kursbewegung ihren Durchschnittswert übersteigt. Take-Profit und Stop-Loss werden auf Basis der ATR festgelegt. Der ATR wird auch für Rasterschritte (Mittelwertbildung) verwendet. Die Position wird bei einem neuen Balken eröffnet. Das ist wichtig für uns. Diese Funktion wird dann in OnTick aufgerufen und arbeitet dann entweder mit einem oder mehreren Symbolen. Ich habe den EA noch nicht erfolgreich auf mehreren Symbolen laufen lassen können, wie ich bereits sagte, werde ich nur das eine Symbol verwenden, auf dessen Chart der EA gestartet wird. Dieses Symbol sollte in den EA-Einstellungen angegeben werden. 

//+------------------------------------------------------------------+
//| Expert Trade unction                                             |
//+------------------------------------------------------------------+
void Trade(string symb)
  {
   double averageMovement = 0;
   double currentMovement = 0;
   double pr=0,sl=0,tp=0,hi=0,lo=0;
// Call function for calculation
   CalculateAverageMovement(symb, averageMovement, currentMovement);

// Use results
   double Ask = SymbolInfoDouble(symb, SYMBOL_ASK);
   double Bid = SymbolInfoDouble(symb, SYMBOL_BID);
   int dg=(int)SymbolInfoInteger(symb,SYMBOL_DIGITS);
   double pp=SymbolInfoDouble(symb,SYMBOL_POINT);
  
   double atr = iATR(symb, PERIOD_CURRENT, 3);
     
// Here define your logic for buying and selling
   bool sell  = currentMovement > -averageMovement; // Buy condition
   bool buy = -currentMovement > averageMovement; // Sell condition
  
   if(AllProfit()>Profit && Profit>0)
      CloseAll();

   if(t!=iTime(symb,PERIOD_CURRENT,0))
     {
      if(buy && CountTrades(symb)<1)
        {
         if(StopLoss>0)
            sl=NormalizeDouble(Bid-(atr*StopLoss)*Point(),_Digits);
         if(TakeProfit>0)
            tp=NormalizeDouble(Bid+(atr*TakeProfit)*Point(),_Digits);
         pr=NormalizeDouble(Bid,dg);
         trade.Buy(Lot(),symb,pr,sl,tp,"");
         last=pr;
        }
      if(sell && CountTrades(symb)<1)
        {
         if(StopLoss>0)
            sl=NormalizeDouble(Ask+(atr*StopLoss)*Point(),_Digits);
         if(TakeProfit>0)
            tp=NormalizeDouble(Ask-(atr*TakeProfit)*Point(),_Digits);
         pr=NormalizeDouble(Ask,dg);
         trade.Sell(Lot(),symb,Ask,sl,tp,"");
         last=pr;
        }
      if(CountTrades(symb)>0)
        {
         if(PositionType(symb)==0 && (FindLastBuyPrice(symb)-Ask)/pp>=atr*30)
           {
            if(StopLoss>0)
               sl=NormalizeDouble(Bid-(atr*StopLoss)*Point(),_Digits);
            if(TakeProfit>0)
               tp=NormalizeDouble(Bid+(atr*TakeProfit)*Point(),_Digits);
            trade.Buy(Lot(),symb,Ask,sl,tp);
           }
         if(PositionType(symb)==1 && (Bid-FindLastSellPrice(symb))/pp>=atr*30)
           {
            if(StopLoss>0)
               sl=NormalizeDouble(Ask+(atr*StopLoss)*Point(),_Digits);
            if(TakeProfit>0)
               tp=NormalizeDouble(Ask-(atr*TakeProfit)*Point(),_Digits);
            trade.Sell(Lot(),symb,Bid,sl,tp);
           }
        }
      t=iTime(symb,0,0);
     }
  }


Prüfung des Modells

Jetzt kommt der spaßige Teil: Wir werden unser Modell auf dem realen Markt testen. Bitte beachten Sie, dass Berechnungen in Schleifen recht rechenintensiv sind, weshalb es sinnvoller ist, den EA nur mit Eröffnungskursen laufen zu lassen. Führen wir einen einzigen Test für EURUSD durch, Eröffnungspreis, H1-Zeitrahmen, vom 1. Januar 2020 bis zum 6. Dezember 2023:


Ein einzelner Test ist rentabel, aber der Drawdown ist hoch. Niemand möchte beim Handel zusätzliche Risiken eingehen. Denken Sie daran, dass wir auch ein gewinnorientiertes Schließen haben. Wir können einen Test für ein Netting-Konto durchführen

Um einen Test mit gewinnbasiertem Abschluss durchzuführen, setzen wir das Schließen mit einem Gewinn über 0. Versuchen wir es mit einem Test. Vielleicht werden wir einen stabilen Test bekommen. Führen wir den EA für denselben Vermögenswert zu offenen Preisen aus. Unser Kontotyp ist ein Hedging-Konto. Und das ist, was wir sehen:


Der EA erwies sich aufgrund der Mittelwertbildung als äußerst riskant. Versuchen wir, den gleichen Test mit einem Netting-Konto durchzuführen.


Wieder haben wir einen großen Drawdown; der Gewinn ist das Risiko überhaupt nicht wert. Versuchen wir, den Code zu überarbeiten. Dieses Mal werden wir das Schließen durch ein Signal implementieren (wenn ein Kaufsignal in ein Verkaufssignal umschlägt, werden die vorherigen Positionen geschlossen). Wir fügen das Schließen nach Gewinn mit folgendem Code hinzu:

if (CloseSig)
   {
      if (buy)
         CloseAll(1);
      if (sell)
         CloseAll(0);
   }

Und fügen wir diese Einstellung hinzu:

input bool CloseSig     = 1;        // close by signal

Wiederholen wir den Test. Die Ergebnisse sind wieder nicht gut:


Tests können im Allgemeinen nicht als ideal bezeichnet werden. Der Drawdown ist enorm, sowohl auf dem Netting- als auch auf dem Hedging-Konto gibt es große Drawdowns. Außerdem bringt ein Schließen auf der Grundlage eines Signals keine positiven Ergebnisse und ist im Allgemeinen unrentabel. Das ist ziemlich beunruhigend.


Schlussfolgerung

Wir haben uns ein einfaches Beispiel für die Erstellung eines grundlegenden und einfachen quantitativen Analysealgorithmus in MQL5 angesehen. Wir zählten die Wellen der Kursbewegung, verglichen sie mit den Durchschnittswerten und trafen auf der Grundlage dieser Daten eine Entscheidung zum Kauf oder Verkauf. Leider führte dies zu einem verlustbringenden Algorithmus, obwohl die Grundlage der Idee ziemlich gut war. In künftigen Artikeln werden wir uns weiter mit der quantitativen Analyse befassen.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/13835

Beigefügte Dateien |
Algorithmen zur Optimierung mit Populationen: der Algorithmus Simulated Annealing (SA). Teil I Algorithmen zur Optimierung mit Populationen: der Algorithmus Simulated Annealing (SA). Teil I
Der Algorithmus des Simulated Annealing ist eine Metaheuristik, die vom Metallglühprozess inspiriert ist. In diesem Artikel führen wir eine gründliche Analyse des Algorithmus durch und räumen mit einer Reihe von weit verbreiteten Überzeugungen und Mythen rund um diese weithin bekannte Optimierungsmethode auf. Der zweite Teil des Artikels befasst sich mit dem nutzerdefinierten Algorithmus Simulated Isotropic Annealing (SIA).
Klassifizierungsmodelle in der Bibliothek Scikit-Learn und ihr Export nach ONNX Klassifizierungsmodelle in der Bibliothek Scikit-Learn und ihr Export nach ONNX
In diesem Artikel werden wir die Anwendung aller in der Bibliothek Scikit-Learn verfügbaren Klassifizierungsmodelle untersuchen, um die Klassifizierungsaufgabe im Iris-Datensatz von Fisher, zu lösen. Wir werden versuchen, diese Modelle in das ONNX-Format zu konvertieren und die resultierenden Modelle in MQL5-Programmen zu verwenden. Außerdem werden wir die Genauigkeit der Originalmodelle mit ihren ONNX-Versionen auf dem vollständigen Iris-Datensatz vergleichen.
Algorithmen zur Optimierung mit Populationen: Umformen, Verschieben von Wahrscheinlichkeitsverteilungen und der Test auf Smart Cephalopod (SC) Algorithmen zur Optimierung mit Populationen: Umformen, Verschieben von Wahrscheinlichkeitsverteilungen und der Test auf Smart Cephalopod (SC)
Der Artikel untersucht die Auswirkungen einer Formveränderung von Wahrscheinlichkeitsverteilungen auf die Leistung von Optimierungsalgorithmen. Wir werden Experimente mit dem Testalgorithmus Smart Cephalopod (SC) durchführen, um die Effizienz verschiedener Wahrscheinlichkeitsverteilungen im Zusammenhang mit Optimierungsproblemen zu bewerten.
Algorithmen zur Optimierung mit Populationen: Nelder-Mead- oder Simplex-Suchverfahren (NM) Algorithmen zur Optimierung mit Populationen: Nelder-Mead- oder Simplex-Suchverfahren (NM)
Der Artikel stellt eine vollständige Untersuchung der Nelder-Mead-Methode vor und erklärt, wie das Simplex (Funktionsparameterraum) bei jeder Iteration geändert und neu angeordnet wird, um eine optimale Lösung zu erreichen, und beschreibt, wie die Methode verbessert werden kann.