English
preview
Selbstoptimierende Expert Advisors mit MQL5 und Python erstellen

Selbstoptimierende Expert Advisors mit MQL5 und Python erstellen

MetaTrader 5Beispiele | 18 September 2024, 09:54
17 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Zusammenfassung

Die Entwickler von algorithmischem Handel stehen vor der großen Herausforderung, sich an die sich ständig verändernden Marktbedingungen anzupassen, die sich im Laufe der Zeit unvorhersehbar verändern. In dem Maße, wie sich diese Bedingungen ändern, müssen sich auch die eingesetzten Strategien ändern. Eine Strategie, die auf den Mittelwert zurückgreift, könnte beispielsweise optimal sein, wenn sich die Märkte innerhalb einer bestimmten Bandbreite bewegen. Wenn die Märkte jedoch beginnen, durchgängig in eine Richtung zu tendieren, ist eine Trendfolgestrategie besser geeignet.

Oftmals implementieren wir als Entwickler eine einzige Handelsstrategie und versuchen, diese universell auf alle Marktbedingungen anzuwenden. Leider kann dieser Ansatz keinen beständigen Erfolg garantieren. Alternativ ist es auch möglich, mehrere Handelsstrategien in einem Programm zu kodieren, sodass der Endnutzer die am besten geeignete Strategie nach eigenem Ermessen auswählen kann.

Es liegt daher auf der Hand, dass wir Programme entwickeln müssen, die in der Lage sind, je nach den vorherrschenden Marktbedingungen selbständig zwischen verschiedenen Strategien zu wählen und zu wechseln. Zu diesem Zweck benötigen wir eine quantitative Methode zur Messung der Stärke von Trends oder mittleren Umkehrbewegungen auf dem Markt. Sobald unser Expert Advisor die Stärke der einzelnen Bewegungen bewertet, kann er die optimale Strategie auswählen.

Dieser Artikel zeigt, wie wir unser Ziel auf intelligente Weise erreichen können, indem wir eine Übergangsmatrix verwenden, um das Marktverhalten zu modellieren und zu bestimmen, ob unsere Strategie trendfolgend oder aus der Rückkehr zum Mittelwert sein soll. Wir beginnen damit, ein grundlegendes Verständnis von Übergangsmatrizen zu entwickeln. Anschließend untersuchen wir, wie diese mathematischen Werkzeuge zur Entwicklung intelligenter Handelsalgorithmen mit verbesserten Entscheidungsfähigkeiten eingesetzt werden können.


Einleitung: Wer war Andrey Markov?

Das 19. Jahrhundert war eine Ära brillanter Entdeckungen, wie die Erfindung des Telefons durch Alexander Graham Bell, die Erfindung der Glühbirne durch Thomas Edison und die Entwicklung des Radios durch Guglielmo Marconi. Von allen wissenschaftlichen Durchbrüchen dieser Zeit sind jedoch nur wenige für uns als Algorithmenentwickler bedeutsamer als die Beiträge des brillanten russischen Mathematikers Andrej Markow.

Markov

Abb. 1: Ein Bild des jungen Andrey Markov.

Markov arbeitete an vielen Problemen, bei denen er Prozesse modellieren musste, die völlig zufällig waren, ähnlich wie unsere Herausforderung, mit der Unvorhersehbarkeit der Marktdynamik umzugehen. Er beschrieb formell einen Rahmen, der heute als „Markov-Kette“ bekannt ist. Wir wollen es intuitiv verstehen.

Stellen Sie sich vor, Sie leiten ein öffentliches Verkehrsunternehmen, das seit über 70 Jahren Busdienste in Deutschland anbietet. Das Unternehmen erwägt, den Fuhrpark um weitere Busse zu erweitern, und Sie als Manager müssen entscheiden, für welche Ziele die zusätzlichen Busse eingesetzt werden sollen und für welche sich eine weitere Investition nicht lohnt.

Die Betrachtung des Problems als Markov-Kette könnte den Entscheidungsprozess für Sie als Manager vereinfachen. Stellen wir uns vor, das folgende Diagramm stellt die Markov-Kette aller abgeschlossenen Reisen dar, die das Unternehmen in seiner 70-jährigen Geschichte unternommen hat.


Markov-Modell

Abb. 2: Ein fiktives Markov-Modell eines Transportunternehmens und die von den Kunden zufällig genutzten Routen.

Lassen Sie uns die obige Markov-Kette interpretieren. Wir können feststellen, dass 40 % der Passagiere, die in Frankfurt einsteigen, in München aussteigen, während die anderen 60 % nach Köln fahren. Von den Fahrgästen in Köln kehren 30 % nach Frankfurt zurück, und 70 % fahren in der Regel nach Berlin weiter. Dieses Modell zeigt deutlich, welche Strecken von Ihren Kunden am häufigsten genutzt werden.

