English 日本語
preview
Datenwissenschaft und ML (Teil 37): Mit Kerzenmustern und AI den Markt schlagen

Datenwissenschaft und ML (Teil 37): Mit Kerzenmustern und AI den Markt schlagen

MetaTrader 5Handelssysteme |
114 5
Omega J Msigwa
Omega J Msigwa

Inhalt



Einführung

Die erste Strategie, die ich jemals beim Handel verwendet habe, basierte auf Kerzen. Ich würde es jetzt nicht als Strategie bezeichnen, aber mein erstes Mal, als ich einen Handel eröffnete, war auf einige Kerzenmuster zurückzuführen, die ich durch die Lektüre eines Buches namens „Candlestick Trading Bible“ von Honma Munehisa gelernt hatte, das mir von einem Freund empfohlen wurde.

Kerzenmuster in Finanzcharts werden von Händlern verwendet, um mögliche Preisbewegungen auf der Grundlage vergangener Muster zu bestimmen. Sie werden durch Auf- und Abwärtsbewegungen des Preises erzeugt, die zwar zufällig erscheinen, aber von Händlern verwendet werden, um die kurzfristige Richtung des Preises vorherzusagen.

Dieses Konzept wurde in den 1700er Jahren von einem Händler namens Honma Munehisa entwickelt, der als der erfolgreichste Händler der Geschichte gilt. Zu seiner Zeit war er als Gott der Märkte bekannt, und seine Entdeckungen brachten ihm heute mehr als 10 Milliarden Dollar ein. 

Munehisa entdeckte, dass die Kosten für Angebot und Nachfrage zwar den Preis für Reis bestimmen, die Märkte aber auch von menschlichen Emotionen beeinflusst werden.

Diese menschlichen Emotionen können in einer Kerze widergespiegelt werden, wo sich die Preisbewegung in verschiedenen Farben darstellt. Oft wird eine schwarze Kerze für eine Abwärtsbewegung verwendet und eine weiße Kerze repräsentiert oft eine Aufwärtsbewegung. Diese Farben spielen heutzutage keine Rolle, weil sie in jeder Handelsplattform geändert werden können.


Die Grundlagen einer Kerze

Die Schatten oder Dochte der Kerzen zeigen die Tageshöchst- und -tiefstkurse und deren Vergleich mit den Eröffnungs- und Schlusskursen. Die Form der Kerze variiert je nach dem Verhältnis zwischen den OHLC-Kursen (Open, High, Low und Close) der Kerze. Es gibt eine ganze Reihe von Kerzenmustern, die Munehisa seinerzeit eingeführt hat, und einige, die in letzter Zeit von Händlern entwickelt wurden.

Basierend auf diesen Kerzenmustern werden wir diese Kerzenmuster identifizieren, sammeln und auf maschinelle Lernmodelle anwenden und beobachten, wie diese Kerzenmuster unseren KI-basierten Handelsmodellen einen Mehrwert verleihen und uns dabei helfen können, die Finanzmärkte zu schlagen.


Kerzenmuster

Da es viele Kerzenmuster gibt, werden wir nur 10 Muster besprechen. Der Hauptgrund für diese 10 ist, dass sie einfach zu verstehen sind und sich nur auf einen einzigen Balken beziehen, d.h. sie können auf dem aktuellen Balken erkannt werden.

Wir verwerfen alle Kerzenmuster, bei denen zur Erkennung mehrere Kerzen verarbeitet werden müssen.

Das Kodierungsformat für die CTALib-Klasse wurde von  TALib (Technical Analysis Library) inspiriert, einer C#-, C++- und Python-basierten Bibliothek für die technische Analyse, die mehrere Funktionen zur Erkennung von Kerzenmustern enthält.

Eine weiße Kerze

Dies ist eine Aufwärtskerze, bei der der Schlusskurs höher ist als der Eröffnungskurs. Diese einfache Aufwärtskerze zeigt einen Aufwärtsimpuls an, sie bietet keine Vorhersage, sondern ist nur ein Hinweis auf die Richtung der Kerze.

Wir können es wie folgt kodieren.

bool CTALib::CDLWHITECANDLE(double open, double close)
 {
   return (close > open);
 }

Eine schwarze Kerze

Im Gegensatz zur weißen Kerze handelt es sich hier um eine Abwärtskerze, bei der der Eröffnungskurs höher ist als der Schlusskurs. Diese einfache Abwärtskerze deutet auf ein Abwärtsmomentum hin.

Ähnlich wie die weiße Kerze bietet sie keine Vorhersage, sondern ist nur ein Hinweis auf die Richtung der Kerze.

bool CTALib::CDLBLACKCANDLE(double open,double close)
 {
   return (open > close);
 }

Eine Doji-Kerze

Eine Doji-Kerze ist eine Kerze, bei der sowohl das Eröffnungs- als auch das Schlusskursniveau praktisch gleich sind. Sie sieht aus wie ein Kreuz, manchmal ein umgekehrtes Kreuz, oder ein Pluszeichen. Dieses Kerzenmuster ist selten, da es oft innerhalb von mehreren Kerzen auftritt. Es handelt sich in der Regel um ein Trendumkehrsignal, das aber auch Unentschlossenheit über die zukünftigen Preise am Markt signalisieren kann.

Es gibt einige Variationen, wie in der Abbildung unten gezeigt.

Die Kodierung einer Funktion zur Erkennung einer Doji-Kerzen kann schwierig sein, einfach aufgrund der Definition einer Doji-Kerzen. Damit eine Kerze als Doji betrachtet werden kann, müssen sowohl der Eröffnungs- als auch der Schlusskurs gleich sein. Angesichts des Volumens und der Volatilität auf dem heutigen Markt ist es äußerst selten, dass der Eröffnungs- und der Schlusskurs gleich sind. Selbst wenn der Kurs zum Beispiel zu nahe beieinander liegt - Eröffnungskurs = 1,0000 und Schlusskurs = 1,0001 - sind diese beiden für einen Computer nicht gleich.

Wenn die Differenz zwischen Eröffnungs- und Schlusskurs innerhalb dieses Wertes liegt, können wir davon ausgehen, dass die Kurse zu nahe beieinander liegen und daraus schließen, dass es sich um ein Doji-Kerzenmuster handelt.

bool CTALib::CDLDOJI(double open,double close, double torrelance=3)
 {
   return (fabs(open-close)<torrelance*_Point); //A torrelance in market points
 }

Dragonfly Doji

Das Kerzenmuster eines Dragonfly-Doji signalisiert eine mögliche Kursumkehr bei einem Handelsinstruments. Wenn ein Dragonfly-Doji in einem Abwärtstrend auftaucht, ist das ein guter Hinweis darauf, dass die Kurse bald steigen oder der Abwärtstrend zu Ende sein könnte.

