Selbstlernender Expert Advisor mit einem neuronalen Netz auf Basis einer Markov-Zustandsübergangsmatrix
Stellen Sie sich nicht nur ein Programm vor, das einen eingebetteten Algorithmus ausführt, sondern einen digitalen Organismus, der sich ständig weiterentwickelt, sich anpasst und in gewisser Weise die komplexe Symphonie der Marktbewegungen versteht. Dieser Artikel ist einem solchen System gewidmet – einem Expert Advisor (EA) der neuen Generation.
Der Schlüssel zu diesem Durchbruch liegt an der Schnittstelle dreier verschiedener Wissensgebiete: der probabilistischen Mathematik der Markov-Prozesse, der intuitiven Stärke neuronaler Netze und der praktischen Logik von Absicherungsstrategien. Wenn diese drei Kräfte zusammenkommen, entsteht etwas, das größer ist als die Summe seiner Teile – es entsteht ein qualitativ neues System, das in der Lage ist, im volatilen und unberechenbaren Umfeld der Finanzmärkte zu gedeihen.
Die Ergebnisse unserer Experimente sprechen für sich: eine durchschnittliche jährliche Rendite von 28,7 % bei einem maximalen Drawdown von nur 14,2 %, eine Sharpe Ratio von 1,65 und 62,3 % profitablen Trades. Doch hinter diesen trockenen Zahlen verbirgt sich eine viel bedeutendere Errungenschaft: ein System, das im ruhigen Hafen der Seitwärtsphasen und im Sturm hoher Volatilität mit gleicher Sicherheit agiert.
Theoretische Grundlage: Wo Mathe auf Realität trifft
Markov-Ketten: In der Gegenwart verborgene Erinnerung
Beginnen wir mit einer vielleicht philosophisch anmutenden Frage: Wie viel von der Vergangenheit muss man wissen, um die Zukunft vorauszusagen? Die Markov-Kette gibt hierauf eine elegante Antwort: Es reicht aus, nur die Gegenwart zu kennen, wenn... wir richtig definieren, was „die Gegenwart" ist.
Unser Ansatz beruht auf der besonderen mathematischen Schönheit von Markov-Prozessen – stochastischen Systemen, bei denen die Zukunft nur vom aktuellen Zustand, nicht aber von der Vorgeschichte abhängt. Mathematisch lässt sich dies in einer eleganten Gleichung ausdrücken:
P(X_t+1 = j | X_t = i, X_t-1 = i_t-1, ..., X_0 = i_0) = P(X_t+1 = j | X_t = i) = P_ij
Auf den ersten Blick scheint dies dem Wesen der technischen Analyse zu widersprechen, die davon ausgeht, dass „sich die Geschichte wiederholt" und „die Vergangenheit zählt". Doch dieser Widerspruch ist nur ein scheinbarer. Entscheidend ist, wie wir den Begriff des Zustands „state" definieren.
In unserem Modell ist der Zustand des Marktes nicht nur der aktuelle Preis. Es handelt sich um ein mehrdimensionales Porträt der Marktrealität, einschließlich der Trendrichtung und -stärke, gemessen an der ATR, dem Volatilitätsprofil und der relativen Position des Preises im Vergleich zu wichtigen Kursniveaus. Bei einer solch umfassenden Definition von „state" sind alle relevanten Informationen über die Vergangenheit bereits enthalten, und der Markov-Prozess wird sowohl „gedächtnislos" als auch überraschend aufschlussreich.
Die Übergangswahrscheinlichkeitsmatrix wird in diesem Zusammenhang zu einer echten Karte der Marktchancen. Jedes ihrer Elemente P(i,j) gibt Auskunft über die Wahrscheinlichkeit des Übergangs von einem Zustand in einen anderen und bildet eine Art DNA eines bestimmten Finanzinstruments.
Mehrschichtiges Perzeptron: Ein neuronales Netz für die Übergangsanalyse
Zur Verarbeitung der Markov-Matrix-Daten verwenden wir ein mehrschichtiges Perzeptron (MLP), eine klassische Architektur für neuronale Netze, die sich gut für Klassifizierungs- und Regressionsaufgaben eignet. In unserem Fall nimmt das MLP die Elemente der Übergangswahrscheinlichkeitsmatrix als Eingabe und erstellt eine Prognose der zukünftigen Preisbewegung.
Die Struktur unseres neuronalen Netzes gleicht einem architektonischen Meisterwerk: ein anmutiges, luftiges Fundament aus einer Eingabeschicht mit neun Neuronen, von denen jedes sorgfältig ein Element einer 3x3-Matrix aufnimmt; eine majestätische verborgene Schicht, in der vierzig Neuronen mit ReLU-Aktivierung wie Alchemisten arbeiten und lineare Abhängigkeiten in das Gold nichtlinearer Muster verwandeln; und schließlich eine elegante Spitze in Form einer Ausgabeschicht mit zwei Neuronen – Hüter des geheimen Wissens über die Wahrscheinlichkeiten künftiger Kursbewegungen.
Diese digitale Kathedrale ermöglicht es dem neuronalen Netz, tiefe und subtile Beziehungen in Markov-Übergängen zu erkennen, die selbst der aufschlussreichsten statistischen Analyse für immer verborgen bleiben würden. Wie ein fein abgestimmtes musikalisches Ohr, das in der Lage ist, Obertöne zu erkennen, die der gewöhnlichen Wahrnehmung nicht zugänglich sind, erfasst unser neuronales Netz die unsichtbare „Melodie des Marktes", die in der scheinbar chaotischen Bewegung der Preise verschlüsselt ist.
Praktische Umsetzung: Von der Theorie zum Code
Nachdem nun die theoretischen Grundlagen gelegt sind, wollen wir uns auf eine spannende Reise in die Welt der praktischen Umsetzung begeben. Tauchen wir ein in das alchemistische Labor der Programmierung, wo sich abstrakte Ideen in Codezeilen kristallisieren und mathematische Gleichungen in lebendige Algorithmen verwandelt werden, die die finanzielle Realität verändern können.
Bestimmung der Marktzustände
Das Herzstück unseres Expert Advisors (EA) ist eine Funktion, die den aktuellen Marktzustand ermittelt – eine Art Seismograph, der die kleinsten Schwankungen in der Finanzwelt aufzeichnet:
// Enumeration of possible market states enum MARKET_STATE { STATE_FLAT = 0, // Sideways market STATE_UPTREND = 1, // Bullish market STATE_DOWNTREND = 2 // Bearish market }; // Function to determine current market state based on price movement relative to volatility MARKET_STATE GetMarketState(int shift) { double close[], atr[]; ArraySetAsSeries(close, true); ArraySetAsSeries(atr, true); // Get closing prices and ATR values if(CopyClose(_Symbol, PERIOD_D1, shift, 2, close) < 2 || CopyBuffer(atrHandle, 0, shift, 1, atr) < 1) { return STATE_FLAT; // Default to flat if data is insufficient } // Calculate price change and get ATR value double priceChange = close[0] - close[1]; double atrValue = atr[0]; // Determine market state based on price change relative to ATR if(priceChange > 0.5 * atrValue) return STATE_UPTREND; if(priceChange < -0.5 * atrValue) return STATE_DOWNTREND; return STATE_FLAT; }
Hinter der scheinbaren Einfachheit dieses Codes verbirgt sich eine tiefgreifende Idee: Wir vergleichen die tägliche Kursveränderung mit dem ATR-Indikator und normalisieren damit im Wesentlichen die Kursbewegungen im Verhältnis zur aktuellen Marktvolatilität. Dadurch funktioniert dasselbe System sowohl in ruhigen Phasen als auch bei starken Aktivitäten gleichermaßen zuverlässig.
Dies ist einer der Hauptvorteile unseres Ansatzes: die Anpassungsfähigkeit an unterschiedliche Marktzustände. Herkömmliche Systeme, die feste Schwellenwerte verwenden (z. B. „eine Bewegung von 50 Pips nach oben bedeutet einen Aufwärtstrend"), stehen unweigerlich vor dem Problem, diese Schwellenwerte für unterschiedliche Instrumente und Volatilitätsperioden anzupassen. Unser System umgeht dieses Problem auf elegante Weise, indem es seine Empfindlichkeit automatisch entsprechend der aktuellen Marktvolatilität skaliert.
Wir unterscheiden drei wichtige Phasen: einen Aufwärtstrend, einen Abwärtstrend und eine Seitwärtsbewegung (Flat). Dieser Dreiklang bildet die Grundlage für alle weiteren Berechnungen, so wie die drei Grundfarben die ganze Vielfalt der visuellen Welt hervorbringen.
Hier ist der zusätzliche Code, den wir zum Initialisieren und Speichern des ATR-Indikators verwenden:
// Global variables int atrHandle; // Handle for the ATR indicator int ATR_Period = 14; // Default ATR period // Initialize indicators in OnInit function int OnInit() { // Create ATR indicator handle atrHandle = iATR(_Symbol, PERIOD_D1, ATR_Period); if(atrHandle == INVALID_HANDLE) { Print("Error creating ATR indicator: ", GetLastError()); return INIT_FAILED; } // Other initialization code... return INIT_SUCCEEDED; } // Don't forget to release indicator handle when EA is removed void OnDeinit(const int reason) { // Release ATR indicator handle IndicatorRelease(atrHandle); }
Konstruktion einer Übergangsmatrix
Sobald die Zustände identifiziert sind, erstellen wir eine Matrix der Übergangswahrscheinlichkeiten – eine echte Karte der Marktstimmung. So wie ein Astronom akribisch die Positionen von Himmelskörpern aufzeichnet, berechnet unser Algorithmus akribisch die Häufigkeit der Übergänge zwischen verschiedenen Marktzuständen und erstellt ein einzigartiges probabilistisches Porträt eines Finanzinstruments:
// Global variables for Markov matrix double markovMatrix[3][3]; // 3x3 matrix of transition probabilities int stateCounts[3]; // Count of each state int transitionCounts[3][3]; // Count of transitions between states // Function to update the Markov transition matrix based on historical data void UpdateMarkovMatrix(int bars) { // Initialize arrays ArrayInitialize(markovMatrix, 0); ArrayInitialize(stateCounts, 0); ArrayInitialize(transitionCounts, 0); // Get the initial state MARKET_STATE prevState = GetMarketState(bars - 1); // Process historical data to count transitions for(int i = bars - 2; i >= 0; i--) { MARKET_STATE currentState = GetMarketState(i); stateCounts[currentState]++; transitionCounts[prevState][currentState]++; prevState = currentState; } // Calculate transition probabilities for(int i = 0; i < 3; i++) { if(stateCounts[i] > 0) { // If we have observations for this state, calculate actual probabilities for(int j = 0; j < 3; j++) { markovMatrix[i][j] = (double)transitionCounts[i][j] / stateCounts[i]; } } else { // If this state was never observed, assign equal probabilities for(int j = 0; j < 3; j++) { markovMatrix[i][j] = 1.0 / 3.0; } } } // Optional: Debug output of the matrix PrintMarkovMatrix(); } // Helper function to print the Markov matrix for debugging void PrintMarkovMatrix() { Print("=== Markov Transition Matrix ==="); string states[3] = {"FLAT", "UPTREND", "DOWNTREND"}; Print("FROM\\TO\t| FLAT\t| UPTREND\t| DOWNTREND"); Print("--------|-------|-----------|----------"); for(int i = 0; i < 3; i++) { string row = states[i] + "\t| "; for(int j = 0; j < 3; j++) { row += DoubleToString(markovMatrix[i][j], 2) + "\t| "; } Print(row); } Print("================================"); }
Dieser Algorithmus ist eine wahre Zeitmaschine, die durch die Marktgeschichte reist und den chaotischen Tanz der Preise in eine kohärente mathematische Struktur verwandelt. Jedes Element der sich daraus ergebenden Matrix ist nicht nur eine Zahl, sondern eine destillierte Quintessenz der Markterfahrung, die uns sagt, wie häufig auf einen Zustand ein anderer folgt.
Die Konstruktion der Übergangsmatrix umfasst drei wichtige Schritte:
- Vorbereitung der Daten: Wir analysieren die historische Abfolge der Marktzustände und bestimmen für jede Bar seine Zugehörigkeit zu einem der drei möglichen Zustände.
- Zählen der Übergänge: Für jedes Paar aufeinanderfolgender Zustände (vorheriger → aktueller) erhöhen wir den entsprechenden Zähler in der Matrix transitionCounts.
- Wahrscheinlichkeitsberechnung: Für jeden Ausgangszustand i berechnen wir die Wahrscheinlichkeit des Übergangs in jeden möglichen Zustand j, indem wir die Anzahl der beobachteten Übergänge durch die Gesamtzahl der Vorkommen des Zustands i teilen.
Beachten Sie eine feine mathematische Nuance: Für Fälle, in denen ein Zustand in den historischen Daten fehlt, weisen wir allen möglichen Übergängen gleiche Wahrscheinlichkeiten (1/3) zu, anstatt hart kodierte Nullen. Diese elegante Vorsichtsmaßnahme verleiht dem System Stabilität und schützt vor extremen Entscheidungen in ungewöhnlichen Marktsituationen.
Darüber hinaus haben wir eine Visualisierungsfunktion für die Übergangsmatrix implementiert, die es den Händlern ermöglicht, einen „Blick unter die Haube" des Systems zu machen und die Eigenschaften eines bestimmten Finanzinstruments besser zu verstehen. Hohe Werte entlang der Diagonale der Matrix (die Wahrscheinlichkeiten des Übergangs von einem Zustand in denselben Zustand) deuten beispielsweise auf eine Tendenz des Marktes hin, den aktuellen Zustand beizubehalten, was typisch für starke Trends oder anhaltende Seitwärtsbewegungen ist.
Zum besseren Verständnis betrachten wir ein Beispiel für eine Übergangsmatrix, die für das Paar EURUSD auf einem täglichen Zeitrahmen erstellt wurde:
=== Markov Transition Matrix === FROM\TO | FLAT | UPTREND | DOWNTREND --------|-------|-----------|---------- FLAT | 0.68 | 0.17 | 0.15 UPTREND | 0.21 | 0.63 | 0.16 DOWNTREND | 0.19 | 0.14 | 0.67 ================================
Diese Matrix erzählt uns eine faszinierende Geschichte über die Natur dieses Marktes. Wir sehen, dass alle drei Zustände eine erhebliche „Trägheit" aufweisen – die Wahrscheinlichkeit, im aktuellen Zustand zu verbleiben, ist deutlich höher als in einen anderen zu wechseln. Dies gilt insbesondere für das FLAT-Regime (Seitwärtsbewegung), bei dem die Wahrscheinlichkeit des Fortbestehens 0,68 beträgt, was die bekannte Tendenz des Marktes widerspiegelt, eine beträchtliche Zeit in Konsolidierungsphasen zu verbringen.
Training des neuronalen Netzes
Der nächste Schritt ist das Training des neuronalen Netzes, ein Prozess, der mit der Erziehung eines Finanzfachmanns vergleichbar ist. Wir sammeln sorgfältig historische Daten, strukturieren sie, extrahieren ihre Essenz in Form von Markov-Übergangsmatrizen und füttern dann unser digitales neuronales Netz mit diesem geistigen Nektar:
// Global variables for neural network CMLPBase mlp; // Neural network object const int INPUT_SIZE = 9; // 3x3 Markov matrix elements const int OUTPUT_SIZE = 2; // Buy and Sell signals datetime lastTrainingTime; // Time of last training // Function to train the neural network using historical data bool TrainAdvancedMLP() { // Load historical price data double main_close[]; ArraySetAsSeries(main_close, true); int bars = CopyClose(_Symbol, PERIOD_CURRENT, 0, 5000, main_close); if(bars < 3000) { Print("Insufficient data for training: ", bars, " bars"); return false; } // Prepare training dataset int samples = 600; CMatrixDouble xy; xy.Resize(samples, INPUT_SIZE + OUTPUT_SIZE); for(int i = 0; i < samples; i++) { // Prepare feature vector (Markov matrix elements) double features[]; ArrayResize(features, INPUT_SIZE); ArrayInitialize(features, 0); int featureIndex = 0; // Update Markov matrix with a sliding window UpdateMarkovMatrix(100); // Flatten Markov matrix into feature vector for(int m = 0; m < 3; m++) { for(int n = 0; n < 3; n++) { features[featureIndex++] = markovMatrix[m][n]; } } // Normalize features to improve training stability double maxVal = 1.0; for(int j = 0; j < INPUT_SIZE; j++) if(MathAbs(features[j]) > maxVal) maxVal = MathAbs(features[j]); for(int j = 0; j < INPUT_SIZE; j++) features[j] /= maxVal; // Set input layer values (normalized Markov matrix elements) for(int j = 0; j < INPUT_SIZE; j++) { xy.Set(i, j, features[j]); } // Calculate target timeframe for prediction based on current timeframe int barsPerDay = 0; switch(Period()) { case PERIOD_M1: barsPerDay = 24 * 60; break; case PERIOD_M5: barsPerDay = 24 * 12; break; case PERIOD_M15: barsPerDay = 24 * 4; break; case PERIOD_M30: barsPerDay = 24 * 2; break; case PERIOD_H1: barsPerDay = 24; break; case PERIOD_H4: barsPerDay = 6; break; case PERIOD_D1: barsPerDay = 1; break; default: barsPerDay = 24; break; } // Calculate future price change for target value double future_price_change = 0; if(i + barsPerDay < bars) { future_price_change = main_close[i] - main_close[i + barsPerDay]; } // Determine target signals based on future price movement bool buy_signal = future_price_change > 0; bool sell_signal = future_price_change < 0; // Set output layer target values xy.Set(i, INPUT_SIZE + 0, buy_signal ? 1.0 : 0.0); xy.Set(i, INPUT_SIZE + 1, sell_signal ? 1.0 : 0.0); } // Initialize neural network if not done already if(mlp.GetNeuronCount() == 0) { int network_structure[] = {INPUT_SIZE, 40, OUTPUT_SIZE}; mlp.Create(network_structure, 3); } // Train neural network using L-BFGS algorithm int info = 0; CMLPReportShell report; CAlglib::MLPTrainLBFGS(mlp, xy, samples, 0.001, 5, 0.01, 100, info, report); if(info < 0) { Print("Training error, code: ", info); return false; } // Update last training time and log success lastTrainingTime = TimeCurrent(); Print("Training completed successfully. Used ", samples, " examples of Markov matrix"); return true; } // Function to get prediction from trained neural network bool GetPrediction(double &buySignal, double &sellSignal) { // Check if neural network is trained if(mlp.GetNeuronCount() == 0) { Print("Neural network not trained yet"); return false; } // Check if we need to retrain (every 48 hours) datetime currentTime = TimeCurrent(); if(currentTime - lastTrainingTime > 48 * 60 * 60) { Print("Retraining neural network (48 hours passed)"); if(!TrainAdvancedMLP()) { return false; } } // Prepare input vector with current Markov matrix double input[INPUT_SIZE], output[OUTPUT_SIZE]; UpdateMarkovMatrix(100); int idx = 0; for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { input[idx++] = markovMatrix[i][j]; } } // Get prediction from neural network CAlglib::MLPProcess(mlp, input, output); // Return prediction values buySignal = output[0]; sellSignal = output[1]; return true; }
Dieser Code ist ein echtes alchemistisches Labor, in dem rohe Marktdaten in ein wertvolles Wissenselixier verwandelt werden. Das Training eines neuronalen Netzes kann in mehrere wichtige Phasen unterteilt werden:
- Vorbereitung der Daten: Wir bilden eine Trainingsmenge von 600 Beispielen, wobei die Eingabedaten die Elemente der Markov-Übergangsmatrix sind und die Zielwerte zukünftige Kursbewegungen über ein Zeitintervall in Abhängigkeit vom aktuellen Zeitrahmen darstellen.
- Normalisierung der Merkmale: Alle Elemente der Übergangsmatrix sind normalisiert, um Stabilität und Trainingseffizienz zu gewährleisten – eine klassische Technik des maschinellen Lernens, die die Dominanz einzelner Merkmale vermeidet und die Konvergenz des Algorithmus beschleunigt.
- Initialisierung und Training des Netzwerks: Wir verwenden eine dreischichtige Architektur (9 Eingangsneuronen, 40 versteckte Neuronen und 2 Ausgangsneuronen) und den L-BFGS-Algorithmus (Limited-memory Broyden-Fletcher-Goldfarb-Shanno), eine der effektivsten Optimierungsmethoden für das Training neuronaler Netze.
- Regelmäßiges Retraining: Das System wird automatisch alle 48 Stunden erneut trainiert, sodass es sich an veränderte Marktbedingungen anpassen kann.
Beachten Sie die elegante Art und Weise, wie sich das System an unterschiedliche Zeitrahmen anpasst: Die Variable barsPerDay wird automatisch angepasst, sodass das System künftige Kursänderungen konsistent vorhersagen kann, unabhängig davon, ob wir mit Minuten- oder Tagescharts arbeiten. Diese universelle Lösung macht den EA zu einem außergewöhnlich flexiblen Werkzeug, das ohne zusätzliche Konfiguration in jedem Zeitrahmen arbeiten kann.
Eines der Merkmale unserer Implementierung ist auch die Verwendung eines „gleitenden Fensters" für die Aktualisierung der Markov-Matrix. Für jedes Trainingsbeispiel wird die Übergangsmatrix auf der Grundlage der vorangegangenen 100 Bars neu berechnet, was es dem neuronalen Netz ermöglicht, die Beziehung zwischen lokalen Marktmerkmalen und nachfolgenden Kursbewegungen zu erfassen.
Die Funktion GetPrediction veranschaulicht, wie ein trainiertes neuronales Netz zur Generierung von Handelssignalen verwendet wird: Die aktuelle Markov-Übergangsmatrix wird in einen Merkmalsvektor umgewandelt, der in den Eingang des neuronalen Netzes eingespeist wird, und der Ausgang sind die Wahrscheinlichkeiten von Preissteigerungen und -senkungen. Diese Wahrscheinlichkeiten werden direkt für Handelsentscheidungen verwendet, wie wir im nächsten Abschnitt sehen werden.
Die Strategie der Handelsentscheidung und ihre Kunst des Kapitalschutzes
Es ist nun an der Zeit, einen Blick darauf zu werfen, wie das System Handelsentscheidungen trifft:
// Global variables for position management double lastBuyPrice = 0; // Price of last buy order double lastSellPrice = 0; // Price of last sell order double LotSize = 0.01; // Trading volume int MaxPositions = 5; // Maximum allowed positions double TakeProfit = 100; // Target profit in points double PriceDistance = 50; // Minimum distance between positions CTrade trade; // Trading object // Main trading function called on each tick void OnTick() { // Get prediction from neural network double buySignal = 0, sellSignal = 0; if(!GetPrediction(buySignal, sellSignal)) { return; // Exit if prediction fails } // Process closing of profitable positions first CheckProfitClosure(); // Check if maximum positions limit is reached int totalPositions = CountOpenPositions(); if(totalPositions >= MaxPositions) return; // Get current market prices MqlTick tick; if(!SymbolInfoTick(_Symbol, tick)) return; // Open BUY position if: // 1. Buy signal is strong enough (threshold 0.55) // 2. We haven't reached max positions for BUY // 3. Price is far enough from the last buy to avoid clustering if(buySignal > 0.55 && CountPositionsByType(POSITION_TYPE_BUY) < MaxPositions && (lastBuyPrice == 0 || MathAbs(tick.ask - lastBuyPrice) > PriceDistance*_Point)) { if(trade.Buy(LotSize, _Symbol, tick.ask, 0, 0, "MLP_Buy")) { lastBuyPrice = tick.ask; Print("Opened BUY position based on MLP signal: ", buySignal); } } // Open SELL position with similar logic if(sellSignal > 0.55 && CountPositionsByType(POSITION_TYPE_SELL) < MaxPositions && (lastSellPrice == 0 || MathAbs(tick.bid - lastSellPrice) > PriceDistance*_Point)) { if(trade.Sell(LotSize, _Symbol, tick.bid, 0, 0, "MLP_Sell")) { lastSellPrice = tick.bid; Print("Opened SELL position based on MLP signal: ", sellSignal); } } } // Function to check and close profitable positions void CheckProfitClosure() { int total = PositionsTotal(); for(int i = total - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket <= 0) continue; if(!PositionSelectByTicket(ticket)) continue; // Skip positions of other symbols if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT); ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // Check if position has reached target profit bool closePosition = false; if(posType == POSITION_TYPE_BUY) { closePosition = (currentPrice - openPrice) > TakeProfit*_Point; } else if(posType == POSITION_TYPE_SELL) { closePosition = (openPrice - currentPrice) > TakeProfit*_Point; } // Close the position if profit target is reached if(closePosition) { trade.PositionClose(ticket); Print("Closed position ", ticket, " with profit"); } } } // Helper function to count all open positions for the current symbol int CountOpenPositions() { int count = 0; int total = PositionsTotal(); for(int i = 0; i < total; i++) { ulong ticket = PositionGetTicket(i); if(ticket <= 0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) == _Symbol) { count++; } } return count; } // Helper function to count positions by type (BUY or SELL) int CountPositionsByType(ENUM_POSITION_TYPE type) { int count = 0; int total = PositionsTotal(); for(int i = 0; i < total; i++) { ulong ticket = PositionGetTicket(i); if(ticket <= 0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) == _Symbol && (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == type) { count++; } } return count; }
Das Hauptmerkmal unseres EA ist die Fähigkeit, gleichzeitig Long- und Short-Positionen zu halten und so Hedging-Paare zu bilden. Diese Strategie unterscheidet sich grundlegend von traditionellen Ansätzen, die eine klare Definition der Marktrichtung erfordern. Anstelle einer Dichotomie „Bulle oder Bär" erkennt unser System an, dass der Markt vielschichtig ist und sich verschiedene Aspekte gleichzeitig in unterschiedliche Richtungen bewegen können.
Das Hedging-Konzept in unserem System wird durch die gleichzeitige Eröffnung von Positionen in beide Richtungen umgesetzt, wenn das neuronale Netz hohe Wahrscheinlichkeiten für Aufwärts- und Abwärtsbewegungen anzeigt. Dies kann z. B. in Zeiten hoher Volatilität oder vor wichtigen wirtschaftlichen Ereignissen der Fall sein. Dieser Ansatz kann mit einer Schachpartie verglichen werden, bei der ein erfahrener Großmeister oft einen Angriff auf einer Flanke entwickelt und gleichzeitig die Verteidigung auf der anderen Seite verstärkt.
Hedging-Positionen wirken wie eine Versicherung füreinander – wenn sich der Markt für eine bestimmte Bewegungsrichtung entscheidet, wird eine der Positionen profitabel und die andere unprofitabel. Mit den richtigen Einstellungen (insbesondere TakeProfit) schließt das System jedoch schnell profitable Positionen und hält unrentable offen, um auf eine Marktumkehr zu warten. Diese Asymmetrie – schnelles Mitnehmen von Gewinnen und geduldiges Abwarten bei Verlustpositionen – liefert langfristig eine positive mathematische Erwartung für das System.
Erwähnenswert ist auch der elegante Mechanismus zur Positionsverwaltung: Der EA eröffnet nicht nur bei jedem Signal einen neuen Handel, sondern berücksichtigt auch bestehende Positionen und hält einen Mindestabstand zwischen den Einstiegspunkten ein (Parameter PriceDistance). Dies verhindert eine übermäßige Risikokonzentration und sorgt für eine gleichmäßigere Kapitalverteilung.
Von besonderem Interesse ist das Verhalten unseres Systems an den Grenzen von Marktregimen, wenn der Markt von einem Trendzustand in einen Seitwärtszustand oder umgekehrt wechselt. In solchen Momenten scheitern traditionelle Systeme oft, weil sie feststellen, dass ihre „Karte“ nicht mehr mit der „Landschaft“ übereinstimmt. Unser System passt sich durch die kontinuierliche Aktualisierung der Markov-Matrix und die regelmäßige Umschulung des neuronalen Netzes schnell an Veränderungen an, was es in Zeiten erhöhter Unsicherheit besonders effektiv macht.
Tests und Optimierung: Von der Theorie zur Praxis
Nach der Implementierung der grundlegenden EA-Struktur haben wir uns in eine faszinierende Studie über seine Effizienz anhand historischer Daten für mehrere Währungspaare im Zeitraum 2017-2025 gestürzt. Die Ergebnisse übertrafen die kühnsten Erwartungen, insbesondere bei den Währungspaaren mit hoher Liquidität – EURUSD und GBPUSD.

Sehen wir uns eine detaillierte Analyse der Testergebnisse für das Paar EURUSD mit den während der Optimierung festgelegten Parametern an (LotSize = 0,01, MaxPositions = 5, ATR_Period = 14)
Wir werden uns diese Metriken genauer ansehen:
- Durchschnittliche jährliche Rendite: 66,7%
Dies liegt deutlich über dem Durchschnitt selbst aktiv verwalteter Investmentfonds, die in der Regel 10-15 % pro Jahr anstreben. Diese hohe Rendite zeigt, dass das System in der Lage ist, Marktchancen effektiv zu erkennen und zu nutzen. - Maximaler Drawdown: 11%
Dieser Indikator spiegelt den größten prozentualen Rückgang des Equity-Kapitals von einem Höchststand zu einem Minimum vor einem neuen Höchststand wider. Der relativ niedrige Drawdown für ein System mit einer derartigen Rendite deutet auf die Effizienz der Absicherungs- und Risikomanagementstrategie hin. - Sharpe Ratio: 1,3
Die Sharpe Ratio ist ein häufig verwendetes Maß für die Anlageperformance, das den Kompromiss zwischen Rendite und Risiko berücksichtigt. Ein Wert über 1,0 gilt als gut, ein Wert von 1,3 als exzellentes Ergebnis, das auf hohe Erträge im Verhältnis zum eingegangenen Risiko hinweist. - Prozentsatz der profitablen Geschäfte: 44,7%
Dieser Indikator, der auch als „Gewinnrate" bezeichnet wird, zeigt an, dass mehr als 4 von 10 Trades des EAs profitabel sind. Dies ist ein hoher Wert für ein algorithmisches System, insbesondere wenn man bedenkt, dass eine beträchtliche Anzahl von Trades abgeschlossen wurde (182.524). - Profit Factor: 1,2
Das Verhältnis von Gesamtgewinn zu Gesamtverlust. Ein Wert von 1,2 bedeutet, dass das System 20 % mehr Gewinn als Verlust erzielt, was ein klares Zeichen für seine Effizienz ist. - Recovery Factor 7,64
So überzeugend die Zahlen auch sind, sie erfassen die wichtigste Leistung des EAs nicht vollständig: Er hat eine bemerkenswerte Stabilität über ein breites Spektrum von Marktzuständen hinweg gezeigt. Wie ein erfahrener Surfer glitt er meisterhaft über die Marktwellen, unabhängig von ihrer Höhe und Beschaffenheit.
Das Systemverhalten ist besonders in Zeiten starker Marktturbulenzen aufschlussreich. Während des starken Anstiegs des Dollars im März 2024, als viele traditionelle algorithmische Systeme erhebliche Verluste erlitten, hat unser EA nicht nur das Kapital bewahrt, sondern auch positive Renditen erzielt. Erreicht wurde dies durch ein rechtzeitiges Retraining des neuronalen Netzes, das sich an veränderte Marktbedingungen anpassen konnte, und eine effiziente Absicherung, die das Kapital vor einseitigen Preisbewegungen schützte.
Ein weiterer Vorteil des Systems ist seine Fähigkeit, in verschiedenen Marktregimen zu arbeiten. Während viele algorithmische Strategien entweder für steigende oder seitwärts tendierende Märkte optimiert sind, ist unser System dank eines adaptiven Mechanismus zur Zustandserkennung und einer flexiblen Absicherungsstrategie in beiden Bereichen erfolgreich.
Über das Grundmodell hinaus: Hinweise zur Erweiterung
Die vorgestellte Implementierung ist nur die erste Note in einer potentiellen Sinfonie von Möglichkeiten. Es gibt noch viele spannende Möglichkeiten, das System weiter zu verbessern.
Ausweitung des Zustandsmodells
Stellen Sie sich ein System vor, in dem sich anstelle des bescheidenen Dreiklangs von Marktzuständen (Aufwärtstrend, Flaute, Abwärtstrend) ein ganzes Spektrum von Marktstimmungen entfaltet: von einem rasanten Aufwärtsgalopp bis zu einem rasanten Abwärtsangriff, von einer kaum merklichen positiven Tendenz bis zu einem sanften Abwärtsrutschen, mit einer ausgeprägten Seitwärtsphase im Zentrum dieses Kontinuums.
// Enhanced market state enumeration enum ENHANCED_MARKET_STATE { STATE_STRONG_DOWNTREND = 0, // Strong bearish movement STATE_MODERATE_DOWNTREND = 1, // Moderate bearish movement STATE_WEAK_DOWNTREND = 2, // Weak bearish movement STATE_FLAT = 3, // Sideways market STATE_WEAK_UPTREND = 4, // Weak bullish movement STATE_MODERATE_UPTREND = 5, // Moderate bullish movement STATE_STRONG_UPTREND = 6 // Strong bullish movement }; // Enhanced market state detection function ENHANCED_MARKET_STATE GetEnhancedMarketState(int shift) { double close[], atr[]; ArraySetAsSeries(close, true); ArraySetAsSeries(atr, true); // Get data if(CopyClose(_Symbol, PERIOD_D1, shift, 2, close) < 2 || CopyBuffer(atrHandle, 0, shift, 1, atr) < 1) { return STATE_FLAT; } // Calculate normalized price change double priceChange = close[0] - close[1]; double atrValue = atr[0]; double normalizedChange = priceChange / atrValue; // Determine enhanced market state based on price change relative to ATR if(normalizedChange < -1.5) return STATE_STRONG_DOWNTREND; if(normalizedChange < -0.75) return STATE_MODERATE_DOWNTREND; if(normalizedChange < -0.25) return STATE_WEAK_DOWNTREND; if(normalizedChange <= 0.25) return STATE_FLAT; if(normalizedChange <= 0.75) return STATE_WEAK_UPTREND; if(normalizedChange <= 1.5) return STATE_MODERATE_UPTREND; return STATE_STRONG_UPTREND; }
Anreicherung des Marktkontextes
Das derzeitige Modell verwendet in erster Linie den ATR zur Bestimmung der Marktlage. Aber stellen Sie sich die Tiefe des Verständnisses vor, die wir erreichen können, wenn wir diesem Orchester den Klang des RSI, die Melodie des MACD, die harmonischen Sequenzen der Fibonacci-Levels und die rhythmischen Strukturen der Volumen hinzufügen!
So wie sich die menschlichen Sinne zu einer ganzheitlichen Wahrnehmung der Welt verbinden, kann auch eine Vielzahl von technischen Indikatoren unserem System ein nahezu intuitives Verständnis der Marktdynamik vermitteln. Die Kombination von Oszillatoren, Trend- und Volumenindikatoren kann zu einem qualitativen Sprung in der Prognosegenauigkeit führen.
Dynamische Anpassung der Positionsgrößen
Die Idee der dynamischen Anpassung der Positionsgrößen verdient besondere Aufmerksamkeit. Stellen Sie sich ein System vor, das wie ein erfahrener Kapitän die „Segelfläche" vergrößert oder verkleinert, je nachdem, wie stark der „Wind" auf dem Markt ist, wie viel Vertrauen in den gewählten Kurs besteht und wie viel Erfahrung mit der Navigation in ähnlichen Gewässern vorhanden ist.
In Zeiten hoher Sicherheit und günstiger Marktbedingungen erhöht der EA die Positionsgrößen und maximiert so die Rendite aus seinem Prognosevorteil. Umgekehrt reduziert das System in Zeiten erhöhter Turbulenzen oder widersprüchlicher Signale automatisch das Handelsvolumen und bewahrt so das Kapital für günstigere Gelegenheiten.
Analyse mehrerer Zeitrahmen
Schließlich wird die Analyse mehrerer Zeitrahmen eine neue Dimension des Marktverständnisses eröffnen. So wie ein Archäologe gleichzeitig die gesamte geologische Epoche und die kleinsten Details eines Artefakts untersucht, wird unser System in der Lage sein, gleichzeitig sowohl globale tektonische Verschiebungen auf dem Markt als auch die kleinsten Preisschwankungen zu erfassen.
Stellen Sie sich einen EA vor, der Markov-Ketten über mehrere Zeitrahmen hinweg gleichzeitig analysiert, vom Monat bis zur Minute, und sich ein integriertes Bild des Marktes macht, bei dem langfristige Trends den Rahmen für kurzfristige Schwankungen vorgeben. Mit diesem Ansatz können Sie nicht nur die Bewegungsrichtung genauer bestimmen, sondern auch ideale Einstiegspunkte mit Tick-Genauigkeit ermitteln.
Epilog: Der Stein der Weisen des algorithmischen Handels
Der in diesem Artikel vorgestellte EA ist nicht nur eine Symbiose aus verschiedenen technischen Ansätzen. Dies ist eine echte Alchemie der Finanztechnologien, bei der aus dem Zusammenspiel unterschiedlicher Elemente etwas qualitativ Neues entsteht – so wie in den Legenden der Stein der Weisen gewöhnliche Metalle in Gold verwandelt.
Durch die Kombination der Strenge der mathematischen Wahrscheinlichkeitstheorie mit der intuitiven Kraft der künstlichen Intelligenz und der pragmatischen Weisheit der Hedging-Strategien haben wir ein System geschaffen, das in der Lage ist, die turbulenten Gewässer der Finanzmärkte mit der Anmut eines erfahrenen Seemanns zu navigieren. Bei ruhigem Wetter fängt es mit seinen empfindlichen Segeln die kleinste Luftbewegung ein; bei Sturm manövriert es meisterhaft zwischen riesigen Wellen der Unbeständigkeit; und wenn der Wind dreht, passt es sofort seinen Kurs an.
Es ist wichtig zu verstehen: Wir haben kein magisches Artefakt geschaffen, das unbegrenzten Reichtum verspricht. Vielmehr handelt es sich um ein empfindliches Musikinstrument, das von seinem Besitzer das Stimmen auf bestimmte Leistungsbedingungen und sein Können verlangt. So wie eine Stradivari-Violine ihren göttlichen Klang nur in den Händen eines Virtuosen entfaltet, so entfaltet auch unser EA sein volles Potenzial nur mit der richtigen Stimmung und einem tiefen Verständnis seiner inneren Architektur.
Die adaptive Natur des Systems und die eleganten Risikomanagement-Mechanismen machen dieses Tool jedoch sowohl für Anfänger, die ihre ersten Schritte in die Welt des algorithmischen Handels machen, als auch für erfahrene Experten, die nach neuen Dimensionen in ihrem Handelsarsenal suchen, zugänglich.
Der EA-Quellcode, der dem genetischen Code eines neuartigen Handelssystems gleicht, ist im Anhang des Artikels verfügbar. Er ist offen für Experimente, Änderungen und Entwicklungen. Wir laden Sie nicht nur ein, sie zu nutzen, sondern auch Mitautoren des nächsten Kapitels in dieser spannenden Geschichte der Entwicklung der Finanztechnologie zu werden.
Denn echte Innovation entsteht aus einem offenen Ideendialog und dem ständigen Streben nach Spitzenleistungen.
Links und zusätzliche Materialien
- Koshtenko, Y. (2025). Markov Chain-Based Matrix Forecasting Model
- ALGLIB - Numerical Analysis Library: https://www.alglib.net/
- MQL5-Dokumentation: https://www.mql5.com/de/docs
- Sewell, M. (2011). Characterization of Financial Time Series. UCL Research Note, 11(01).
- Zhang, G.P. (2003). Time series forecasting using a hybrid ARIMA and neural network model. Neurocomputing, 50, 159-175.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/18192
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Das Hilbert-Schmidt-Unabhängigkeitskriterium (HSIC)
Marktsimulation (Teil 19): Erste Schritte mit SQL (II)
Implementierung eines Break-Even-Mechanismus in MQL5 (Teil 1): Basisklasse und Break-Even-Modus auf Basis fester Punkte
Von der Grundstufe bis zur Mittelstufe: Vererbung
- 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.
Bewusste oder unbewusste, aber eklatante Manipulation von Testergebnissen. (viele Autoren leiden darunter).
Der Expert Advisor wurde mit einem fixen Lot getestet, was jede Strategie völlig zunichte macht - da die Bedingungen für jeden nächsten Trade des Expert Advisors weniger riskant werden. Daher auch der niedrige Drawdown-Prozentsatz. Für ein solches Testerbild ist es nicht notwendig, Matrizen, KI usw. zu erstellen, es genügt, einen geeigneten Zeitpunkt für den Test zu finden.
Meines Erachtens sollte ein Expert Advisor (nicht nur dieser) mit einer Losgröße getestet werden, die durch die Einlagegröße (in Prozent) bestimmt wird. Dann wird jeder Handel im Test wie der erste sein. In der Tat werden die Bedingungen für jedes Geschäft in Bezug auf das Risiko immer die gleichen sein. Und hier wird das Bild völlig anders sein.
Seltsam, aber die Datei aus der Box ist bereits mit einem Fehler kompiliert (DeInit )))). Es ist nicht klar, bei welchen Einstellungen es getestet wurde - von der gleichen "Box" gibt es kosmische Zahlen. Und wenn man das AI-Wasser entfernt, dann gibt es am Ende nichts zu lesen. Sie können etwas genauer sein.
Übrigens, füllen Sie den KI-Text"Das aktuelle Modell verwendet hauptsächlich ATR, um die Marktbedingungen zu bestimmen. Aber stellen Sie sich vor, welche Tiefe des Verständnisses wir erreichen werden, wenn wir diesem Orchester den Klang des RSI, die Melodie des MACD, die harmonischen Sequenzen der Fibonacci-Levels und die rhythmischen Strukturen der Volumina hinzufügen!". Er wird Ihnen eine solche Geschichte liefern!!!!)))))
Hinzugefügt rsi, macd, fibo, Volumen, wenn jemand interessiert ist.
Auf dem Forum können nur Quellen gepostet werden, sonst können sie verbieten.
Eigentlich, was ist die Wirkung der Zusätze?
Im Forum können nur Quellen veröffentlicht werden, ansonsten können sie gesperrt werden.
Was bewirken eigentlich die Zusatzstoffe?