Beachten Sie außerdem, dass es Ziele gibt, zu denen es keine direkten Verbindungen gibt. Das Fehlen einer Verbindung deutet darauf hin, dass in der 70-jährigen Geschichte des Unternehmens noch nie ein Kunde direkt zwischen diesen beiden Städten reisen musste. Als Manager können Sie daher guten Gewissens feststellen, dass das Hinzufügen von Bussen von Frankfurt nach Berlin im Vergleich zu anderen beliebten Strecken, wie z. B. Frankfurt nach Köln, nicht so rentabel ist.

Der Punkt ist, dass eine Übergangsmatrix die verschiedenen Wahrscheinlichkeiten des Übergangs von einem Zustand in einen anderen zeigt. Nach Andrey Markov hängt die Wahrscheinlichkeit für einen beliebigen Zustand nur von ihrem aktuellen Zustand ab. Das hilft uns zu verstehen, wie sich ein System verändert und in welchen Zustand es höchstwahrscheinlich als Nächstes übergehen wird. Bevor wir Übergangsmatrizen auf Finanzmärkte anwenden können, müssen wir zunächst alle möglichen Zustände definieren, die der Markt annehmen kann.



Aufbau unserer Strategie: Definition der Marktzustände

Eine wirksame Methode zur Bestimmung von Marktzuständen ist die Verwendung technischer Indikatoren. Im folgenden Beispiel haben wir einen gleitenden Durchschnitt auf ein Symbol aus unserem MetaTrader 5 Terminal angewendet. Wir können die Zustände wie folgt definieren: “Immer wenn eine Kerze oberhalb des gleitenden Durchschnitts schließt, ist der Status UP (1 im Chart), und immer wenn eine Kerze unterhalb des gleitenden Durchschnitts schließt, ist der Status DOWN (2 im Chart).“


Definition von Marktzuständen

Abb. 3: Ein schematisches Chart, das den Zustand des Marktes als 1 oder 2 darstellt.

Wir können eine Markov-Kette konstruieren, um zu modellieren, wie der Markt von einem Schluss über dem gleitenden Durchschnitt zu einem Schluss unter dem gleitenden Durchschnitt übergeht. Mit anderen Worten: Eine Markov-Kette, die die Beziehung zwischen dem gleitenden Durchschnitt und dem Schlusskurs modelliert, würde Fragen beantworten wie: „Wenn eine Kerze oberhalb des gleitenden Durchschnitts schließt, wie hoch ist die Wahrscheinlichkeit, dass die nächste Kerze ebenfalls oberhalb des gleitenden Durchschnitts schließt?“ Liegt diese Wahrscheinlichkeit über 0,5, ist der Markt möglicherweise für Trendfolgestrategien geeignet. Andernfalls ist der Markt eher für Strategien geeignet, die zum Mittelwert zurückgehen.


Erste Schritte: Aufbau unserer ersten Übergangsmatrix

Um loszulegen, importieren wir zunächst unsere Standard-Python-Bibliotheken für die Kommunikation mit unserem MetaTrader5-Terminal und für die Datenanalyse.

#Import packages
import pandas as pd
import numpy as np
import MetaTrader5 as mt5
from   datetime import datetime
import pandas_ta as ta
import time

Dann müssen wir unsere Anmeldedaten festlegen und andere globale Variablen wie das Symbol, mit dem wir handeln möchten, und den Zeitrahmen, den wir verwenden möchten, angeben. 

#Account login details
login = 123456789
password = "Enter Your Password"
server = "Enter Your Server"
symbol = "EURUSD"
#What timeframe are we working on?
timeframe = mt5.TIMEFRAME_M1
#This data frame will store the most recent price update
last_close = pd.DataFrame()
#We may not always enter at the price we want, how much deviation can we tolerate?
deviation = 100
#The size of our positions
volume = 0
#How many times the minimum volume should our positions be
lot_multiple = 1

Jetzt können wir uns einloggen.

#Login
if(mt5.initialize(login=login,password=password,server=server)):
    print("Logged in successfully")
else:
    print("Failed to login")
Erfolgreich eingeloggt.

Im nächsten Schritt definieren wir nun unser Handelsvolumen.

#Setup trading volume
symbols = mt5.symbols_get()
for index,symbol in enumerate(symbols):
    if symbol.name == "EURUSD":
        print(f"{symbol.name} has minimum volume: {symbol.volume_min}")
        volume = symbol.volume_min * lot_multiple
EURUSD hat ein minimales Volumen: 0.01
Wir müssen nun angeben, wie viele Daten wir von unserem MetaTrader5-Terminal benötigen.
#Specify date range of data to be collected
date_start = datetime(2020,1,1)
date_end = datetime.now()

Nachdem wir die Daten abgerufen haben, können wir nun unsere Übergangsmatrix berechnen, um zu sehen, wie sich der EUR-USD-Markt entwickelt.