Dragonfly-Doji

Da es sich um eine Doji-Kerze handelt, deren unterer Schatten länger ist als der obere, können wir im Code zunächst sicherstellen, dass es sich um eine Doji-Kerze handelt, und müssen dann explizit sicherstellen, dass der untere Schatten doppelt so lang ist wie der obere Schatten.

bool CTALib::CDLDRAGONFLYDOJI(double open, 
                              double high, 
                              double low, 
                              double close, 
                              double body_torrelance = 3, 
                              double shadow_ratio = 2.0)
{
   double body_size    = MathAbs(open - close);
   double upper_shadow = upperShadowCalc(open, close, high);
   double lower_shadow = lowerShadowCalc(open, close, low);

   //--- Body is very small (like a Doji)
   if (CDLDOJI(open, close, body_torrelance))
   {
      //--- Lower shadow is significantly longer than upper shadow
      if (lower_shadow > upper_shadow * shadow_ratio)
         return true;
   }

   return false;
}

Gravestone doji

Dies ist eine Doji-Kerze mit einem langen oberen Schatten (Docht). Es handelt sich um die Strategie einer Abwärts-Umkehr, die auf eine Änderung der Marktrichtung hinweist.

Wenn diese Kerze während eines Aufwärtstrends erscheint, deutet dies darauf hin, dass der aktuelle Trend zu Ende sein könnte und ein Abwärtstrend bevorsteht.

Da es sich um eine Doji-Kerze handelt, deren oberer Schatten länger ist als der untere, können wir im Code zunächst sicherstellen, dass es sich um eine Doji-Kerze handelt, und müssen dann explizit sicherstellen, dass der obere Schatten zweimal länger ist als der untere.

bool CTALib::CDLGRAVESTONEDOJI(double open, 
                               double high, 
                               double low, 
                               double close, 
                               double body_torrelance = 3, 
                               double shadow_ratio = 2.0)
{
   double body_size    = MathAbs(open - close);
   double upper_shadow = upperShadowCalc(open, close, high);
   double lower_shadow = lowerShadowCalc(open, close, low);

   //--- Body is very small (like a Doji)
   if (CDLDOJI(open, close, body_torrelance))
   {
      //--- Lower shadow is significantly longer than upper shadow
      if (upper_shadow > lower_shadow * shadow_ratio)
         return true;
   }

   return false;
}

Hammer

Dies ist eine Kerze mit einem kleinen Körper an der Spitze der Kerze und einem langen unteren Schatten. Es handelt sich um ein Aufwärts-Umkehrsignal, das, wenn es bei einem Abwärtstrend auftritt, bedeutet, dass eine potenzielle Aufwärtsbewegung bevorsteht.

Das Kerzenmuster Hammer

Kerzenmuster sind abstrakt und verwirrend. Manchmal kann ein Muster variieren, je nachdem, wie man es betrachtet und wonach man zu einem bestimmten Zeitpunkt sucht. Das Muster eines Hammers kann mit einem Dragonfly-Doji verwechselt werden, denn was diese beiden unterscheidet, ist nur die Größe ihres Körpers, aber sie haben die gleichen Merkmale.

Dies ist eines der Probleme, auf die wir bei der Arbeit mit Kerzenmustern häufig stoßen.

Dieses Problem kann durch die Festlegung expliziter Regeln und Schwellenwerte gelöst werden, die je nach Instrument (Symbol) und anderen Anforderungen angepasst werden können.

bool CTALib::CDLHAMMER(double open, 
                       double high, 
                       double low, 
                       double close, 
                       double min_body_percentage = 0.2,       // To avoid being a doji
                       double lower_shadow_ratio = 2.0,        // Lower shadow at least 2x the body
                       double upper_shadow_max_ratio = 0.3)    // Upper shadow must be small
{
   double body_size      = MathAbs(open - close);
   double total_range    = high - low + DBL_EPSILON;
   double upper_shadow   = upperShadowCalc(open, close, high);
   double lower_shadow   = lowerShadowCalc(open, close, low);
   double body_percentage = body_size / total_range;

   return (
      body_percentage >= min_body_percentage &&
      lower_shadow >= lower_shadow_ratio * body_size &&
      upper_shadow <= upper_shadow_max_ratio * body_size
   );
}

Da sowohl der Dragonfly-Doji als auch der Hammer einen längeren unteren Schatten, einen kleinen Körper und einen kurzen oberen Schatten haben, mussten wir den min_body_percentage einführen, um zu prüfen, wie klein ein Körper im Verhältnis zu seinem Gesamtbereich (High-Low) sein muss, damit eine Kerze ein Hammer ist, während wir auch prüfen, ob ihr unterer Schatten standardmäßig doppelt so lang wie ihr oberer Schatten ist.

Inverted Hammer

Er ähnelt dem Hammer, hat aber unten einen kleinen Körper, einen langen oberen Schatten und einen kleinen unteren Schatten.

Dieses Muster ist in Abwärtstrends zu finden, da es auf eine bevorstehende Aufwärtsbewegung des Marktes hinweist.

Inverted Hammer

Wir können ihn ähnlich wie den Hammer kodieren, nur mit kleinen Änderungen bei den Schatten.

bool CTALib::CDLINVERTEDHAMMER(double open, 
                               double high, 
                               double low, 
                               double close, 
                               double min_body_percentage = 0.2,        // Avoid doji
                               double upper_shadow_ratio = 2.0,         // Upper shadow must be long
                               double lower_shadow_max_ratio = 0.3)     // Lower shadow should be small
{
   double body_size        = MathAbs(open - close);
   double total_range      = high - low + DBL_EPSILON;
   double upper_shadow     = upperShadowCalc(open, close, high);
   double lower_shadow     = lowerShadowCalc(open, close, low);
   double body_percentage  = body_size / total_range;

   return (
      body_percentage >= min_body_percentage &&
      upper_shadow >= upper_shadow_ratio * body_size &&
      lower_shadow <= lower_shadow_max_ratio * body_size
   );
}

Spinning Top

Dies ist eine Kerze mit einem kleinen Körper in der Mitte und langen Schatten auf beiden Seiten.

Das Kerzenmuster Spinning Top

Wenn diese Kerze erscheint, bedeutet dies Unentschlossenheit auf dem Markt, was oft die Fortsetzung des aktuellen Trends bedeutet, da weder Käufer noch Verkäufer die Oberhand haben.

Da diese Kerze einer Doji-Kerze ähnelt (sie hat nur einen größeren Körper), müssen wir explizit sicherstellen, dass der Körper nicht so klein ist wie bei einem Doji und dass er sich in der Mitte der langen Schatten im Verhältnis zum Körper befindet.

