
Selbstoptimierende Expert Advisors mit MQL5 und Python erstellen
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?
Abb. 1: Ein Bild des jungen Andrey Markov.
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).“
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
#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")
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
#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
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:
- Definiere alle möglichen Zustände (wir haben 2 einfache Zustände definiert, UP und DOWN).
- Zähle, wie viele Kerzen in den jeweiligen Zustand fallen.
- Berechne, welcher Anteil aller Kerzen im Zustand UP von einer weiteren Kerze im gleichen Zustand gefolgt wurde.
- 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]) / downJetzt 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
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.
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.
Abb. 6: Der von unserem Handelsalgorithmus ausgewählte Handelsgeschäft.
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.
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 IndikatorenErinnern 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 KerzenIn 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:
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:
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:
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:
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(); } } } //+------------------------------------------------------------------+
Abb. 12: Unsere in MQL5 berechnete Übergangsmatrix.
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





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.