#Fetch market data
market_data = pd.DataFrame(mt5.copy_rates_range("EURUSD",timeframe,date_start,date_end))
market_data["time"] = pd.to_datetime(market_data["time"],unit='s')
#Add simple moving average technical indicator
market_data.ta.sma(length=20,append=True)
#Delete missing rows
market_data.dropna(inplace=True)
#Inspect the data frame
market_data

Unser Datenrahmen

Abb. 4: Unser Datenrahmen in seinem aktuellen Format.

Wir müssen festlegen, wie viel Platz zwischen den beiden Kerzen von Interesse sein muss. In diesem Beispiel interessieren wir uns für die Antwort auf die Frage: „Wenn die aktuelle Kerze über dem gleitenden Durchschnitt schließt, wie hoch ist die Wahrscheinlichkeit, dass die nächste Kerze ebenfalls über dem gleitenden Durchschnitt schließt?“ Wenn Sie an den Übergangswahrscheinlichkeiten über größere Zeithorizonte interessiert sind, sollten Sie diesen Parameter erhöhen, um Ihren spezifischen Strategieanforderungen gerecht zu werden.

#Define how far ahead we are looking
look_ahead = 1

Die Berechnung einer Übergangsmatrix ist einfach:

  1. Definiere alle möglichen Zustände (wir haben 2 einfache Zustände definiert, UP und DOWN).
  2. Zähle, wie viele Kerzen in den jeweiligen Zustand fallen.
  3. Berechne, welcher Anteil aller Kerzen im Zustand UP von einer weiteren Kerze im gleichen Zustand gefolgt wurde.
  4. Berechne, welcher Anteil aller Kerzen im Zustand DOWN von einer weiteren Kerze im gleichen Zustand gefolgt wurde.
#Count the number of times price was above the moving average
up = market_data.loc[market_data["close"] > market_data["SMA_20"]].shape[0]

#Count the number of times price was below the moving average
down = market_data.loc[market_data["close"] < market_data["SMA_20"]].shape[0]

#Count the number of times price was above the moving average and remained above it
up_and_up = (market_data.loc[( (market_data["close"] > market_data["SMA_20"]) & (market_data["close"].shift(-look_ahead) > market_data["SMA_20"].shift(-look_ahead)) )].shape[0]) / up

#Count the number of times price was below the moving average and remained below it
down_and_down = (market_data.loc[( (market_data["close"] < market_data["SMA_20"]) & (market_data["close"].shift(-look_ahead) < market_data["SMA_20"].shift(-look_ahead)) )].shape[0]) / down
Jetzt kombinieren wir die Daten zu einem Datenrahmen.
transition_matrix = pd.DataFrame({
    "UP":[up_and_up,(1-down_and_down)],
    "DOWN":[(1-up_and_up),down_and_down]
},index=['UP','DOWN'])

Schauen wir uns unsere Übergangsmatrix an.

transition_matrix

Übergangsmatrix

Abb. 5: Unsere Übergangsmatrix.

Interpretieren wir die Übergangsmatrix. Unsere Matrix informiert uns darüber, dass, wenn die aktuelle Kerze über dem gleitenden Durchschnitt schließt, eine 88%ige Chance besteht, dass die nächste Kerze ebenfalls über dem gleitenden Durchschnitt schließt und eine 12%ige Chance, dass die nächste Kerze unter dem gleitenden Durchschnitt schließt. Dies ist ein gutes Zeichen dafür, dass sich die Bewegungen auf diesem Markt nicht so oft umkehren. Daher kann der Markt für Trendfolgestrategien geeignet sein. 

Nachdem wir nun unsere Übergangsmatrix erstellt haben, können wir nun den Rest unseres Algorithmus entwickeln, der diese Übergangsmatrix als Entscheidungshilfe für den Kauf oder Verkauf eines bestimmten Wertpapiers verwendet. 

Zunächst definieren wir eine Funktion, die die aktuellen Kursdaten von unserem Terminal abruft und die Werte unserer technischen Indikatoren berechnet.

def get_prices():
    start = datetime(2024,6,1)
    end   = datetime.now()
    data  = pd.DataFrame(mt5.copy_rates_range("EURUSD",timeframe,start,end))
    #Add simple moving average technical indicator
    data.ta.sma(length=20,append=True)
    #Delete missing rows
    data.dropna(inplace=True)
    data['time'] = pd.to_datetime(data['time'],unit='s')
    data.set_index('time',inplace=True)
    return(data.iloc[-1,:])

Als Nächstes werden wir eine Funktion definieren, die den aktuellen Stand des Marktes ermittelt.

def get_state(current_data):
    #Price is above the moving average, UP state
    if(current_data["close"]  > current_data["SMA_20"]):
        return(1)
    #Price is below the moving average, DOWN state
    elif(current_data["close"] < current_data["SMA_20"]):
        return(2)