bool CTALib::CDLSPINNINGTOP(double open,
                            double high,
                            double low,
                            double close, 
                            double body_percentage_threshold = 0.3, 
                            double shadow_ratio = 2.0,
                            double shadow_symmetry_tolerance = 0.3)
{
   double body_size      = MathAbs(open - close);
   double total_range    = high - low + DBL_EPSILON;
   double upper_shadow   = upperShadowCalc(open, close, high);
   double lower_shadow   = lowerShadowCalc(open, close, low);
   double body_percentage = body_size / total_range;

   //--- Calculate shadow symmetry ratio
   double shadow_diff = MathAbs(upper_shadow - lower_shadow);
   double shadow_sum = upper_shadow + lower_shadow + DBL_EPSILON;
   double symmetry_ratio = shadow_diff / shadow_sum; // Closer to 0 = more balanced

   return (
      body_percentage < body_percentage_threshold && // Body is small compared to candle size
      upper_shadow > body_size * shadow_ratio && // Both shadows are significantly larger than the body
      lower_shadow > body_size * shadow_ratio &&
      symmetry_ratio <= shadow_symmetry_tolerance //Shadows are roughly equal (symmetrical)
   );
}

Aufwärts-Marubozu

Der Name „Marubozu“ stammt von dem japanischen Wort für „eng geschnitten“, was auf eine Kerze ohne Schatten hinweist.

Ein Marubozu ist dann eine Aufwärtskerze mit einem kleinen oder gar keinem unteren und oberen Schatten (Docht), das ist ein starkes Aufwärtssignal, das auf ein Momentum hinweist.

Aufwärts-Marubozu

Wir können eine Toleranz in Punkten hinzufügen, um zu prüfen, ob die Eröffnungs- und Schlusskurse sehr nahe an ihren Höchst- und Tiefstkursen liegen.

bool CTALib::CDLBULLISHMARUBOZU(double open, double high, double low, double close, double tolerance = 2)
 {
   return (MathAbs(open - low) <= (tolerance*_Point) &&  MathAbs(close - high) <= (tolerance*_Point) && close > open);
 }

Abwärts-Marubozu

Ein Abwärts-Marubozu ist eine Abwärtskerze mit kleinen oder gar keinen unteren und oberen Schatten (Dochten). Es ist ein starkes Abwärtssignal, das auf ein Momentum hinweist.

Das Muster eines Abwärts-Marubozu

Ähnlich wie bei der Aufwärts-Marubozu haben wir eine Toleranz in Punkten, um zu prüfen, ob die Eröffnungs- und Schlusskurse sehr nahe an ihren Höchst- und Tiefstkursen liegen.

bool CTALib::CDLBEARISHMARUBOZU(double open, double high, double low, double close, double tolerance = 2)
 {
   return (MathAbs(open - high) <= (tolerance*_Point) && MathAbs(close - low) <= (tolerance*_Point) && close < open);
 }

Im Moment betrachten wir nur die Erkennung von Kerzenmustern und deren Signale auf der Grundlage ihres Aussehens, aber die richtige Art und Weise, die Signale aus einem Kerzen zu extrahieren, muss meinen Quellen zufolge auch die Trenderkennung beinhalten, z. B. muss ein Hammer in einem Abwärtstrend erscheinen, um als Aufwärtssignal zu gelten.

Der Trend ist ein entscheidender Teil der Gleichung, den Sie berücksichtigen sollten, wenn Sie dieses Projekt weiterführen wollen.


Indikator zur Erkennung von Kerzenmustern

Lassen Sie uns die Kerzenmuster mit Hilfe des Codes, den wir für ihre Ableitung verwendet haben, visualisieren. Ganz einfach, weil ich diese Muster oft abstrakt und verwirrend finde und weil wir beabsichtigen, diese Daten für maschinelles Lernen zu verwenden, wobei wir wissen, dass die Qualität der Daten am wichtigsten ist. Durch die Visualisierung dieser Muster stellen wir sicher, dass Sie darüber informiert sind, wie diese Muster berechnet wurden und welches Ergebnis sie zeigen.

Zögern Sie nicht, einige Parameter zu verändern und den Code nach Ihrem Ermessen zu modifizieren.

Lassen Sie uns sicherstellen, dass zumindest der Code, den wir gerade geschrieben haben, diese Muster erkennen kann, die wir auch als Menschen auf dem Markt erkennen können.

Dieser auf Kerzen basierende Indikator wird 5 Puffer und eine Darstellung im Hauptchart haben.

Dateiname: Candlestick Identifier.mq5

#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots 1

#property indicator_type1 DRAW_COLOR_CANDLES
#property indicator_color1 clrDodgerBlue, clrOrange, clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

double OpenBuff[];
double HighBuff[];
double LowBuff[];
double CloseBuff[];
double ColorBuff[];

#include <ta-lib.mqh> //!important for candlestick patterns

