Integration von MQL5 mit Datenverarbeitungspaketen (Teil 3): Verbesserte Datenvisualisierung
Einführung
Händler an den Finanzmärkten stehen oft vor der Herausforderung, riesigen Datenmengen zu verstehen - von Kursschwankungen und Handelsvolumen bis hin zu technischen Indikatoren und Wirtschaftsnachrichten. Angesichts der Geschwindigkeit und Komplexität der modernen Märkte ist es schwierig, diese Datenströme mit herkömmlichen Methoden effektiv zu interpretieren. Charts allein bieten möglicherweise nicht genügend Einblick, was zu verpassten Chancen oder schlecht getimten Entscheidungen führt. Die Notwendigkeit, Trends, Umschwünge und potenzielle Risiken schnell zu erkennen, macht die Sache noch schwieriger. Für Händler, die fundierte, datengestützte Entscheidungen treffen wollen, ist die Unfähigkeit, wichtige Erkenntnisse aus Daten zu destillieren, ein kritisches Problem, das zu Gewinneinbußen oder erhöhten Risiken führen kann.
Die erweiterte Datenvisualisierung geht auf diese Herausforderung ein, indem sie rohe Finanzdaten in intuitivere und interaktive visuelle Darstellungen umwandelt. Tools wie dynamische Kerzen-Charts, Überlagerungen von technischen Indikatoren und Heatmaps von Renditen bieten Händlern ein tieferes, besser umsetzbares Verständnis der Marktbedingungen. Durch die Integration von visuellen Elementen, die Trends, Korrelationen und Anomalien hervorheben, können Händler schnell Chancen erkennen und fundiertere Entscheidungen treffen. Dieser erweiterte Ansatz trägt dazu bei, die Komplexität der Dateninterpretation zu verringern, sodass Händler auf den schnelllebigen Finanzmärkten sicherer und effizienter handeln können.
Sammeln der historischen Daten
from datetime import datetime import MetaTrader5 as mt5 import pandas as pd import pytz # Display data on the MetaTrader 5 package print("MetaTrader5 package author: ", mt5.__author__) print("MetaTrader5 package version: ", mt5.__version__) # Configure pandas display options pd.set_option('display.max_columns', 500) pd.set_option('display.width', 1500) # Establish connection to MetaTrader 5 terminal if not mt5.initialize(): print("initialize() failed, error code =", mt5.last_error()) quit() # Set time zone to UTC timezone = pytz.timezone("Etc/UTC") # Create 'datetime' objects in UTC time zone to avoid the implementation of a local time zone offset utc_from = datetime(2024, 1, 2, tzinfo=timezone) utc_to = datetime.now(timezone) # Set to the current date and time # Get bars from XAUUSD H1 (hourly timeframe) within the specified interval rates = mt5.copy_rates_range("XAUUSD", mt5.TIMEFRAME_H1, utc_from, utc_to) # Shut down connection to the MetaTrader 5 terminal mt5.shutdown() # Check if data was retrieved if rates is None or len(rates) == 0: print("No data retrieved. Please check the symbol or date range.") else: # Display each element of obtained data in a new line (for the first 10 entries) print("Display obtained data 'as is'") for rate in rates[:10]: print(rate) # Create DataFrame out of the obtained data rates_frame = pd.DataFrame(rates) # Convert time in seconds into the 'datetime' format rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s') # Save the data to a CSV file filename = "XAUUSD_H1_2nd.csv" rates_frame.to_csv(filename, index=False) print(f"\nData saved to file: {filename}")
Um historische Daten abzurufen, stellen wir zunächst mit der Funktion „mt5.initialize()“ eine Verbindung zum MetaTrader 5 Terminal her. Dies ist wichtig, da das Python-Paket direkt mit der laufenden MetaTrader 5-Plattform kommuniziert. Wir konfigurieren den Code, um den gewünschten Zeitraum für die Datenextraktion festzulegen, indem wir das Start- und Enddatum angeben. Die „datetime“-Objekte werden in der UTC-Zeitzone erstellt, um die Konsistenz zwischen verschiedenen Zeitzonen zu gewährleisten. Das Skript verwendet dann die Funktion „mt5.copy-rates-range()“, um stündliche historische Daten für das Symbol XAUUSD ab dem 2. Januar 2024 bis zum aktuellen Datum und zur aktuellen Uhrzeit abzufragen.
Nachdem wir die historischen Daten erhalten haben, trennen wir die Verbindung zum MetaTrader 5 Terminal mit „mt5.shutdown()“, um weitere unnötige Verbindungen zu vermeiden. Die abgerufenen Daten werden zunächst in ihrem Rohformat angezeigt, um die erfolgreiche Datenextraktion zu bestätigen. Wir konvertieren diese Daten in einen Pandas DataFrame, um sie leichter bearbeiten und analysieren zu können. Darüber hinaus konvertiert der Code die Unix-Zeitstempel in ein lesbares Datetime-Format, sodass die Daten gut strukturiert und für die weitere Verarbeitung oder Analyse bereit sind. Dieser Ansatz ermöglicht es Händlern, historische Marktbewegungen zu analysieren und fundierte Handelsentscheidungen auf der Grundlage der bisherigen Performance zu treffen.
filename = "XAUUSD_H1_2nd.csv" rates_frame.to_csv(filename, index=False) print(f"\nData saved to file: {filename}")
Da mein Betriebssystem Linux ist, muss ich die empfangenen Daten in einer Datei speichern. Wer jedoch mit Windows arbeitet, kann die Daten mit dem folgenden Skript abrufen:
from datetime import datetime import MetaTrader5 as mt5 import pandas as pd import pytz # Display data on the MetaTrader 5 package print("MetaTrader5 package author: ", mt5.__author__) print("MetaTrader5 package version: ", mt5.__version__) # Configure pandas display options pd.set_option('display.max_columns', 500) pd.set_option('display.width', 1500) # Establish connection to MetaTrader 5 terminal if not mt5.initialize(): print("initialize() failed, error code =", mt5.last_error()) quit() # Set time zone to UTC timezone = pytz.timezone("Etc/UTC") # Create 'datetime' objects in UTC time zone to avoid the implementation of a local time zone offset utc_from = datetime(2024, 1, 2, tzinfo=timezone) utc_to = datetime.now(timezone) # Set to the current date and time # Get bars from XAUUSD H1 (hourly timeframe) within the specified interval rates = mt5.copy_rates_range("XAUUSD", mt5.TIMEFRAME_H1, utc_from, utc_to) # Shut down connection to the MetaTrader 5 terminal mt5.shutdown() # Check if data was retrieved if rates is None or len(rates) == 0: print("No data retrieved. Please check the symbol or date range.") else: # Display each element of obtained data in a new line (for the first 10 entries) print("Display obtained data 'as is'") for rate in rates[:10]: print(rate) # Create DataFrame out of the obtained data rates_frame = pd.DataFrame(rates) # Convert time in seconds into the 'datetime' format rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s') # Display data directly print("\nDisplay dataframe with data") print(rates_frame.head(10))
Und wenn Sie aus irgendeinem Grund keine historischen Daten erhalten können, können Sie diese mit den folgenden Schritten manuell auf Ihrer MetTrader5-Plattform abrufen. Starten Sie Ihre MetaTrader-Plattform und navigieren Sie oben in Ihrem MetaTrader 5-Panel zu > Tools und dann > Options und Sie gelangen zu den Charts-Optionen. Sie müssen dann die Anzahl der Balken in dem Chart auswählen, das Sie herunterladen möchten. Am besten wählen Sie die Option unbegrenzte Balken, da wir mit dem Datum arbeiten und nicht wissen, wie viele Balken in einem bestimmten Zeitraum vorhanden sind.
Danach müssen Sie nun die eigentlichen Daten herunterladen. Dazu müssen Sie zu > Ansicht und dann zu > Symbole navigieren, um auf die Registerkarte Spezifikationen zu gelangen. Navigieren Sie einfach zu > Balken oder Ticks, je nachdem, welche Art von Daten Sie herunterladen möchten. Fahren Sie fort und geben Sie das Anfangs- und Enddatum der historischen Daten ein, die Sie herunterladen möchten. Klicken Sie anschließend auf die Schaltfläche „Anfordern“, um die Daten herunterzuladen und im .csv-Format zu speichern.
MetaTrader 5 Datenvisualisierung auf Jupyter Lab
Um Ihre historischen MetaTrader 5-Daten in Jupyter Lab zu laden, müssen Sie zunächst den Ordner finden, in den die Daten heruntergeladen wurden. Sobald Sie in Jupyter Lab sind, navigieren Sie zu diesem Ordner, um auf die Dateien zuzugreifen. Der nächste Schritt besteht darin, die Daten zu laden und die Spaltennamen zu überprüfen. Die Überprüfung der Spaltennamen ist wichtig, um sicherzustellen, dass Sie die Daten korrekt verwalten und Fehler vermeiden, die durch die Verwendung falscher Spaltennamen entstehen könnten.
Python-Code:
import pandas as pd # assign variable to the historical data file_path = '/home/int_junkie/Documents/ML/predi/XAUUSD.m_H1_2nd.csv' data = pd.read_csv(file_path, delimiter='\t') # Display the first few rows and column names print(data.head()) print(data.columns)
Unsere historischen Daten beginnen am 2. Januar 2024 und reichen bis zu den aktuellen Daten.
Python-Code:
# Convert the <DATE> and <TIME> columns into a single datetime column data['<DATETIME>'] = pd.to_datetime(data['<DATE>'] + ' ' + data['<TIME>'], format='%Y.%m.%d %H:%M:%S') # Drop the original <DATE> and <TIME> columns data = data.drop(columns=['<DATE>', '<TIME>']) # Convert numeric columns from strings to appropriate float types numeric_columns = ['<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>', '<TICKVOL>', '<VOL>', '<SPREAD>'] data[numeric_columns] = data[numeric_columns].apply(pd.to_numeric) # Set datetime as index for easier plotting data.set_index('<DATETIME>', inplace=True) # Let's plot the close price and tick volume to visualize the trend import matplotlib.pyplot as plt # Plot closing price and tick volume fig, ax1 = plt.subplots(figsize=(12, 6)) # Close price on primary y-axis ax1.set_xlabel('Date') ax1.set_ylabel('Close Price', color='tab:blue') ax1.plot(data.index, data['<CLOSE>'], color='tab:blue', label='Close Price') ax1.tick_params(axis='y', labelcolor='tab:blue') # Tick volume on secondary y-axis ax2 = ax1.twinx() ax2.set_ylabel('Tick Volume', color='tab:green') ax2.plot(data.index, data['<TICKVOL>'], color='tab:green', label='Tick Volume') ax2.tick_params(axis='y', labelcolor='tab:green') # Show the plot plt.title('Close Price and Tick Volume Over Time') fig.tight_layout() plt.show()
Das obige Diagramm zeigt zwei Schlüsselkennzahlen im Zeitverlauf:
- Schlusskurse (in blau): Dies sind die Schlusskurse für jede Stunde im Chart. Im Laufe der Zeit sind Schwankungen zu beobachten, die auf Phasen mit einem Aufwärts- und Abwärtstrend des Goldpreises (XAU/USD) hinweisen.
- Tick-Volumen (in grün): Hier wird die Anzahl der Preisänderungen innerhalb einer Stunde angegeben. Ausschläge im Tick-Volumen entsprechen oft einer erhöhten Marktaktivität oder Volatilität. So können beispielsweise Perioden mit hohem Volumen mit bedeutenden Kursbewegungen zusammenfallen, die wichtige Ereignisse oder Veränderungen der Marktstimmung signalisieren könnten.
Lassen Sie uns nun tiefer in unsere Daten eintauchen:
1. Trendanalyse.
# Calculating moving averages: 50-period and 200-period for trend analysis data['MA50'] = data['<CLOSE>'].rolling(window=50).mean() data['MA200'] = data['<CLOSE>'].rolling(window=200).mean() # Plot close price along with the moving averages plt.figure(figsize=(12, 6)) # Plot close price plt.plot(data.index, data['<CLOSE>'], label='Close Price', color='blue') # Plot moving averages plt.plot(data.index, data['MA50'], label='50-Period Moving Average', color='orange') plt.plot(data.index, data['MA200'], label='200-Period Moving Average', color='red') plt.title('Close Price with 50 & 200 Period Moving Averages') plt.xlabel('Date') plt.ylabel('Price') plt.legend(loc='best') plt.grid(True) plt.show()
Das Chart zeigt die Schlusskurse zusammen mit den gleitenden Durchschnitten der 50- und 200-Periode:
- Schlusskurse (blaue Linie): Dies ist der tatsächliche Preis am Ende eines jeden Zeitraums.
- Gleitender 50-Perioden-Durchschnitt (orangefarbene Linie): Ein schneller, gleitender Durchschnitt, der die Preisdaten über 50 Perioden glättet. Wenn der Schlusskurs diese Linie überschreitet, kann dies ein Zeichen für einen potenziellen Aufwärtstrend sein, und wenn er sie unterschreitet, kann dies auf einen Abwärtstrend hindeuten.
- Gleitender 200-Balken-Durchschnitt (rote Linie): Ein längerfristiger gleitender Durchschnitt, der Aufschluss über den Gesamttrend gibt. Er neigt dazu, langsamer auf Kursänderungen zu reagieren, sodass Überkreuzungen mit dem gleitenden 50-Perioden-Durchschnitt bedeutende langfristige Trendumkehrungen signalisieren können.
Wenn der gleitende 50-Perioden-Durchschnitt über dem gleitenden 200-Perioden-Durchschnitt kreuzt, kann dies auf einen potenziellen starken Aufwärtstrend hindeuten. Wenn der gleitende 50-Perioden-Durchschnitt unter den gleitenden 200-Perioden-Durchschnitt fällt, kann dies auf einen potenziellen Abwärtstrend hindeuten.
Anschließend analysieren wir die Volatilität, indem wir die Preisspanne (Differenz zwischen Höchst- und Tiefstkurs) berechnen und visualisieren.
2. Volatilitätsanalyse.
# Calculate the price range (High - Low) data['Price_Range'] = data['<HIGH>'] - data['<LOW>'] # Calculate Bollinger Bands # Use a 20-period moving average and 2 standard deviations data['MA20'] = data['<CLOSE>'].rolling(window=20).mean() data['BB_upper'] = data['MA20'] + 2 * data['<CLOSE>'].rolling(window=20).std() data['BB_lower'] = data['MA20'] - 2 * data['<CLOSE>'].rolling(window=20).std() # Plot the price range and Bollinger Bands along with the close price plt.figure(figsize=(12, 8)) # Plot the close price plt.plot(data.index, data['<CLOSE>'], label='Close Price', color='blue') # Plot Bollinger Bands plt.plot(data.index, data['BB_upper'], label='Upper Bollinger Band', color='red', linestyle='--') plt.plot(data.index, data['BB_lower'], label='Lower Bollinger Band', color='green', linestyle='--') # Fill the area between Bollinger Bands for better visualization plt.fill_between(data.index, data['BB_upper'], data['BB_lower'], color='gray', alpha=0.3) # Plot the price range on a separate axis plt.figure(figsize=(12, 6)) plt.plot(data.index, data['Price_Range'], label='Price Range (High-Low)', color='purple') plt.title('Bollinger Bands and Price Range (Volatility Analysis)') plt.xlabel('Date') plt.ylabel('Price') plt.legend(loc='best') plt.grid(True) plt.show()
Aus der obigen Ausgabe geht hervor, dass wir Folgendes getan haben:
- Berechnung der Preisspanne: Wir berechnen die Preisspanne für jeden Zeitraum, indem wir den niedrigsten Preis „<LOW>“ vom höchsten Preis „<High>“ subtrahieren. Dies gibt Aufschluss über das Ausmaß der Preisbewegung während jeder Stunde und hilft, die Volatilität für diesen Zeitraum zu messen.
- data[Price-Range] = data[<HIGH>] - data[<LOW>] : Die sich daraus ergebende Spalte „Price-Range“ zeigt, wie stark der Preis innerhalb jeder Stunde schwankte.
- Berechnung der Bollinger-Bänder: Der Bollinger-Bänder-Indikator gibt Aufschluss über die Preisvolatilität. „MA-20“ ist der gleitende 20-Balken-Durchschnitt der Schlusskurse. Sie dient als Mittellinie der Bollinger Bänder. „BB-upper“ und „BB-lower“ stellen das obere bzw. untere Band dar. Sie werden als zwei Standardabweichungen über und unter dem gleitenden 20-Perioden-Durchschnitt berechnet. Wenn sich die Kurse in Richtung des oberen Bandes bewegen, deutet dies darauf hin, dass der Markt überkauft sein könnte; in ähnlicher Weise deuten Bewegungen in Richtung des unteren Bandes darauf hin, dass der Markt überverkauft sein könnte.
- Visualisierung von Preis und Bollinger Bändern: Schlusskurse (blaue Linie), stellt die tatsächlichen Schlusskurse für jeden Zeitraum dar. Oberes Bollinger-Band (rot gestrichelte Linie) und unteres Bollinger-Band (grün gestrichelte Linie): Diese Linien zeigen die Volatilitätsbänder, die die Bollinger-Bänder um den gleitenden Durchschnitt bilden. Der Bereich zwischen dem oberen und dem unteren Bollinger-Band ist grau schattiert und stellt visuell den Volatilitätsbereich dar, in dem sich der Kurs voraussichtlich bewegen wird.
- Separate Preisspanne Plot: Das zweite Diagramm zeigt die Preisspanne als separate violette Linie an, die zeigt, wie die Volatilität im Laufe der Zeit schwankt. Größere Ausschläge in diesem Diagramm weisen auf Zeiten erhöhter Volatilität hin.
import pandas as pd import talib from stable_baselines3 import DQN from stable_baselines3.common.env_checker import check_env # Verify the environment check_env(env) # Initialize and train the DQN model model = DQN('MlpPolicy', env, verbose=1) model.learn(total_timesteps=10000) # Save the trained model model.save("trading_dqn_model") # Load and preprocess the data as before data = pd.read_csv('XAUUSD_H1_Data-V.csv', delimiter='\t') data['<DATETIME>'] = pd.to_datetime(data['<DATE>'] + ' ' + data['<TIME>'], format='%Y.%m.%d %H:%M:%S') data = data.drop(columns=['<DATE>', '<TIME>']) numeric_columns = ['<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>', '<TICKVOL>', '<VOL>', '<SPREAD>'] data[numeric_columns] = data[numeric_columns].apply(pd.to_numeric) data.set_index('<DATETIME>', inplace=True) # Calculate Bollinger Bands (20-period moving average with 2 standard deviations) data['MA20'] = data['<CLOSE>'].rolling(window=20).mean() data['BB_upper'] = data['MA20'] + 2 * data['<CLOSE>'].rolling(window=20).std() data['BB_lower'] = data['MA20'] - 2 * data['<CLOSE>'].rolling(window=20).std() # Calculate percentage price change and volume change as additional features data['Pct_Change'] = data['<CLOSE>'].pct_change() data['Volume_Change'] = data['<VOL>'].pct_change() # Fill missing values data.fillna(0, inplace=True)
Im obigen Code bereiten wir Finanzdaten für ein Reinforcement Learning-Modell (RL) vor, um Handelsentscheidungen auf dem XAU/USD (Gold)-Markt unter Verwendung eines DQN (Deep Q-Network)-Algorithmus aus der „stable-baseline3“ zu treffen. Zunächst werden historische Daten geladen und verarbeitet, einschließlich der Berechnung von Bollinger-Bändern (ein technischer Indikator, der auf gleitenden Durchschnitten und der Preisvolatilität basiert) und dem Hinzufügen von Merkmalen wie prozentualen Preis- und Volumenänderungen. Die Umgebung wird validiert, und ein DQN-Modell wird für 10.000 Zeitschritte trainiert, wonach das Modell für die zukünftige Verwendung gespeichert wird. Schließlich werden die fehlenden Daten mit Nullen aufgefüllt, um ein reibungsloses Modelltraining zu gewährleisten.
import gym from gym import spaces import numpy as np class TradingEnv(gym.Env): def __init__(self, data): super(TradingEnv, self).__init__() # Market data and feature columns self.data = data self.current_step = 0 # Define action and observation space # Actions: 0 = Hold, 1 = Buy, 2 = Sell self.action_space = spaces.Discrete(3) # Observations (features: Bollinger Bands, Price Change, Volume Change) self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(5,), dtype=np.float32) # Initial balance and positions self.balance = 10000 # Starting balance self.position = 0 # No position at the start (0 = no trade, 1 = buy, -1 = sell) def reset(self): self.current_step = 0 self.balance = 10000 self.position = 0 return self._next_observation() def _next_observation(self): # Get the current market data (Bollinger Bands, Price Change, Volume Change) obs = np.array([ self.data['BB_upper'].iloc[self.current_step], self.data['BB_lower'].iloc[self.current_step], self.data['Pct_Change'].iloc[self.current_step], self.data['Volume_Change'].iloc[self.current_step], self.position ]) return obs def step(self, action): # Execute the trade based on action and update balance and position self.current_step += 1 # Get current price current_price = self.data['<CLOSE>'].iloc[self.current_step] reward = 0 # Reward initialization done = self.current_step == len(self.data) - 1 # Check if we're done # Buy action if action == 1 and self.position == 0: self.position = 1 self.entry_price = current_price # Sell action elif action == 2 and self.position == 1: reward = current_price - self.entry_price self.balance += reward self.position = 0 # Hold action else: reward = 0 return self._next_observation(), reward, done, {} def render(self, mode='human', close=False): # Optional: Print the current balance and position print(f"Step: {self.current_step}, Balance: {self.balance}, Position: {self.position}") # Create the trading environment env = TradingEnv(data)
Im obigen Code definieren wir eine Handelsumgebung der Klasse „TradingEnv“ unter Verwendung der Bibliothek „gym“, um eine Handelsumgebung auf der Grundlage historischer Marktdaten zu simulieren. Das Umfeld erlaubt drei mögliche Aktionen: Halten, Kaufen oder Verkaufen. Es umfasst einen Beobachtungsraum mit fünf Merkmalen (Bollinger Bänder, prozentuale Preisänderung, Volumenänderung und die aktuelle Handelsposition). Der Agent beginnt mit einem Guthaben von 10.000 Einheiten und keinem Bestand. In jedem Schritt aktualisiert die Umgebung auf der Grundlage der gewählten Aktion die Position und den Kontostand des Agenten, berechnet die Belohnungen für gewinnbringende Geschäfte und geht zum nächsten Schritt in den Daten über. Die Umgebung kann zurückgesetzt werden, um eine neue Episode zu beginnen oder den aktuellen Stand des Handelsprozesses wiederzugeben. Diese Umgebung wird für das Training von Verstärkungslernmodellen verwendet.
import gymnasium as gym from gymnasium import spaces import numpy as np from stable_baselines3 import DQN from stable_baselines3.common.env_checker import check_env # Define the custom Trading Environment class TradingEnv(gym.Env): def __init__(self, data): super(TradingEnv, self).__init__() # Market data and feature columns self.data = data self.current_step = 0 # Define action and observation space # Actions: 0 = Hold, 1 = Buy, 2 = Sell self.action_space = spaces.Discrete(3) # Observations (features: Bollinger Bands, Price Change, Volume Change) self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(5,), dtype=np.float32) # Initial balance and positions self.balance = 10000 # Starting balance self.position = 0 # No position at the start (0 = no trade, 1 = buy, -1 = sell) def reset(self, seed=None, options=None): # Initialize the random seed self.np_random, seed = self.seed(seed) self.current_step = 0 self.balance = 10000 self.position = 0 # Return initial observation and an empty info dictionary return self._next_observation(), {} def _next_observation(self): # Get the current market data (Bollinger Bands, Price Change, Volume Change) obs = np.array([ self.data['BB_upper'].iloc[self.current_step], self.data['BB_lower'].iloc[self.current_step], self.data['Pct_Change'].iloc[self.current_step], self.data['Volume_Change'].iloc[self.current_step], self.position ], dtype=np.float32) # Explicitly cast to float32 return obs def step(self, action): self.current_step += 1 current_price = self.data['<CLOSE>'].iloc[self.current_step] reward = 0 done = self.current_step == len(self.data) - 1 truncated = False # Set to False unless there's an external condition to end the episode early # Execute the action if action == 1 and self.position == 0: self.position = 1 self.entry_price = current_price elif action == 2 and self.position == 1: reward = current_price - self.entry_price self.balance += reward self.position = 0 # Return next observation, reward, terminated, truncated, and an empty info dict return self._next_observation(), reward, done, truncated, {} def render(self, mode='human', close=False): print(f"Step: {self.current_step}, Balance: {self.balance}, Position: {self.position}") def seed(self, seed=None): self.np_random, seed = gym.utils.seeding.np_random(seed) return self.np_random, seed # Assuming your data is already prepared (as a DataFrame) and includes Bollinger Bands and other necessary features # Create the environment env = TradingEnv(data) # Verify the environment check_env(env) # Train the model using DQN model = DQN('MlpPolicy', env, verbose=1) model.learn(total_timesteps=10000) # Save the trained model model.save("trading_dqn_model")
Ausgabe:
Dann definieren wir eine nutzerdefinierte Handelsumgebung mit „gymnasium“ für Reinforcement Learning, in der ein Agent lernt, Handelsentscheidungen auf der Grundlage von historischen Marktdaten zu treffen. Die Umgebung erlaubt drei Aktionen: Halten, Kaufen oder Verkaufen und bietet fünf Beobachtungen, darunter Bollinger Bands, prozentuale Preisänderung, Volumenänderung und die aktuelle Position. Der Agent beginnt mit einem Guthaben von 10.000 und keinen offenen Positionen. Jeder Schritt in der Umgebung bringt den Agenten voran, aktualisiert seine Position, sein Gleichgewicht und berechnet die Belohnungen für erfolgreiche Geschäfte. Die Umgebung wird mit der Funktion „check-Env()“ aus „stable-baseline3“ validiert, und ein DQN-Modell (Deep Q-Network) wird für 10.000 Zeitschritte trainiert, um optimale Handelsstrategien zu lernen. Das trainierte Modell wird für die zukünftige Verwendung in automatisierten Handelssystemen gespeichert.
# Unpack the observation from the reset() method obs, _ = env.reset() # Loop through the environment steps for step in range(len(data)): # Predict the action based on the observation action, _states = model.predict(obs) # Step the environment obs, rewards, done, truncated, info = env.step(action) # Render the environment (print the current state) env.render() # Check if the episode is done if done or truncated: print("Testing completed!") break
Das Ergebnis zeigt, dass die vom trainierten Modell umgesetzte Handelsstrategie zu einem kleinen Gewinn während des Handelszeitraums geführt hat, mit einem Anstieg des Guthabens von 10.000 $ auf 10.108 $. Obwohl dies darauf hindeutet, dass das Modell in der Lage war, profitable Handelsgeschäfte zu identifizieren, ist die Gewinnspanne von 108 $ (1,08 % Gewinn) relativ bescheiden.
Die Ergebnisse deuten darauf hin, dass das Modell vorsichtige oder niedrigfrequente Handelsgeschäfte tätigt, oder sie könnten darauf hindeuten, dass die Strategie nicht vollständig für höhere Renditen optimiert ist. Eine weitere Evaluierung, einschließlich eines längeren Testzeitraums oder Anpassungen der Modellparameter (z. B. der Belohnungsfunktion, der Merkmalsauswahl oder der Handelsausführungslogik), könnte dazu beitragen, die Leistung des Modells zu verbessern. Es ist auch wichtig, Faktoren wie Transaktionskosten und Risikomanagement zu berücksichtigen, um sicherzustellen, dass die Strategie langfristig rentabel bleibt.
Alles zusammen auf MQL5
Um MQL5 mit dem Python-Skript zu verbinden, das unser trainiertes Modell ausführen wird, müssen wir einen Kommunikationskanal zwischen MQL5 und Python einrichten. In unserem Fall werden wir einen Socket-Server verwenden, der üblicherweise eingesetzt wird.//+------------------------------------------------------------------+ //| EnhancedML.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Includes | //+------------------------------------------------------------------+ #include <WinAPI\winapi.mqh> #include <Trade\Trade.mqh> CTrade trade;
Erstens enthalten wir die Windows-API-Bibliothek („winapi.mqh“) für Operationen auf Systemebene und die Handelsbibliothek („trade.mqh“) für das Handelsmanagement. Dann deklarieren wir eine Instanz der Klasse (CTrade) mit dem Namen trade.
//+------------------------------------------------------------------+ //| Global Vars | //+------------------------------------------------------------------+ int stopLoss = 350; int takeProfit = 500; string Address = "127.0.0.1"; int port = 9999; int socket = SocketCreate(); double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
Wir setzen „Address“ auf 127.0.0.1 (lokaler Host) und den „Port“ auf 9999, den wir für die Socket-Kommunikation verwenden werden. Die Funktion „SocketCreate()“ initialisiert einen Socket und speichert das Socket-Handle in der Variable „socket“, um die Kommunikation mit dem Python-Server zu ermöglichen.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ if (!SocketConnect(socket, Address, port, 1000)){ Print("Successfully connected to ", Address, ":", port); } else { Print("Connection to ", Address, ":", port, " failed, error ", GetLastError()); return INIT_FAILED; } return(INIT_SUCCEEDED); }
Die Funktion „SocketConnect()“ versucht, den erstellten Socket mit dem Python-Server unter der angegebenen Adresse (lokaler Host) und dem Port 9999 zu verbinden. Unser Timeout in Millisekunden ist 1000.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ uint len=SocketIsReadable(socket); char buffer[16]; int bytes = SocketRead(socket, buffer, len, 23); if (bytes > 0){ string action_str = CharArrayToString(buffer); int action = StringToInteger(action_str); //int action = atoi(buffer); // Execute a trade based on action if(action == 1){ //buy trade MBuy(); Print("Buy action received..."); } else if(action == 2){ //sell trade MSell(); Print("Sell action received..."); } } }
Die Funktion „OnTick()“ in unserem MQL5 wurde entwickelt, um eingehende Handelsanweisungen bei jedem Markttick über eine Socket-Verbindung zu verarbeiten. Zunächst wird mit Hilfe von „SocketIsReadable()“ geprüft, ob Daten auf dem Socket verfügbar sind, was die Länge der Daten zurückgibt. Wenn Daten vorhanden sind, liest die Funktion „SocketRead()“ die Daten in einen Puffer, und die Anzahl der erfolgreich gelesenen Bytes wird in „bytes“ gespeichert. Wenn Daten empfangen wurden, wird der Puffer in eine Zeichenkette und dann in eine Ganzzahl (Aktion) umgewandelt. Basierend auf dem Wert von „action“, führt die Funktion einen entsprechenden Handel aus: wenn „action == 1“, wird ein Kauf durch den Aufruf von „MBuy()“ ausgeführt, und wenn „action == 2“, wird ein Verkauf durch den Aufruf von „MSell()“ ausgelöst. Nach jeder Handelsaktion protokolliert eine Druckanweisung die erhaltene Aktion (Kauf oder Verkauf). Im Wesentlichen lauscht die Funktion auf Kauf- oder Verkaufsbefehle über den Socket und führt die entsprechenden Geschäfte automatisch aus.
//+------------------------------------------------------------------+ //| Buy Function | //+------------------------------------------------------------------+ void MBuy(){ static int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); double Lots = 0.02; double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - stopLoss, digits); double tp = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK) + takeProfit * _Point, digits); trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, Lots, Ask, sl, tp); }
Funktion zur Eröffnung von Kaufgeschäften.
//+------------------------------------------------------------------+ //| Sell Function | //+------------------------------------------------------------------+ void MSell(){ static int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); double Lots = 0.02; double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + stopLoss, digits); double tp = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID) - takeProfit * _Point, digits); trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, Lots, Bid, sl, tp); }
Funktion zur Eröffnung von Verkaufsgeschäften.
Python-Socket-Server-Skript (Handelsmodell-Server)
Unser Python-Skript wird das trainierte Modell laden und einen Socket-Server einrichten, um auf Verbindungen von MQL5 zu warten. Wenn es Daten erhält, erstellt es eine Prognose und sendet die Handelsaktion zurück.
import socket import numpy as np from stable_baselines3 import DQN # Load the trained model model = DQN.load("trading_dqn_model") # Set up the server HOST = '127.0.0.1' # Localhost (you can replace this with your IP) PORT = 9999 # Port to listen on # Create a TCP/IP socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen(1) print(f"Server listening on {HOST}:{PORT}...") while True: # Wait for a connection client_socket, client_address = server_socket.accept() print(f"Connection from {client_address}") # Receive data from MQL5 (price data sent by EA) data = client_socket.recv(1024).decode('utf-8') if data: print(f"Received data: {data}") # Convert received data to a numpy array observation = np.fromstring(data, sep=',') # Assumes comma-separated price data # Make prediction using the model action, _ = model.predict(observation) # Send the predicted action back to MQL5 client_socket.send(str(action).encode('utf-8')) # Close the client connection client_socket.close()
Speichern Sie das Python-Skript unter dem Namen „trading-model-server.py“ oder einem beliebigen Namen Ihrer Wahl. Öffnen Sie Ihr Terminal oder Ihre Eingabeaufforderung, navigieren Sie zu dem Verzeichnis, in dem Sie Ihr Modell und die Datei „trading-model-server.py“ gespeichert haben, und führen Sie den folgenden Befehl aus, um eine Verbindung herzustellen.
python trading_model_server.py
Schlussfolgerung
Zusammenfassend lässt sich sagen, dass wir ein umfassendes Handelssystem entwickelt haben, das maschinelles Lernen mit MQL5 integriert, um Handelsentscheidungen auf der Grundlage historischer Daten zu automatisieren. Wir begannen mit dem Laden und der Vorverarbeitung von XAU/USD-Historiendaten, der Berechnung von Bollinger-Bändern und der Implementierung anderer wichtiger Funktionen wie Preis- und Volumenänderungen. Mit Hilfe von Reinforcement Learning, insbesondere einem Deep Q-Network (DQN), haben wir ein Modell zur Vorhersage von Kauf- und Verkaufsaktionen auf der Grundlage von Mustern in den Daten trainiert. Das trainierte Modell wurde dann über ein Socket-Kommunikationssystem mit MQL5 verbunden, was eine Echtzeit-Interaktion zwischen der Handelsplattform und unserem Python-basierten Entscheidungsmodell ermöglichte. Dies ermöglichte es uns, auf der Grundlage der Vorhersagen des Modells automatisch Trades auszuführen, was das gesamte System zu einem leistungsstarken Werkzeug für den algorithmischen Handel machte.
Diese verbesserte Datenvisualisierung und die Integration von maschinellem Lernen können Händlern erhebliche Vorteile bringen, da sie tiefere Einblicke und fundiertere Entscheidungen ermöglichen. Durch die Analyse von Trends, Volatilität und Schlüsselmustern auf dem Markt kann das System optimale Einstiegs- und Ausstiegspunkte für den Handel ermitteln. Durch die Automatisierung der Handelsausführung auf der Grundlage datengesteuerter Modelle werden menschliche Fehler und emotionale Verzerrungen reduziert, was zu einem konsistenteren und strategischeren Handel führt. Insgesamt gibt dieser Ansatz den Händlern ein ausgeklügeltes Tool an die Hand, das historische Daten nutzt, um die Leistung zu verbessern, und gleichzeitig Zeit spart, indem es sich wiederholende Handelsaufgaben automatisiert.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16083
- 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.