Schließlich werden wir eine Funktion definieren, um eine Aktion auszuwählen, die den aktuellen Zustand des Marktes und die Übergangswahrscheinlichkeit berücksichtigt.

def get_action(current_state):
    if(current_state == 1):
        if(transition_matrix.iloc[0,0] > transition_matrix.iloc[0,1]):
            print("The market is above the moving average and has strong trends, buy")
            print("Opening a BUY position")
            mt5.Buy("EURUSD",volume)
        elif(transition_matrix.iloc[0,0] < transition_matrix.iloc[0,1]):
            print("The market is above the moving average and has strong mean reverting moves, sell")
            print("Opening a sell position")
            mt5.Sell("EURUSD",volume)
    elif(current_state == 2):
        if(transition_matrix.iloc[1,0] > transition_matrix.iloc[1,1]):
            print("The market is below the moving average and has strong mean reverting moves, buy")
            print("Opening a BUY position")
            mt5.Buy("EURUSD",volume)
        elif(transition_matrix.iloc[1,0] < transition_matrix.iloc[1,1]):
            print("The market is below the moving average and has strong trends, sell")
            print("Opening a sell position")
            mt5.Sell("EURUSD",volume)

Jetzt können wir unseren Algorithmus in Aktion sehen.

while True:
    #Get data on the current state of our terminal and our portfolio
    positions = mt5.positions_total()
    #If we have no open positions then we can open one
    if(positions == 0):
        get_action(get_state(get_prices()))
    #If we have finished all checks then we can wait for one day before checking our positions again
    time.sleep(60)

Der Markt liegt unter dem gleitenden Durchschnitt und weist starke Trends auf: verkaufen.

Eröffnen einer Verkaufsposition.

Ursprüngliche Marktbedingungen

Abb. 6: Der von unserem Handelsalgorithmus ausgewählte Handelsgeschäft.


Unser Handelsgeschäft am nächsten Tag.

Abb. 7: Das von unserem Handelsalgorithmus am folgenden Tag ausgewählte Handelsgeschäft.

Das ist noch nicht alles, was man über Übergangsmatrizen sagen kann. Es ist jedoch eine gute Einführung in das Thema. Bevor wir unsere Diskussion abschließen, ist es wichtig zu erörtern, welche Variablen unsere Übergangsmatrix beeinflussen und wie wir die Übergangsmatrix gegebenenfalls manipulieren können.


Das Symbol
Die erste Variable, die sich auf unsere Übergangsmatrix auswirkt, ist natürlich das gewählte Symbol. Wenn wir zum Beispiel alle anderen Variablen unverändert lassen und einfach ein neues Symbol, „Boom 1000 Index“, auswählen, sieht unsere Übergangsmatrix folgendermaßen aus.



UP DOWN
UP 0.926 0.074
DOWN 0.043 0.957

Wie Sie sehen können, lag die Wahrscheinlichkeit, dass 2 aufeinanderfolgende Kerzen über dem gleitenden Durchschnitt liegen, bei 88 %, als wir den EURUSD als Symbol ausgewählt haben. Mit dem neuen Symbol, „Boom 1000 Index“, ist die Wahrscheinlichkeit, dass 2 aufeinanderfolgende Kerzen über dem gleitenden Durchschnitt liegen, auf 93 % gestiegen. Das Symbol der Wahl hat also einen unbestreitbaren Einfluss auf die Übergangsmatrix.

Die Parameter der technischen Indikatoren

Erinnern Sie sich, dass wir technische Indikatoren verwendet haben, um den Marktzustand in Bezug auf den Indikator leicht zu definieren. Daher würde eine Änderung der Periode des gleitenden Durchschnitts die Übergangsmatrix stark beeinflussen. Zur einfachen Veranschaulichung kehren wir zu unseren ursprünglichen Bedingungen für die Modellierung des EURUSD zurück, wobei der einzige Unterschied darin besteht, dass wir eine Periode von 2 verwenden, während wir in unserem ursprünglichen Beispiel eine Periode von 20 verwendeten. Alle anderen Variablen werden konstant gehalten.


UP DOWN
UP 0.456 0.544
DOWN 0.547 0.453

Beachten Sie, dass sich die Übergangswahrscheinlichkeiten nun auf 50/50 Chancen in beide Richtungen annähern. Dies sagt uns implizit, dass unsere Übergangswahrscheinlichkeiten mit zunehmender Dauer des gleitenden Durchschnitts immer weiter von einer 50/50-Chance entfernt sind.

Lücke zwischen den Kerzen