Da es sich bei der Bibliothek ta-lib.mqh um eine statische Klasse handelt, müssen ihre Klassen nicht initialisiert werden. Wir können die Funktionen zur Erkennung von Kerzenmustern sofort innerhalb der Funktion OnCalculate aufrufen.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   
   if (rates_total<1)
     return rates_total;
   
    for(int i = prev_calculated; i < rates_total; i++)
     {
      OpenBuff[i]  = open[i];
      HighBuff[i]  = high[i];
      LowBuff[i]   = low[i];
      CloseBuff[i] = close[i];
      
      //---
      
      if (close[i]>open[i])
         ColorBuff[i] = 1.0; 
      else
         ColorBuff[i] = 0.0;
      
      //---
      
      double padding = MathAbs(high[i] - low[i]) * 0.2; // 20% padding
      
      if (CTALib::CDLDOJI(open[i], close[i]))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding, "Doji", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
        
      if (CTALib::CDLDRAGONFLYDOJI(open[i], high[i], low[i], close[i]))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"DragonFly Doji", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
        
      if (CTALib::CDLGRAVESTONEDOJI(open[i], high[i], low[i], close[i]))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"GraveStone Doji", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
        
      if (CTALib::CDLHAMMER(open[i], high[i], low[i], close[i]))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Hammer", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
        
      if (CTALib::CDLINVERTEDHAMMER(open[i], high[i], low[i], close[i]))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Inverted Hammer", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
        
      if (CTALib::CDLSPINNINGTOP(open[i], high[i], low[i], close[i], 0.3, 2.0))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Spinning Top", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
        
      if (CTALib::CDLBULLISHMARUBOZU(open[i], high[i], low[i], close[i], 2))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Bullish Marubozu", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
        
      if (CTALib::CDLBEARISHMARUBOZU(open[i], high[i], low[i], close[i], 2))
        {
          TextCreate(string(i)+(string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Bearish Marubozu", clrBlack, 90.0);
          ColorBuff[i] = 2.0;
        }
     }
     
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Standardmäßig sind die Aufwärtskerzen orange und die Abwärtskerzen blau. Jede Kerze, die mit den oben in diesem Artikel besprochenen Mustern erkannt wird, wird rot markiert und folgt einem Text, der in einem 90-Grad-Winkel ausgerichtet ist und die Art des Kerzenmusters angibt, zu dem die rot markierte Kerze gehört.

Anzeige des Kerzenmuster-Indikators

Wie Sie auf dem Bild oben sehen können, leistet unser Kerzen-Erkennungsindikator gute Arbeit bei der Erkennung dieser Kerzenmuster. Wir können uns jetzt auf die Logik verlassen, die wir in unserem Code angewendet haben. 

Nun wollen wir diese Muster sammeln und mit Hilfe eines Skripts in einer CSV-Datei speichern, um diese Informationen für das Training von Modellen für maschinelles Lernen zu verwenden.


Sammeln von Kerzenmustern für maschinelles Lernen

Da einige Kerzenmuster auf dem Markt nicht häufig auftreten, insbesondere in höheren Zeitrahmen, in denen es nur sehr wenige Balken in der Geschichte gibt, sammeln wir unsere Daten vom 01. Januar 2005 bis zum 01. Januar 2023.

Dieser Zeitraum von 18 Jahren sollte uns eine Vielzahl von Balken aus dem täglichen Zeitrahmen und damit zahlreiche Muster liefern, die unsere maschinellen Lernmodelle beobachten können.

#include <ta-lib.mqh> //Contains CTALib class for candlestick patterns detection
#include <MALE5\Pandas\pandas.mqh> //https://www.mql5.com/de/articles/17030

input datetime start_date = D'2005.01.01';
input datetime end_date = D'2023.01.01';

input string symbol = "XAUUSD";
input ENUM_TIMEFRAMES timeframe = PERIOD_D1;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   
   vector open, high, low, close;
   
   open.CopyRates(symbol, timeframe, COPY_RATES_OPEN, start_date, end_date);
   high.CopyRates(symbol, timeframe, COPY_RATES_HIGH, start_date, end_date);
   low.CopyRates(symbol, timeframe, COPY_RATES_LOW, start_date, end_date);
   close.CopyRates(symbol, timeframe, COPY_RATES_CLOSE, start_date, end_date);
   
   CDataFrame df;
   
   vector cdl_patterns = {};
   cdl_patterns = CTALib::CDLWHITECANDLE(open, close);
   df.insert("White Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLBLACKCANDLE(open, close);
   df.insert("Black Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLDOJI(open, close);
   df.insert("Doji Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLDRAGONFLYDOJI(open, high, low, close);
   df.insert("Dragonflydoji Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLGRAVESTONEDOJI(open, high, low, close);
   df.insert("Gravestonedoji Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLHAMMER(open, high, low, close);
   df.insert("Hammer Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLINVERTEDHAMMER(open, high, low, close);
   df.insert("Invertedhammer Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLSPINNINGTOP(open, high, low, close);
   df.insert("Spinningtop Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLBULLISHMARUBOZU(open, high, low, close);
   df.insert("BullishMarubozu Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLBEARISHMARUBOZU(open, high, low, close);
   df.insert("BearishMarubozu Candle", cdl_patterns);
   
   df.insert("Open", open);
   df.insert("High", high);
   df.insert("Low", low);
   df.insert("Close", close);
   
   df.to_csv(StringFormat("CandlestickPatterns.%s.%s.csv",symbol,EnumToString(timeframe)), true);
  }

Wir sammeln auch die OHLC-Werte (Open, High, Low und Close), um die Zielvariable vorzubereiten und für den Fall, dass etwas passiert und wir sie brauchen.


Training eines KI-Modells für Vorhersagen auf der Grundlage von Kerzenmustern

Da wir nun einen Datensatz haben, können wir diese Daten in ein Python-Skript (Jupyter Notebook) laden.

import pandas as pd

symbol = "XAUUSD"
df = pd.read_csv(f"/kaggle/input/forex-candlestick-patterns/CandlestickPatterns.{symbol}.PERIOD_D1.csv")

df

Ausgabe:

Weiße Kerze Schwarze Kerze Doji-Kerze Dragonfly-Doji Kerze Gravestone-Doji Kerze Hammer-Kerze Inverted-Hammer Kerze Gravestonedoji-Kerze Aufwärts-Marubozu Abwärts-Marubozu Open High Low Close
0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 438.45 438.71 426.72 429.55
1 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 429.52 430.18 423.71 427.51
2 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 427.50 428.77 425.10 426.58
3 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 426.31 427.85 420.17 421.37
4 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 421.39 425.48 416.57 419.02
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...


Die größte Herausforderung bei der Arbeit mit diesen Kerzen-Daten ist die Festlegung der Zielvariablen.

Bei den meisten Klassifizierungsproblemen im Zusammenhang mit den Finanzmärkten wird die Zielvariable häufig auf der Grundlage der künftigen Preisentwicklung definiert, wobei ein Parameter verwendet wird, den wir häufig als „Lookahead“ bezeichnen.

Dieser Parameter gibt an, wie viele Balken (oder Zeitschritte) wir in den Daten vorausschauen. Wenn zum Beispiel der Lookahead auf 1 gesetzt ist, wird der Schlusskurs des aktuellen Balkens mit dem Schlusskurs des nächsten Balkens verglichen:

Wenn Close[nächster Balken] > Close[aktueller Balken] ist, deutet dies auf eine Aufwärtsbewegung hin, sodass wir eine Zielmarke von 1 zuweisen.

Andernfalls, wenn diese Bedingung nicht erfüllt ist, deutet dies auf eine Abwärtsbewegung hin, sodass wir den Wert 0 vergeben.

Wir können hier dasselbe tun, um sicherzustellen, dass wir unsere Merkmale nutzen, um die Informationen im Voraus zu prognostizieren, aber wie in der obigen Tabelle dargestellt, haben wir viele Nullen in diesen Daten, da wir oft keine besonderen Balken in jeder Zeile erkannt haben, abgesehen von den weißen und schwarzen Kerzen, die keine besonderen Muster sind und wir sie nicht als Kerzenmuster betrachten.

Das bedeutet, dass wir unseren maschinellen Lernmodellen häufiger Nullwerte geben und die Modelle dazu zwingen, die Beziehung zu verstehen und die Zielvariable vorauszusagen, wenn sie keinen Wert hat, was dazu führt, dass sich unsere Modelle zu sehr auf weiße und schwarze Kerzen verlassen, was wir nicht wollen.

Wir können dieses Problem angehen, indem wir alle Zeilen mit Nullwerten für alle Kerzenmuster löschen und das Modell mit reinen Kerzen-Daten trainieren. Dies würde auch erfordern, dass wir die gleiche Situation zur Laufzeit des Modells vermeiden.

Eine andere Möglichkeit, dieses Problem zu lösen, ist die Einführung des Klassensignals Halten, das in allen Zeilen, in denen alle Kerzenmuster 0 (falsch) waren, mit Ausnahme der Spalten „Weiße“ und „Schwarze Kerzen“, durch -1 angezeigt wird, aber dies führt zu einem enormen Problem des Klassenungleichgewichts, das wir im vorherigen Artikel behandelt haben.

Fahren wir fort, die Zielvariable unabhängig davon vorzubereiten.

lookahead = 1

new_df = df.copy()
new_df["future_close"] = new_df["Close"].shift(-lookahead)
new_df.dropna(inplace=True)  # Drop NaNs caused by the shift operation

signal = []
for i in range(len(new_df)):  # Iterate over rows, not columns
    if new_df["future_close"].iloc[i] > new_df["Close"].iloc[i]:
        signal.append(1)
    else:
        signal.append(0)

new_df["Signal"] = signal

Anschließend werden die Prädiktoren in ein 2D-Array mit dem Namen X aufgeteilt, wobei unerwünschte Merkmale wie die OHLC-Werte, die Spalte, die wir vorhersagen wollen (Ziel), und das Merkmal future_close close, das wir zur Ableitung der Zielspalte verwendet haben, weggelassen werden. Wir weisen dem Array y auch die Zielspalte mit dem Namen Signal zu.

X = new_df.drop(columns=[
    "Signal",
    "Open",
    "High",
    "Low",
    "Close",
    "future_close"
])

y = new_df["Signal"]

# Split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, shuffle=False)

Ich habe mich bei diesem Problem für ein Catboost-Modell entschieden, da wir viele kategorische Spalten haben, die theoretisch gut mit dem Catboost-Klassifikator funktionieren sollten.

from catboost import CatBoostClassifier
from sklearn.utils.class_weight import compute_class_weight

# Automatically calculate class weights
classes = np.unique(y)
weights = compute_class_weight(class_weight='balanced', classes=classes, y=y)
class_weights = dict(zip(classes, weights))

# Define the base model
model = CatBoostClassifier(
    iterations=1000,
    learning_rate=0.01,
    depth=5,
    loss_function='Logloss',
    class_weights=class_weights,
    verbose=100
)

model.fit(X_train, y_train) # Training the classifier

Ausgabe:

0:      learn: 0.6930586        total: 3.64ms   remaining: 3.64s
100:    learn: 0.6897625        total: 136ms    remaining: 1.21s
200:    learn: 0.6888030        total: 269ms    remaining: 1.07s
300:    learn: 0.6883559        total: 401ms    remaining: 931ms
400:    learn: 0.6881469        total: 532ms    remaining: 795ms
500:    learn: 0.6879966        total: 661ms    remaining: 658ms
600:    learn: 0.6879013        total: 789ms    remaining: 524ms
700:    learn: 0.6878311        total: 916ms    remaining: 391ms
800:    learn: 0.6877729        total: 1.04s    remaining: 260ms
900:    learn: 0.6877273        total: 1.17s    remaining: 129ms
999:    learn: 0.6876900        total: 1.3s     remaining: 0us
<catboost.core.CatBoostClassifier at 0x798cc6d08dd0>

Bewerten wir dieses Modell anhand unbekannter Daten (der Testzeitraum).

y_pred = model.predict(X_test)

print("\nClassification Report:\n", classification_report(y_test, y_pred))

Ausgabe:

Classification Report:
               precision    recall  f1-score   support

           0       0.49      0.55      0.52       429
           1       0.58      0.52      0.55       511

    accuracy                           0.53       940
   macro avg       0.53      0.53      0.53       940
weighted avg       0.54      0.53      0.53       940

Die Ergebnisse zeigen ein durchschnittliches Modell mit einer Genauigkeit von 0,58 bzw. 0,49 für die Klassen 1 und 0. Während wir uns bei der Vorhersage der Klasse 1 auf dieses Modell verlassen können, was es mit 58%iger Sicherheit tut, können wir uns bei der Vorhersage der Klasse 0 nicht darauf verlassen; in diesem Fall sind wir besser dran, wenn wir raten.

Eine Gesamtgenauigkeit von 53 % von 100 %, die in diesem Handelsbereich realistisch ist, ist besser als das Werfen einer Münze oder zufälliges Raten, das eine 50/50-Gewinnquote garantieren kann.

Schauen wir uns das Diagramm der Merkmalsbedeutung an, um zu sehen, welche Merkmale für dieses Modell am wichtigsten waren.

import matplotlib.pyplot as plt

# Get feature importances
importances = model.get_feature_importance()
feature_names = X_train.columns if hasattr(X_train, 'columns') else [f'feature_{i}' for i in range(X_train.shape[1])]

# Create DataFrame for plotting
feat_imp_df = pd.DataFrame({
    'Feature': feature_names,
    'Importance': importances
}).sort_values(by='Importance', ascending=False)

# Plot
plt.figure(figsize=(7, 3))
plt.barh(feat_imp_df['Feature'], feat_imp_df['Importance'])
plt.gca().invert_yaxis()  # Highest importance on top
plt.title('Feature Importances')
plt.xlabel('Importance')
plt.ylabel('Feature')
plt.tight_layout()
plt.show()

Ausgabe:

Das Kerzenmuster des Spinning-Top war das wirkungsvollste Merkmal dieses Modells, gefolgt von der Doji-Kerze, während die Abwärts-Marubozu die geringste Wirkung hatte.

Nach dem, was ich über diese Kerzenmuster gelesen habe, nämlich, dass einige Muster dazu bestimmt sind, den Trend oder was im Markt über einen längeren Zeitraum oder Horizont passieren wird, wie zum Beispiel ein Doji-Kerze, die zu zu oberst einer Aufwärtsbewegung erscheint, könnte bedeuten, dass ein Abwärtstrend dabei ist sich zu bilden.

Wir haben die Zielvariable auf der Grundlage des Lookahead-Wertes von 1 erstellt, d. h. wir verwenden diese Kerzenmuster, um einen statt mehrerer Balken für das Signal zu verwenden.

Sie können also gerne verschiedene Lookahead-Werte größer als 1 untersuchen, um die Auswirkungen dieser Kerzenmuster auf einen längeren Zeitraum oder über verschiedene Horizonte hinweg zu beobachten. Bei meinen bisherigen Untersuchungen habe ich herausgefunden, dass das Modell, das auf dem Lookahead-Wert von 1 trainiert wurde, das genaueste Modell erzeugt.

Wir bleiben vorerst bei dem Vorausschauwert von 1, da ich glaube, dass wir dieses Problem des Vorhersagehorizonts in unserem endgültigen Handelsroboter durch Stop-Loss- und Take-Profit-Werte oder durch das Schließen unserer Handelsgeschäfte nach einer bestimmten Anzahl von Bars lösen können.


Finalisierung in einem Handelsroboter

Nachdem wir nun ein Modell auf der Grundlage von Kerzenmustern trainiert haben, wollen wir es in der tatsächlichen Handelsumgebung testen und sehen, ob Kerzenmuster im Bereich der künstlichen Intelligenz (KI) nützlich sein könnten.

Zunächst müssen wir unser Modell im ONNX-Format speichern, das mit MQL5 und MetaTrader 5 kompatibel ist.

model_onnx = convert_sklearn(
    model,
    "catboost",
    [("input", FloatTensorType([None, X_train.shape[1]]))],
    target_opset={"": 12, "ai.onnx.ml": 2},
)

# And save.
with open(f"CatBoost.CDLPatterns.{symbol}.onnx", "wb") as f:
    f.write(model_onnx.SerializeToString())

Weitere Informationen zum Speichern dieses Catboost-Modells finden Sie hier.

Unser Expert Advisor (EA) ist ziemlich einfach.

#include <Trade\Trade.mqh> //The trading module
#include <Trade\PositionInfo.mqh> //Position handling module
#include <ta-lib.mqh> //For candlestick patterns
#include <Catboost.mqh> //Has a class for deploying a catboost model

CTrade m_trade;
CPositionInfo m_position;

CCatboostClassifier catboost;

input int magic_number = 21042025;
input int slippage = 100;
input string symbol_ = "XAUUSD";
input ENUM_TIMEFRAMES timeframe_ = PERIOD_D1;
input int lookahead = 1;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
      
   if (!MQLInfoInteger(MQL_TESTER))
     if (!ChartSetSymbolPeriod(0, symbol_, timeframe_))
       {
         printf("%s failed to set symbol %s and timeframe %s, Check these values. Err = %d",__FUNCTION__,symbol_,EnumToString(timeframe_),GetLastError());
         return INIT_FAILED;
       }
  
//---
   
   if (!catboost.Init(StringFormat("CatBoost.CDLPatterns.%s.onnx",symbol_), ONNX_COMMON_FOLDER)) //Initialize the catboost model
      return INIT_FAILED;
   
//---

   m_trade.SetExpertMagicNumber(magic_number);
   m_trade.SetDeviationInPoints(slippage);
   m_trade.SetMarginMode();
   m_trade.SetTypeFillingBySymbol(Symbol());
           
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
   double open = iOpen(Symbol(), Period(), 1),
          high = iHigh(Symbol(), Period(), 1),
          low  = iLow(Symbol(), Period(), 1), 
          close = iClose(Symbol(), Period(), 1);   
   
   vector x = {
               CTALib::CDLWHITECANDLE(open, close),
               CTALib::CDLBLACKCANDLE(open, close),
               CTALib::CDLDOJI(open, close),
               CTALib::CDLDRAGONFLYDOJI(open, high, low, close),
               CTALib::CDLGRAVESTONEDOJI(open, high, low, close),
               CTALib::CDLHAMMER(open, high, low, close),
               CTALib::CDLINVERTEDHAMMER(open, high, low, close),
               CTALib::CDLSPINNINGTOP(open, high, low, close),
               CTALib::CDLBULLISHMARUBOZU(open, high, low, close),
               CTALib::CDLBEARISHMARUBOZU(open, high, low, close)
              };
       
   long signal = catboost.predict(x).cls; //Predicted class

   MqlTick ticks;
   if (!SymbolInfoTick(Symbol(), ticks))
      {
         printf("Failed to obtain ticks information, Error = %d",GetLastError());
         return;
      }
      
   double volume_ = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);   
   
   if (signal == 1) 
     {        
        if (!PosExists(POSITION_TYPE_BUY) && !PosExists(POSITION_TYPE_SELL))  
            m_trade.Buy(volume_, Symbol(), ticks.ask,0,0);
     }
     
   if (signal == 0)
     {        
        if (!PosExists(POSITION_TYPE_SELL) && !PosExists(POSITION_TYPE_BUY))  
            m_trade.Sell(volume_, Symbol(), ticks.bid,0,0);
     } 
    
    CloseTradeAfterTime((Timeframe2Minutes(Period())*lookahead)*60); //Close the trade after a certain lookahead and according the the trained timeframe
  }

Nach der Initialisierung des Catboost-Modells im ONNX-Format, das im Verzeichnis Common gespeichert wurde.

In der Funktion OnTick werden die Werte der zuvor geschlossenen Leiste Open, High, Low und Close abgerufen und mit den Funktionen von CTALib zur Erkennung von Kerzenmustern geparst, dann werden die Ergebnisse zur Erstellung von Prognosen in einem Vektor namens x verwendet. 

Wir müssen auf die Merkmale und ihre Reihenfolge achten, da sie beim Training des endgültigen Catboost-Modells im Python-Skript verwendet wurden.

X_train.columns

In unserem endgültigen Modell hatten wir.

Index(['White Candle', 'Black Candle', 'Doji Candle', 'Dragonflydoji Candle',
       'Gravestonedoji Candle', 'Hammer Candle', 'Invertedhammer Candle',
       'Spinningtop Candle', 'BullishMarubozu Candle',
       'BearishMarubozu Candle'],
      dtype='object')

Diese Reihenfolge wurde innerhalb des Expert Advisors beibehalten.

Zurzeit haben wir keinen Stop-Loss und keinen entsprechenden Take-Profit-Wert, sodass wir die offenen Handelsgeschäfte (Positionen) nach einer bestimmten Anzahl von Bars, die im gegebenen Zeitrahmen vergangen sind, schließen.

Tester-Konfigurationen.

Ergebnisse.

 

Was mich fasziniert, ist die Ähnlichkeit der Ergebnisse zwischen den Handelsgeschäften von Kauf und Verkauf: Die Zahl der in diesen zwei Jahren eröffneten Verkäufe lag bei 257, 2 Handelsgeschäfte weniger als Käufe mit 259.

Dies ist unangemessen, und wir können sagen, dass trotz des Modells, das alle Kerzenmuster berücksichtigt, die weißen und schwarzen Kerzen am aussagekräftigsten sind, da sie auf jedem Balken erscheinen. Wir schließen den Handel auch nach einem Balken (Lookahead-Wert = 1), dieses Problem geht darauf zurück, wie wir die Zielvariable vorbereitet und das Modell mit den Daten trainiert haben, die in vielen Merkmalen Null (falsche) Werte enthalten. 

Um sicherzustellen, dass die einzigartigen Kerzenmuster beachtet werden, müssen wir prüfen, wann alle speziellen Kerzenmuster 0 (falsch) waren - also vom Modell nicht erkannt wurden - und verhindern, dass ein Handel eröffnet wird, wenn dies geschieht.

Wir wollen nur dann einen Handel eröffnen, wenn ein einzigartiges Kerzenmuster erkannt wird, das sich vom weißen und schwarzen Kerzen unterscheidet.

void OnTick()
  {
//---
   
   double open = iOpen(Symbol(), Period(), 1),
          high = iHigh(Symbol(), Period(), 1),
          low  = iLow(Symbol(), Period(), 1), 
          close = iClose(Symbol(), Period(), 1);
   
   
   vector x = {
               CTALib::CDLWHITECANDLE(open, close),
               CTALib::CDLBLACKCANDLE(open, close),
               CTALib::CDLDOJI(open, close),
               CTALib::CDLDRAGONFLYDOJI(open, high, low, close),
               CTALib::CDLGRAVESTONEDOJI(open, high, low, close),
               CTALib::CDLHAMMER(open, high, low, close),
               CTALib::CDLINVERTEDHAMMER(open, high, low, close),
               CTALib::CDLSPINNINGTOP(open, high, low, close),
               CTALib::CDLBULLISHMARUBOZU(open, high, low, close),
               CTALib::CDLBEARISHMARUBOZU(open, high, low, close)
              };
   
   vector patterns = {
                        CTALib::CDLDOJI(open, close),
                        CTALib::CDLDRAGONFLYDOJI(open, high, low, close),
                        CTALib::CDLGRAVESTONEDOJI(open, high, low, close),
                        CTALib::CDLHAMMER(open, high, low, close),
                        CTALib::CDLINVERTEDHAMMER(open, high, low, close),
                        CTALib::CDLSPINNINGTOP(open, high, low, close),
                        CTALib::CDLBULLISHMARUBOZU(open, high, low, close),
                        CTALib::CDLBEARISHMARUBOZU(open, high, low, close)
                     }; //Store all the special patterns 
    
   long signal = catboost.predict(x).cls; //Predicted class

   MqlTick ticks;
   if (!SymbolInfoTick(Symbol(), ticks))
      {
         printf("Failed to obtain ticks information, Error = %d",GetLastError());
         return;
      }
      
   double volume_ = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);   
   
   if (signal == 1 && patterns.Sum()>0) //Check if there are is atleast a special pattern before opening a trade
     {        
        if (!PosExists(POSITION_TYPE_BUY) && !PosExists(POSITION_TYPE_SELL))  
            m_trade.Buy(volume_, Symbol(), ticks.ask,0,0);
     }
     
   if (signal == 0 && patterns.Sum()>0) //Check if there are is atleast a special pattern before opening a trade
     {        
        if (!PosExists(POSITION_TYPE_SELL) && !PosExists(POSITION_TYPE_BUY))  
            m_trade.Sell(volume_, Symbol(), ticks.bid,0,0);
     } 
    
    CloseTradeAfterTime((Timeframe2Minutes(Period())*lookahead)*60); //Close the trade after a certain lookahead and according the the trained timeframe
  }

Testergebnisse.

Es sieht jetzt viel besser aus. Ein paar Handelsgeschäfte wurden eröffnet, was der Seltenheit dieser Kerzenmuster im höheren Zeitrahmen ähnelt, die unser Modell auf dem täglichen Zeitrahmen trainiert hat.

Die prozentuale Anzahl profitabler Handelsgeschäfte liegt bei 54,55 %, was der im Klassifizierungsbericht ermittelten Gesamtgenauigkeit von 0,53 (53 %) sehr nahe kommt; diese Ähnlichkeit zeigt, dass wir auf dem richtigen Weg sind.


Schlussfolgerung

Es ist also möglich, Kerzenmuster bei der Arbeit mit Modellen der Künstlichen Intelligenz (KI) zu verwenden und das Endergebnis für Vorhersagen auf dem Markt zu nutzen. Im Gegensatz zur Verwendung von Indikatoren und mathematischen Berechnungen, wie bei allen typischen Daten, die wir oft für die Vorhersage der Märkte verwenden, erfordern Kerzenmuster jedoch viele Überlegungen und eine große Aufmerksamkeit für winzige Details bei der Sammlung der Daten und der Erstellung von Merkmalen, die von den beobachtbaren Kerzen auf dem Markt abgeleitet werden, denn eine kleine Fehlinterpretation einer Kerze könnte zu einem ganz anderen Ergebnis führen.

Es heißt, dass unsere Wünsche und Überzeugungen Einfluss darauf haben, wie wir Informationen wahrnehmen und interpretieren. Mit anderen Worten: Wir sehen, was wir sehen wollen.

Ich glaube, das passiert meistens, wenn man mit Kerzenmustern arbeitet. Wenn man nach einem Hammer sucht, dann könnte ein Dragonfly-Doji wie ein Hammer aussehen und umgekehrt.

Ich glaube, dass bei der Aufbereitung der Kerzen-basierten Daten viele Versuche und Irrtümer erforderlich sind, um eine optimale Leistung bei der Verwendung dieser Daten im maschinellen Lernen zu erzielen.

Mit freundlichen Grüßen. 

Bleiben Sie dran und tragen Sie zur Entwicklung von Algorithmen für maschinelles Lernen für die Sprache MQL5 in diesem GitHub-Repository bei.


Quellen und Referenzen


Tabelle der Anhänge

Dateiname Beschreibung/Verwendung
Experts\CandlestickPatterns AI-EA.mq5 Ein Expert Advisor (EA), der das Catboost-Modell einsetzt, das Vorhersagen auf der Grundlage von Kerzenmustern macht.
Indicators\Candlestick Identifier.mq5 Ein Indikator zur Anzeige von Kerzenmustern auf dem Chart.
Scripts\Candlestick Patterns Collect.mq5 Ein Skript zum Sammeln von Kerzenmustern und Speichern dieser Informationen in einer CSV-Datei.
Include\Catboost.mqh Eine Bibliothek, die Klassen zum Laden, Initialisieren und Einsetzen des Catboost-Klassifikators für Vorhersagen auf dem Markt enthält.
Include\pandas.mqh Python-ähnliches Pandas-Modul zur Datenspeicherung und -manipulation.
Include\ta-lib.mqh Bibliothek für technische Analysen, die eine Klasse zur Erkennung von Kerzenmustern enthält.
Common\Files\*.csv CSV-Dateien, die Kerzen-Daten für die Verwendung beim maschinellen Lernen enthalten.
Common\Files\*.onnx Modelle für maschinelles Lernen im ONNX-Format.
CandlestickMarket Prediction.ipynb Ein Python-Skript (Jupyter noteboot) für das Training des Catboost-Modells.


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

Beigefügte Dateien |
Attachments.zip (183.92 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (5)
Rajesh Kumar Nait
Rajesh Kumar Nait | 29 Apr. 2025 in 09:24
Ein großartiger Artikel.
Zhuo Kai Chen
Zhuo Kai Chen | 1 Mai 2025 in 05:37
Ich liebe diesen Versuch! Sehr interessanter Ansatz der Kennzeichnung Candle-Stick-Muster für ML.
Stanislav Korotky
Stanislav Korotky | 1 Mai 2025 in 10:17
Die Verwendung der Candlestick-Konfiguration anstelle der zugrunde liegenden Kursbewegung ist vergleichbar mit der Verwendung eines indizierten 256-Farben-Bildes anstelle eines Echtfarbbildes - man verliert viele Nuancen, die wichtig sein können. Darüber hinaus können sich die Candlestick-Muster je nach Zeitverschiebung eines Brokers in der gleichen Kursgeschichte völlig verändern. Und selbst wenn Sie keinen großen Zeitrahmen (wie D1 (in Ihrem Fall) oder H4) verwenden, kann eine einfache künstliche Zeitverschiebung (Minuten) innerhalb einer Stunde völlig unterschiedliche Candlestick-Formationen erzeugen. Unzuverlässig, bringt keinen Wert.
Omega J Msigwa
Omega J Msigwa | 1 Mai 2025 in 13:08
Stanislav Korotky Candlestick-Muster je nach Zeitverschiebung eines Brokers in der gleichen Kursgeschichte völlig verändern. Und selbst wenn Sie keinen großen Zeitrahmen (wie D1 (in Ihrem Fall) oder H4) verwenden, kann eine einfache künstliche Zeitverschiebung (Minuten) innerhalb einer Stunde völlig unterschiedliche Candlestick-Formationen erzeugen. Das ist unzuverlässig und bringt keinen Wert.

Ich habe dies bereits in diesem Artikel erklärt.

Im Moment betrachten wir nur die Erkennung von Candlestick-Mustern und deren Signale auf der Grundlage ihres Aussehens, aber die richtige Art und Weise, die Signale aus einem Candlestick zu extrahieren, muss meinen Quellen zufolge auch die Trenderkennung einschließen, z. B. muss ein Hammer in einem Abwärtstrend erscheinen, um als bullisches Signal zu gelten.

Der Trend ist ein wichtiger Teil der Gleichung, den Sie berücksichtigen sollten, wenn Sie dieses Projekt weiterverfolgen wollen.

Preisaktionen sind wichtig, das ist unbestritten.

Stanislav Korotky
Stanislav Korotky | 1 Mai 2025 in 13:35
Omega J Msigwa #:

Ich habe dies bereits in dem Artikel erläutert.

Im Moment betrachten wir nur die Erkennung von Candlestick-Mustern und deren Signale auf der Grundlage ihres Aussehens, aber der richtige Weg, die Signale aus einem Candlestick zu extrahieren, muss meinen Quellen zufolge auch die Trenderkennung beinhalten.

Der Trend ist ein wichtiger Teil der Gleichung, den Sie berücksichtigen sollten, wenn Sie dieses Projekt weiterverfolgen wollen.

Preisaktionen sind wichtig, das ist unbestritten.

Ihr Zitat hat nichts mit dem Hauptgedanken meiner Aussage zu tun.

Aufbau eines nutzerdefinierten Systems zur Erkennung von Marktregimen in MQL5 (Teil 2): Expert Advisor Aufbau eines nutzerdefinierten Systems zur Erkennung von Marktregimen in MQL5 (Teil 2): Expert Advisor
Dieser Artikel beschreibt den Aufbau eines adaptiven Expert Advisors (MarketRegimeEA) unter Verwendung des Regime-Detektors aus Teil 1. Er wechselt automatisch die Handelsstrategien und Risikoparameter für steigende, volatile oder Seitwärtsmärkte. Praktische Optimierung, Handhabung von Übergängen und ein Indikator für mehrere Zeitrahmen sind enthalten.
Websockets für MetaTrader 5: Asynchrone Client-Verbindungen mit dem Windows-API Websockets für MetaTrader 5: Asynchrone Client-Verbindungen mit dem Windows-API
Dieser Artikel beschreibt die Entwicklung einer nutzerdefinierten, dynamisch gelinkten Bibliothek, die asynchrone Websocket-Client-Verbindungen für MetaTrader-Programme ermöglicht.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 60): Inferenzlernen (Wasserstein-VAE) mit gleitendem Durchschnitt und stochastischen Oszillatormustern MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 60): Inferenzlernen (Wasserstein-VAE) mit gleitendem Durchschnitt und stochastischen Oszillatormustern
Wir schließen unsere Betrachtung der komplementären Paarung von MA und stochastischem Oszillator ab, indem wir untersuchen, welche Rolle das Inferenzlernen in einer Situation nach überwachtem Lernen und Verstärkungslernen spielen kann. Es gibt natürlich eine Vielzahl von Möglichkeiten, wie man in diesem Fall das Inferenzlernen angehen kann, unser Ansatz ist jedoch die Verwendung von Variationsautokodierern. Wir untersuchen dies in Python, bevor wir unser trainiertes Modell mit ONNX exportieren, um es in einem von einem Assistenten zusammengestellten Expert Advisor in MetaTrader zu verwenden.
Aufbau eines nutzerdefinierten Systems zur Erkennung von Marktregimen in MQL5 (Teil 1): Der Indikator Aufbau eines nutzerdefinierten Systems zur Erkennung von Marktregimen in MQL5 (Teil 1): Der Indikator
Dieser Artikel beschreibt die Erstellung eines MQL5-Systems zur Erkennung von Marktregimen unter Verwendung statistischer Methoden wie Autokorrelation und Volatilität. Es enthält Code für Klassen zur Klassifizierung von Trend-, Spannen- und Volatilitätsbedingungen sowie einen nutzerdefinierten Indikator.