In unserer Diskussion ging es nur um die Beziehung zwischen zwei aufeinanderfolgenden Kerzen. Wenn wir jedoch den Abstand zwischen den untersuchten Kerzen vergrößern, ändert sich auch unsere Übergangsmatrix. Auch hier kehren wir zu den Ausgangsbedingungen zurück, die wir für die Modellierung des EURUSD verwendet haben, wobei wir dieses Mal den Abstand zwischen den beiden Kerzen auf 100 erhöhen. Alle anderen Variablen bleiben also gleich, mit Ausnahme des Abstands zwischen den beiden Kerzen.


UP DOWN
UP 0.503 0.497
DOWN 0.507 0.493


Empfehlungen 

Es gibt keine absolut „richtige“ oder „falsche“ Art und Weise, eine Markov-Kette zu entwerfen. Damit Ihre Anwendung jedoch mit unserer Diskussion übereinstimmt, ist es zwingend erforderlich, dass Sie bei der Erstellung Ihrer Markov-Ketten das unten beschriebene Entwurfsmuster befolgen:
transition_matrix = pd.DataFrame({
    "UP":["UP AND UP","UP AND DOWN"],
    "DOWN":["DOWN AND UP","DOWN AND DOWN"]
},index=['UP','DOWN'])

Unsere Übergangsmatrix soll uns schnell zeigen, ob wir dem Trend folgen oder gegen den Trend spielen sollten.

Trendfolgestrategien funktionieren am besten, wenn die Hauptdiagonale die größten Wahrscheinlichkeiten enthält, d. h. wenn der Markt dazu neigt, einen Trend aufzugreifen, bleibt er tendenziell im Trend:

Trendfolge

Abb. 8: Eine trendfolgende Übergangsmatrix.

Umgekehrt funktionieren Strategien, mit der Rückkehr zum Mittelwert, am besten, wenn die Off-Diagonale die größten Wahrscheinlichkeiten enthält, was bedeutet, dass der Markt dazu neigt, zum Gleichgewichtsniveau zurückzukehren:

Rückkehr zum Mittelwert

Abb. 9: Eine Übergangsmatrix für eine Rückkehr zum Mittelwert.

Wenn die größten Wahrscheinlichkeiten in der untersten Reihe zu finden sind, bedeutet dies, dass der Markt fällt:


Abwärtsmarkt

Abb. 10: Die Übergangsmatrix eines Abwärtsmarktes


Wenn schließlich die größten Wahrscheinlichkeiten in der obersten Zeile zu finden sind, bedeutet dies, dass der Markt steigt:

Aufwärtsmarkt

Abb. 11: Die Übergangsmatrix eines Aufwärtsmarktes


MQL5-Implementierung

Wir werden nun die Strategie mit MQL5 implementieren, damit wir die Strategie anhand echter Marktdaten ausgiebig testen können.

Zuerst laden wir die benötigten Bibliotheken.

//+------------------------------------------------------------------+
//|                                          Transition Matrices.mq5 |
//|                                       Gamuchirai Zororo Ndawana. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Zororo Ndawana."
#property link      "https://www.mql5.com"
#property version   "1.00"

//+------------------------------------------------------------------+
//|Overview                                                          |
//+------------------------------------------------------------------+
/*
This expert advisor will demonstrate how we can use transition matrices to build
self optimizing expert advisors. We will use the transition matrix to decide whether
we should employ trend following, or mean reverting trading strategies.

Gamuchirai Zororo Ndawana
Friday 19 July 2024, 10:09
Selebi Phikwe
Botswana
*/

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade/Trade.mqh>//Trade class
CTrade Trade;

Als Nächstes definieren wir Eingabeparameter, die vom Endnutzer bearbeitet werden können.

//+------------------------------------------------------------------+
//| Inputs                                                            |
//+------------------------------------------------------------------+
input int fetch = 5; //How much historical data should we fetch?
input int look_ahead = 1; //Our forecast horizon

input int ma_period = 20; //The MA Period
input int rsi_period = 20; //The RSI Period
input int wpr_period = 20; //The Williams Percent Range Period

input int  lot_multiple = 20; //How big should the lot sizes be
input double sl_width = 0.4; //Stop loss size

Weiter geht es mit den globalen Variablen, die wir in unserer gesamten Anwendung benötigen werden.

//+------------------------------------------------------------------+
//|Global variables                                                  |
//+------------------------------------------------------------------+
double minimum_volume;//Smallest lot size
double ask_price;//Ask
double bid_price;//Bid
int ma_handler,rsi_handler,wpr_handler;//The handlers for our technical indicators
vector ma_readings(fetch);//MA indicator values
vector rsi_readings(fetch);//RSI indicator values
vector wpr_readings(fetch);//WPR indicator values
vector price_readings(fetch);//The vector we will use to store our historical price values
matrix transition_matrix = matrix::Zeros(2,2);//The matrix to store our observations on price's transition behavior
bool transition_matrix_initialized = false;//This flag will instruct the application to initialize the transition matrix
double up_and_up = 0;//These variables will keep count of the price transitions
double up_and_down = 0;
double down_and_up = 0;
double down_and_down = 0;
double total_count = (double) fetch - look_ahead;//This variable will store the total number of observations used to calculate the transition matrix
double trading_volume;//This is our desired trading size
vector market_behavior = vector::Zeros(4);//Transition matrix interpretations

Wir müssen die Initialisierungsfunktion für unseren Expert Advisor definieren. Diese Funktion stellt sicher, dass der Nutzer gültige Eingaben gemacht hat und richtet unsere technischen Indikatoren ein.

//+------------------------------------------------------------------+
//| Initialization Function                                          |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the technical indicator
   ma_handler = iMA(_Symbol,PERIOD_CURRENT,ma_period,0,MODE_EMA,PRICE_CLOSE);
   rsi_handler = iRSI(_Symbol,PERIOD_CURRENT,rsi_period,PRICE_CLOSE);
   wpr_handler = iWPR(_Symbol,PERIOD_CURRENT,wpr_period);
   minimum_volume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   trading_volume = minimum_volume * lot_multiple;
//--- Look ahead cannot be greater than fetch
   if(look_ahead > fetch)
     {
      Comment("We cannot forecast further into the future than thr to total amount of  data fetched.\nEither fetch more data or forecast nearer to the present.");
      return(INIT_FAILED);
     }
//--- End of initialization
   return(INIT_SUCCEEDED);
  }

Unser Programm benötigt auch eine Prozedur, die bei der Deinitialisierung zu befolgen ist.

//+------------------------------------------------------------------+
//| Expert de-initialization function                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove technical indicators
   IndicatorRelease(rsi_handler);
   IndicatorRelease(wpr_handler);
   IndicatorRelease(ma_handler);
//--- Remove Expert Advisor
   ExpertRemove();
  }

Wir werden auch eine Funktion erstellen, die unsere technischen Indikatoren aktualisiert und aktuelle Marktpreise abruft.

//+------------------------------------------------------------------+
//|This function will update our technical indicator values          |
//+------------------------------------------------------------------+
void update_technical_indicators(void)
  {
//--- Update bid and ask price
   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   bid_price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
//--- Update each indicator value, we only need the most recent reading
   rsi_readings.CopyIndicatorBuffer(rsi_handler,0,0,1);
   wpr_readings.CopyIndicatorBuffer(wpr_handler,0,0,1);
   ma_readings.CopyIndicatorBuffer(ma_handler,0,0,1);
  }

Denken Sie daran, dass unsere Interpretationen der technischen Indikatoren immer vom Verhalten des Marktes abhängen, das durch die Übergangsmatrix gemessen wird.

//+------------------------------------------------------------------+
//|This function will find an entry opportunity based on our signals |                                                                  |
//+------------------------------------------------------------------+
void find_entry(void)
  {
//--- Store the index of our largest entry
   ulong max_arg = market_behavior.ArgMax();

//--- First we have to know the behavior of the market before we decide to buy or sell
   if(max_arg == 0)
     {
      //--- This means that the market is bullish and we should probably only take buy oppurtunities
      Comment("The observed transition matrix can only be generated by a bullish market");
      bullish_sentiment(0);
     }
   else
      if(max_arg == 1)
        {
         //--- This means that the market is bearish and we should probably only take sell oppurtunities
         Comment("The observed transition matrix can only be generated by a bearish market");
         bearish_sentiment(0);
        }
      else
         if(max_arg == 2)
           {
            //--- This means that the market trends and we should probably join either side of the trend
            Comment("The observed transition matrix can only be generated by a trending market");
            bearish_sentiment(0);
            bullish_sentiment(0);
           }
         else
            if(max_arg == 3)
              {
               //--- This means that the market is mean reverting and we should probably play against the trends on either side
               Comment("The observed transition matrix can only be generated by a mean reverting market");
               bearish_sentiment(-1);
               bullish_sentiment(-1);
              }
  }

Wir brauchen eine Funktion, um unsere Kaufaufträge auszuführen.

//+----------------------------------------------------------------+
//|This function will look for oppurtunities to buy                |
//+----------------------------------------------------------------+
void bullish_sentiment(int f_flag)
  {
//--- This function analyses the market for bullish sentiment using our technical indicator
//--- It has only 1 parameter, a flag denoting whether we should interpret the indicators in a trend following fashion
//--- or a mean reverting fashion. For example 0 means interpret the indicators in a trend following fashion.
//--- Therefore if we call the function and pass 0, RSI readings above 50 will trigger buy orders.
//--- However if -1 was passed then RSI readings below 50 will trigger buy orders.
//--- First make sure we have no open positions
   if(PositionsTotal() > 0)
     {
      return;
     }

//--- Interpret the flag
   if(f_flag == 0)
     {
      //--- The flag is telling us to follow the trend
      if((rsi_readings[0] > 50) && (wpr_readings[0] > -50))
        {
         Trade.Buy(trading_volume,_Symbol,ask_price,(ask_price - sl_width),(ask_price + sl_width),"Transition Matrix Order");
        }
     }
   else
      if(f_flag == -1)
        {
         //--- The flag is telling us to bet against the trend
         if((rsi_readings[0] < 50) && (wpr_readings[0] < -50))
           {
            Trade.Buy(trading_volume,_Symbol,ask_price,(ask_price - sl_width),(ask_price + sl_width),"Transition Matrix Order");
           }
        }

  }

Diese Funktion führt unsere Verkaufsaufträge für uns aus. Erinnern Sie sich daran, dass wir die Indikatoren in die „entgegengesetzte“ Richtung interpretieren, wenn unser Markt ein „Zurück-Zum-Mittelwert“ aufweist.

//+-------------------------------------------------------------+
//|This function will help us find oppurtunities to sell        |
//+-------------------------------------------------------------+
void bearish_sentiment(int f_flag)
  {
//--- This function analysises the market for bearish sentiment using our technical indicator
//--- It has only 1 parameter, a flag denoting whether we should interpret the indicators in a trend following fashion
//--- or a mean reverting fashion. For example 0 means interpret the indicators in a trend following fashion.
//--- Therefore if we call the function and pass 0, RSI readings below 50 will trigger sell orders.
//--- However if -1 was passed then RSI readings above 50 will trigger sell orders.
//--- First make sure we have no open positions
   if(PositionsTotal() > 0)
     {
      return;
     }
//--- Interpret the flag
   if(f_flag == 0)
     {
      //--- Now we know how to interpret our technical indicators
      if((rsi_readings[0] < 50) && (wpr_readings[0] < -50))
        {
         Trade.Sell(trading_volume,_Symbol,bid_price,(bid_price + sl_width),(bid_price - sl_width),"Transition Matrix Order");
        }
     }
   else
      if(f_flag == -1)
        {
         //--- Now we know how to interpret our technical indicators
         if((rsi_readings[0] > 50) && (wpr_readings[0] > -50))
           {
            Trade.Sell(trading_volume,_Symbol,bid_price,(bid_price + sl_width),(bid_price - sl_width),"Transition Matrix Order");
           }
        }
  }

Lassen Sie uns auch eine Funktion definieren, die sicherstellt, dass unsere Übergangsmatrix gemäß dem oben beschriebenen Verfahren vorbereitet und berechnet wird.

//+---------------------------------------------------------------+
//|This function will initialize our transition matrix            |
//+---------------------------------------------------------------+
void initialize_transition_matrix(void)
  {
//--- We need to update our historical price readings and our MA readings
   ma_readings.CopyIndicatorBuffer(ma_handler,0,1,fetch);
   price_readings.CopyRates(_Symbol,PERIOD_CURRENT,COPY_RATES_CLOSE,1,fetch);

//--- Now let us update our transition matrix
   for(int i = 0; i < fetch - look_ahead; i++)
     {
      //--- Did price go from being above the MA but end up beneath the MA?
      if((price_readings[i] > ma_readings[i]) && (price_readings[i + look_ahead] < ma_readings[i + look_ahead]))
        {
         up_and_down += 1;
        }
      //--- Did price go from being above the MA and remain above it?
      else
         if((price_readings[i] > ma_readings[i]) && (price_readings[i + look_ahead] > ma_readings[i + look_ahead]))
           {
            up_and_up += 1;
           }

         //--- Did price go from being below the MA but end up above it?
         else
            if((price_readings[i] < ma_readings[i]) && (price_readings[i + look_ahead] > ma_readings[i + look_ahead]))
              {
               down_and_up += 1;
              }

            //--- Did price go from being below the MA and remain below it?
            else
               if((price_readings[i] < ma_readings[i]) && (price_readings[i + look_ahead] < ma_readings[i + look_ahead]))
                 {
                  down_and_down += 1;
                 }
     }

//--- Let us see our counts so far
   Print("Up and up: ",up_and_up,"\nUp and down: ",up_and_down,"\nDown and up: ",down_and_up,"\nDown and down: ",down_and_down);
   double sum_of_counts = up_and_up + up_and_down + down_and_up + down_and_down;
   Print("Sum of counts: ",(sum_of_counts),"\nObservations made: ",total_count,"\nDifference:[the difference should always be 0] ",(total_count - sum_of_counts));
//--- Now we will calculate the transition matrix
//--- The matrix position (0,0) stores the probaility that after making a move up, the market will continue rising
//--- The matrix position (0,1) stores the probability that after making a move down, price will reverse and start rising
//--- The matrix position (1,0) stores the probability that after making a move up, price will reverse and start falling
//--- The matrix position (1,1) stores the probabilty that after making a move down, price will continue falling
   transition_matrix[0][0] = up_and_up / (up_and_up + up_and_down);
   transition_matrix[0][1] = down_and_up / (up_and_up + up_and_down);
   transition_matrix[1][0] = up_and_down / (down_and_up + down_and_down);
   transition_matrix[1][1] = down_and_down / (down_and_up + down_and_down);
//--- Show the transition matrix
   Print("Our transition Matrix");
   Print(transition_matrix);
//--- Now we need to make sense of the transition matrix
   analyse_transition_matrix();
//--- Now we need to update the flag
   transition_matrix_initialized = true;
  }

Wir brauchen auch eine Hilfsfunktion, um unsere Übergangsmatrix zu interpretieren.

//+-------------------------------------------------------------+
//|This function will analyse our transition matrix             |
//+-------------------------------------------------------------+
void analyse_transition_matrix(void)
  {
//--- Check if the market is bullish
   if((transition_matrix[0][0] > transition_matrix[1][0])&&(transition_matrix[0][1] > transition_matrix[1][1]))
     {
      market_behavior[0] = 1;
     }
//--- Check if the market is bearish
   else
      if((transition_matrix[0][1] > transition_matrix[1][0])&&(transition_matrix[1][1] > transition_matrix[1][0]))
        {
         market_behavior[1] = 1;
        }
      //--- Check if the market trends
      else
         if(transition_matrix.Trace() > 1)
           {
            market_behavior[2] = 1;
           }
         //--- Check if the market is mean reverting
         else
            if(transition_matrix.Trace() < 1)
              {
               market_behavior[3] = 1;
              }
  }
//+------------------------------------------------------------------+

Unser OnTick wird dafür sorgen, dass alle oben beschriebenen Funktionen zum richtigen Zeitpunkt aufgerufen werden.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- First we must check if our transition matrix has been initialized
   if(!transition_matrix_initialized)
     {
      initialize_transition_matrix();
     }
//--- Otherwise our transition matrix has been initialized
   else
     {
      //--- Update technical indicator values
      update_technical_indicators();
      //--- If we have no open positions we will use our tranistion matrix to help us interpret our technical indicators
      if(PositionsTotal() == 0)
        {
         find_entry();
        }
     }
  }
//+------------------------------------------------------------------+

Übergangsmatrix

Abb. 12: Unsere in MQL5 berechnete Übergangsmatrix.

Testen unseres EA

Abb. 13: Unser Expert Advisor handelt das Paar AUDJPY.


Schlussfolgerung

Dieser Artikel befasst sich mit der Anwendung von Markov-Ketten im algorithmischen Handel zur Anpassung an sich ändernde Marktbedingungen. Wir beginnen mit einer Einführung in das Konzept der Markov-Ketten und veranschaulichen ihre Nützlichkeit bei der Modellierung von Zufallsprozessen, die der Marktdynamik ähneln. Durch die Definition von Marktzuständen anhand von technischen Indikatoren, wie gleitenden Durchschnitten, zeigen wir, wie man eine Markov-Kette zur Analyse von Marktübergängen konstruiert. Dieser Ansatz ermöglicht es uns, die Wahrscheinlichkeit zukünftiger Marktbewegungen zu bestimmen, was uns bei der Entscheidung hilft, ob wir trendfolgende oder mittelwertumkehrende Strategien einsetzen sollen. Mit dieser Methode wollen wir intelligente Handelsalgorithmen mit verbesserten Entscheidungsfähigkeiten entwickeln, die letztlich die Handelsleistung auf dynamischen Märkten verbessern.


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

Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Aufbau des Kerzenmodells Trend-Constraint (Teil 7): Verfeinerung unseres Modells für die EA-Entwicklung Aufbau des Kerzenmodells Trend-Constraint (Teil 7): Verfeinerung unseres Modells für die EA-Entwicklung
In diesem Artikel werden wir uns mit der detaillierten Vorbereitung unseres Indikators für die Entwicklung von Expert Advisor (EA) befassen. Unsere Diskussion wird weitere Verfeinerungen der aktuellen Version des Indikators umfassen, um seine Genauigkeit und Funktionsweise zu verbessern. Außerdem werden wir neue Funktionen einführen, die Ausstiegspunkte markieren und damit eine Einschränkung der Vorgängerversion beheben, die nur Einstiegspunkte kennzeichnete.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Kolmogorov-Smirnov-Test bei zwei Stichproben als Indikator für die Nicht-Stationarität von Zeitreihen Kolmogorov-Smirnov-Test bei zwei Stichproben als Indikator für die Nicht-Stationarität von Zeitreihen
Der Artikel befasst sich mit einem der bekanntesten nichtparametrischen Homogenitätstests – dem Kolmogorov-Smirnov-Test mit zwei Stichproben. Es werden sowohl Modelldaten als auch reale Kurse analysiert. Der Artikel enthält auch ein Beispiel für die Konstruktion eines Nicht-Stationaritätsindikators (iSmirnovDistance).