English Русский
preview
Entwicklung eines dynamischen Multi-Pair-EA (Teil 6): Adaptive Spread-Sensitivität für hochfrequente Symbolwechsel

Entwicklung eines dynamischen Multi-Pair-EA (Teil 6): Adaptive Spread-Sensitivität für hochfrequente Symbolwechsel

MetaTrader 5Beispiele |
45 0
Hlomohang John Borotho
Hlomohang John Borotho
Inhaltsverzeichnis
  1. Einführung
  2. Systemübersicht und strategischer Ansatz
  3. Die ersten Schritte
  4. Backtest-Ergebnisse
  5. Schlussfolgerung


Einführung

In Teil 5 unserer Dynamic Multi-Pair EA-Serie haben wir uns mit der Herausforderung beschäftigt, den richtigen Handelsstil für unterschiedliche Marktbedingungen zu wählen, insbesondere mit der Entwicklung eines Systems, das zwischen Scalping- und Swing-Trading-Modus wechseln kann. Wir erläuterten, wie Scalping sich auf das Erfassen kleiner Kursbewegungen innerhalb enger Zeitrahmen konzentriert, während Swing-Trading auf größere Richtungsbewegungen über längere Zeiträume abzielt, und zeigten, wie ein EA seine Logik, Stopp-Levels und Zeithorizonte je nach Marktkontext dynamisch anpassen kann.

Im Gegensatz dazu verlagert Teil 6 den Schwerpunkt weg von spezifischen Ein- und Ausstiegslogiken im Handel und richtet den Fokus stattdessen auf die Ausführungsbedingungen selbst – insbesondere auf die über den Spread messbaren Handelskosten. In Teil 6 stellen wir nun ein Modul vor, das die Spread-Bedingungen für alle Symbole kontinuierlich in Echtzeit überwacht und anpasst, indem es dynamische Sensitivitätsschwellenwerte verwendet, um zu bestimmen, welche Symbole derzeit optimal zu handeln sind. Diese Hochfrequenz-Symbolumschaltung auf der Grundlage einer adaptiven Spread-Bewertung ergänzt die breitere Multi-Pair-Architektur, indem sie der Ausführungsqualität und Kosteneffizienz Vorrang vor reinen Strategiemechanismen einräumt.


Systemübersicht und strategischer Ansatz

Der Adaptive Spread-Sensitivity EA ist ein hoch entwickeltes Multi-Symbol-Handelssystem, das entwickelt wurde, um die Handelsausführung dynamisch über mehrere Finanzinstrumente hinweg zu optimieren. Das Kernstück des Systems ist die kontinuierliche Überwachung der Echtzeit-Spreads für alle konfigurierten Symbole, die anhand von Kosteneffizienz-Kennzahlen bewertet werden, um den Handel mit Instrumenten mit den günstigsten Ausführungsbedingungen zu priorisieren.

Symbol-Ranking-Mechanismus

Im Gegensatz zu traditionellen Einzelsymbol-EAs implementiert dieses System eine intelligente Spread-Filterung, die Symbole mit ungewöhnlich hohen Spreads vorübergehend deaktiviert, um kostspielige Markteinstiege bei ungünstigen Liquiditätsbedingungen zu vermeiden, und sie automatisch wieder zu aktivieren, wenn sich die Spreads normalisieren. Die adaptive Architektur ermöglicht es dem EA, als „intelligenter Router“ zu fungieren, der dynamisch zwischen den verfügbaren Symbolen auf der Grundlage der sich verändernden Marktmikrostruktur umschaltet und so sicherstellt, dass die Trades immer auf dem wirtschaftlich effizientesten Instrument zu einem bestimmten Zeitpunkt ausgeführt werden.

Spread-Schutz-Mechanismus

Die Handelsstrategie basiert auf einem einfachen, aber effektiven Ansatz der technischen Analyse, der zwei gleitende Durchschnittsübergänge mit einer RSI-Momentum-Bestätigung kombiniert. Wenn die Spread-Bedingungen günstig sind, generiert das System Kaufsignale, wenn der schnelle EMA den langsamen EMA übersteigt, während der RSI überverkaufte Bedingungen anzeigt, und Verkaufssignale, wenn der schnelle EMA den langsamen EMA unterschreitet, während der RSI im überkauften Bereich liegt. Diese Kombination bietet ein ausgewogenes Einstiegs-Timing, indem gleitende Durchschnitte für die Trendrichtung und der RSI für die Einstiegspräzision verwendet werden.

Echtzeit-Dashboard


Die ersten Schritte

//+------------------------------------------------------------------+
//|                                           Spread Sensitivity.mq5 |
//|                        GIT under Copyright 2025, MetaQuotes Ltd. |
//|                     https://www.mql5.com/en/users/johnhlomohang/ |
//+------------------------------------------------------------------+
#property copyright "GIT under Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/johnhlomohang/"
#property version   "1.00"
#property description "Multi-Symbol EA with Adaptive Spread Sensitivity"
#property description "Dynamically switches between symbols based on spread efficiency"

#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input string   TradePairs = "EURUSD,GBPUSD,XAUUSD,US100,BTCUSD"; // Trading Pairs (comma separated)
input double   RiskPerTrade = 0.01;          // Risk % per trade
input int      MagicNumber = 98765;          // Magic Number

// Spread Sensitivity Settings
input group    "=== Spread Filter Settings ==="
input double   MaxAbsoluteSpread = 10.0;     // Max absolute spread (pips)
input double   MaxSpreadATRRatio = 0.25;     // Max spread/ATR ratio
input bool     UseAdaptiveFilter = true;     // Enable adaptive filtering
input int      DisableTimeoutSec = 60;       // Disable symbol timeout (seconds)
input bool     EnableSpreadRanking = true;   // Enable symbol ranking by spread
input int      MaxActiveSymbols = 3;         // Maximum active symbols at once

// Trading Strategy Settings
input group    "=== Trading Strategy Settings ==="
input ENUM_TIMEFRAMES TradingTimeframe = PERIOD_M5;  // Trading timeframe
input int      EMA_Fast_Period = 9;          // Fast EMA period
input int      EMA_Slow_Period = 21;         // Slow EMA period
input int      RSI_Period = 14;              // RSI period
input double   RSI_Overbought = 70;          // RSI overbought level
input double   RSI_Oversold = 30;            // RSI oversold level
input int      StopLoss_Pips = 30;           // Stop Loss in pips
input int      TakeProfit_Pips = 60;         // Take Profit in pips
input int      MaxOpenPositions = 1;         // Max positions per symbol
input int      TradeCooldownSeconds = 300;   // Cooldown between trades (seconds)

// ATR Settings for adaptive SL/TP
input group    "=== ATR Settings (Optional) ==="
input bool     UseATR_SL_TP = false;         // Use ATR for dynamic SL/TP
input double   ATR_SL_Multiplier = 1.5;      // ATR multiplier for SL
input double   ATR_TP_Multiplier = 2.0;      // ATR multiplier for TP

// Dashboard Settings
input group    "=== Dashboard Settings ==="
input bool     ShowDashboard = true;         // Show dashboard on chart
input color    DashboardBGColor = clrBlack;  // Dashboard background color
input color    DashboardTextColor = clrWhite;// Dashboard text color
input int      DashboardX = 20;              // Dashboard X position
input int      DashboardY = 20;              // Dashboard Y position
input int      FontSize = 8;                 // Dashboard font size

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
string   SymbolList[];
int      TotalPairs;
CTrade   Trade;
CPositionInfo PositionInfo;
datetime LastDashboardUpdate = 0;

// Spread monitoring structure
struct SpreadData
{
   string      symbol;
   double      spreadInPips;
   double      atrValue;
   double      spreadATRRatio;
   double      spreadScore;
   datetime    disabledUntil;
   bool        isTradeable;
   bool        isActive;
   datetime    lastTradeTime;
   int         tradeAttempts;
   int         successfulTrades;
   color       statusColor;
};

SpreadData spreadData[];

// Dashboard messages
string DashboardMessages[10];

Zu Beginn bauen wir eine flexible Konfigurationsebene für unseren dynamischen Multi-Pair Expert Advisor auf, indem wir alle benutzerdefinierten Eingaben in einer sauberen und modularen Weise gruppieren. Wir beginnen mit der allgemeinen Handelskontrolle, wie der Liste der zu überwachenden Symbole, dem Risiko pro Handel und einer „Magic Number“ für die Positionsverfolgung. Von dort aus führen wir einen speziellen Spread-Sensitivity-Bereich ein, der den Kern der Ausführungslogik des EA bildet. Diese Eingaben definieren absolute und adaptive Spread-Limits, ATR-normierte Spread-Schwellenwerte, temporäre Symbol-Deaktivierung und Symbol-Ranking-Beschränkungen, sodass der EA intelligent entscheiden kann, welche Märkte zu einem bestimmten Zeitpunkt kosteneffizient genug für den Handel sind. Wichtig ist, dass diese Ebene nicht vorschreibt, wie Trades eingegeben werden, sondern ob ein Symbol zur Teilnahme berechtigt ist, um die Ausführungsqualität für mehrere Instrumente zu gewährleisten.

Darüber hinaus definiert der Code Eingaben auf Strategieebene und unterstützende Infrastrukturen, die nur dann funktionieren, wenn ein Symbol den Spread-Filter passiert hat. Die Handelseinstellungen konfigurieren Indikatorparameter (EMA, RSI), Risikogrenzen (SL/TP, Cooldowns, Maximalpositionen) und optionale ATR-basierte dynamische Ausstiege, die eine kontrollierte und konsistente Handelsausführung ermöglichen. Unterhalb der Eingaben bilden globale Variablen und die SpreadData-Struktur die interne Zustandsmaschine des EA, die Echtzeit-Spread-Metriken, Symbolstatus, Aktivitätsflags, Handelsstatistiken und Dashboard-Visualisierungen verfolgt. Diese Struktur ermöglicht es dem EA, Symbole zu priorisieren, sie dynamisch zu deaktivieren und wieder zu aktivieren sowie das Systemverhalten in einem Live-Dashboard sichtbar zu machen, wodurch der EA sowohl in seiner Logik adaptiv als auch im Betrieb transparent ist.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Split trading pairs
   SplitString(TradePairs, ",", SymbolList);
   TotalPairs = ArraySize(SymbolList);
   
   if(TotalPairs == 0)
   {
      Print("Error: No symbols specified");
      return INIT_FAILED;
   }
   
   // Initialize spread data array
   ArrayResize(spreadData, TotalPairs);
   
   // Initialize dashboard messages
   for(int i = 0; i < 10; i++) DashboardMessages[i] = "";
   
   // Initialize each symbol
   for(int i = 0; i < TotalPairs; i++)
   {
      string symbol = SymbolList[i];
      
      // Validate symbol
      if(!SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE))
      {
         Print("Warning: Symbol ", symbol, " is not available for trading");
         continue;
      }
      
      // Initialize spread data
      spreadData[i].symbol = symbol;
      spreadData[i].spreadInPips = 0;
      spreadData[i].atrValue = 0;
      spreadData[i].spreadATRRatio = 0;
      spreadData[i].spreadScore = 0;
      spreadData[i].disabledUntil = 0;
      spreadData[i].isTradeable = true;
      spreadData[i].isActive = true;
      spreadData[i].lastTradeTime = 0;
      spreadData[i].tradeAttempts = 0;
      spreadData[i].successfulTrades = 0;
      spreadData[i].statusColor = clrGreen;
      
      // Subscribe to symbol
      SymbolSelect(symbol, true);
   }
   
   // Set trade parameters
   Trade.SetExpertMagicNumber(MagicNumber);
   Trade.SetDeviationInPoints(10);
   
   // Initialize dashboard
   if(ShowDashboard) CreateDashboard();
   
   AddDashboardMessage("EA Initialized with " + IntegerToString(TotalPairs) + " symbols");
   Print("EA Initialized. Total pairs: ", TotalPairs);
   
   return INIT_SUCCEEDED;
}

Die Funktion OnInit() übernimmt die komplette Startvorbereitung des Expert Advisors, indem sie Symbole, interne Datenstrukturen und Ausführungseinstellungen konfiguriert, bevor der Handel beginnt. Sie beginnt mit der Analyse der benutzerdefinierten Symbolliste und überprüft, ob mindestens ein handelbares Symbol vorhanden ist, und bricht die Initialisierung sicher ab, wenn keines gefunden wird. Die Funktion weist dann die spreadData-Struktur für jedes Symbol zu und initialisiert sie. Dabei werden Standardwerte für Spread-Metriken, Handelsstatus, Statistiken und visuelle Statusindikatoren festgelegt, während gleichzeitig jedes Symbol für Echtzeit-Datenaktualisierungen abonniert wird. Schließlich konfiguriert er die Parameter für die Handelsausführung wie die Magic Number und die Slippage-Toleranz, initialisiert das On-Chart-Dashboard, falls es aktiviert ist, protokolliert eine Startnachricht und bestätigt die erfolgreiche Initialisierung, um sicherzustellen, dass der EA vollständig synchronisiert, überwacht und für den adaptiven Multi-Symbol-Betrieb bereit ist.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Remove dashboard objects
   if(ShowDashboard) RemoveDashboard();
   
   // Print statistics
   Print("=== Trading Statistics ===");
   for(int i = 0; i < TotalPairs; i++)
   {
      Print(spreadData[i].symbol, ": ", 
            spreadData[i].tradeAttempts, " attempts, ", 
            spreadData[i].successfulTrades, " successful trades");
   }
   
   Print("EA Deinitialized");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   static int tickCounter = 0;
   tickCounter++;
   
   // Process one symbol per tick (prevents overloading)
   int symbolIndex = tickCounter % TotalPairs;
   string symbol = spreadData[symbolIndex].symbol;
   
   // Update spread data
   UpdateSpreadData(symbolIndex);
   
   // Check if symbol is tradeable
   if(!spreadData[symbolIndex].isTradeable || !spreadData[symbolIndex].isActive) 
      return;
   
   // Check cooldown period
   if(TimeCurrent() - spreadData[symbolIndex].lastTradeTime < TradeCooldownSeconds)
      return;
   
   // Execute trading logic
   ExecuteTradingLogic(symbolIndex);
   
   // Update dashboard every 10 ticks
   if(ShowDashboard && (tickCounter % 10 == 0) && (TimeCurrent() - LastDashboardUpdate >= 1))
   {
      UpdateDashboard();
      LastDashboardUpdate = TimeCurrent();
   }
}

Die Funktion OnDeinit() sorgt für ein sauberes und informatives Herunterfahren des Expert Advisors. Wenn der EA entfernt oder das Terminal geschlossen wird, werden zunächst alle Dashboard-Objekte aus dem Chart gelöscht, um visuelle Überbleibsel zu vermeiden. Anschließend wird eine kurze Zusammenfassung der Handelsleistung für jedes Symbol ausgedruckt, in der die Anzahl der während der Laufzeit aufgezeichneten Handelsversuche und erfolgreichen Ausführungen angegeben ist. Dieser abschließende Protokollierungsschritt sorgt für Transparenz und Post-Run-Diagnose und erleichtert die Bewertung des Verhaltens auf Symbolebene und der Systemeffizienz, bevor der EA vollständig deinitialisiert wird.

Die Funktion OnTick() hingegen definiert den Echtzeit-Ablauf des EA und ist auf Effizienz in einer Multi-Symbol-Umgebung ausgelegt. Anstatt alle Symbole bei jedem Tick zu verarbeiten, wird mithilfe eines Modulo-Zählers ein Symbol pro Tick durchlaufen, wodurch die CPU-Belastung verringert und Ausführungsengpässe vermieden werden. Für jedes ausgewählte Symbol aktualisiert der EA die Spread-Daten, prüft, ob das Symbol aktuell handelbar ist, erzwingt Sperrfristen und führt dann die Handelslogik aus, wenn alle Bedingungen erfüllt sind. Die Aktualisierung des Dashboards erfolgt nicht bei jedem Tick, sondern in regelmäßigen Abständen, sodass die Schnittstelle reaktionsschnell bleibt und die Leistungsstabilität in Szenarien mit hoher Symbolüberwachungsfrequenz erhalten bleibt.

//+------------------------------------------------------------------+
//| Timer function for spread updates                                |
//+------------------------------------------------------------------+
void OnTimer()
{
   // Update all spread data and rank symbols
   UpdateAllSpreadData();
   if(EnableSpreadRanking) RankSymbolsBySpread();
}

//+------------------------------------------------------------------+
//| Update spread data for all symbols                               |
//+------------------------------------------------------------------+
void UpdateAllSpreadData()
{
   for(int i = 0; i < TotalPairs; i++)
   {
      UpdateSpreadData(i);
   }
}

//+------------------------------------------------------------------+
//| Update spread data for specific symbol                           |
//+------------------------------------------------------------------+
void UpdateSpreadData(int index)
{
   string symbol = spreadData[index].symbol;
   
   // Get current bid and ask
   double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
   
   if(bid == 0 || ask == 0) return;
   
   // Calculate spread in pips
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double spreadPoints = (ask - bid) / point;
   spreadData[index].spreadInPips = NormalizeDouble(spreadPoints / 10, 1);
   
   // Calculate ATR for spread ratio
   spreadData[index].atrValue = CalculateATR(symbol, PERIOD_H1, 14);
   
   // Calculate spread/ATR ratio
   if(spreadData[index].atrValue > 0)
   {
      spreadData[index].spreadATRRatio = NormalizeDouble(spreadData[index].spreadInPips / spreadData[index].atrValue, 3);
   }
   
   // Evaluate tradeability
   EvaluateTradeability(index);
}

//+------------------------------------------------------------------+
//| Evaluate if symbol is tradeable based on spread                  |
//+------------------------------------------------------------------+
void EvaluateTradeability(int index)
{
   // Check if symbol is in timeout
   if(TimeCurrent() < spreadData[index].disabledUntil)
   {
      spreadData[index].isTradeable = false;
      spreadData[index].statusColor = clrOrange;
      return;
   }
   
   // Check absolute spread limit
   if(spreadData[index].spreadInPips > MaxAbsoluteSpread)
   {
      spreadData[index].isTradeable = false;
      spreadData[index].disabledUntil = TimeCurrent() + DisableTimeoutSec;
      AddDashboardMessage(spreadData[index].symbol + " disabled: High spread " + 
                          DoubleToString(spreadData[index].spreadInPips, 1));
      spreadData[index].statusColor = clrRed;
      return;
   }
   
   // Check ATR ratio if adaptive filtering is enabled
   if(UseAdaptiveFilter && spreadData[index].spreadATRRatio > MaxSpreadATRRatio)
   {
      spreadData[index].isTradeable = false;
      spreadData[index].statusColor = clrRed;
      return;
   }
   
   // All checks passed
   spreadData[index].isTradeable = true;
   spreadData[index].statusColor = clrGreen;
}

Dieser Block führt ein timergesteuertes Spread-Monitoring-System ein, das unabhängig von der Tickfrequenz arbeitet und eine konsistente und rechtzeitige Auswertung aller Symbole gewährleistet. Die Funktion OnTimer() fungiert als Scheduler, der periodisch die Spread-Daten für jedes konfigurierte Symbol aktualisiert und sie optional nach Spread-Effizienz einstuft, wenn dies aktiviert ist. Dieses Design entkoppelt die Spread-Analyse von den Preis-Ticks, sodass der EA auch in Zeiten geringer Liquidität reaktionsfähig bleibt. Der Aktualisierungsfluss läuft kaskadenförmig über UpdateAllSpreadData() und UpdateSpreadData(), wo Geld-/Briefkurse in Echtzeit gesammelt werden, Spreads in Pips berechnet werden und Volatilitätskontext durch die Berechnung von ATR-Werten hinzugefügt wird, was die Grundlage für die adaptive Spread-Bewertung bildet.

Die Funktion EvaluateTradeability() wendet dann einen mehrstufigen Entscheidungsprozess an, um festzustellen, ob jedes Symbol für den Handel geeignet ist. Es erzwingt zunächst Sperrfristen für zuvor deaktivierte Symbole, um ein schnelles Wiedereintreten unter instabilen Bedingungen zu verhindern. Als Nächstes werden absolute Spread-Limits und adaptive ATR-normierte Schwellenwerte überprüft, wobei Symbole, deren Handel zu kostspielig wird, automatisch deaktiviert und diese Ereignisse zur Transparenz auf dem Dashboard aufgezeichnet werden. Wenn alle Spread-Bedingungen erfüllt sind, wird das Symbol als handelbar markiert und optisch als gesund gekennzeichnet. Damit wird ein robuster Schutzmechanismus vervollständigt, der Symbole dynamisch auf der Grundlage der Ausführungsqualität in Echtzeit und nicht nach statischen Regeln filtert.

//+------------------------------------------------------------------+
//| Rank symbols by spread efficiency                                |
//+------------------------------------------------------------------+
void RankSymbolsBySpread()
{
   // Calculate spread score for each symbol
   for(int i = 0; i < TotalPairs; i++)
   {
      // Lower spread = better score
      double spreadComponent = 1.0 / (1.0 + spreadData[i].spreadInPips);
      
      // Lower ATR ratio = better score
      double atrComponent = 1.0 / (1.0 + spreadData[i].spreadATRRatio);
      
      // Combine components with weights
      spreadData[i].spreadScore = (0.6 * spreadComponent) + (0.4 * atrComponent);
   }
   
   // Simple bubble sort by score
   for(int i = 0; i < TotalPairs - 1; i++)
   {
      for(int j = i + 1; j < TotalPairs; j++)
      {
         if(spreadData[j].spreadScore > spreadData[i].spreadScore)
         {
            SpreadData temp = spreadData[i];
            spreadData[i] = spreadData[j];
            spreadData[j] = temp;
         }
      }
   }
   
   // Activate top N symbols
   for(int i = 0; i < TotalPairs; i++)
   {
      spreadData[i].isActive = (i < MaxActiveSymbols);
   }
}

//+------------------------------------------------------------------+
//| Execute trading logic for symbol                                 |
//+------------------------------------------------------------------+
void ExecuteTradingLogic(int index)
{
   string symbol = spreadData[index].symbol;
   
   // Check if symbol already has max positions
   if(CountOpenPositions(symbol) >= MaxOpenPositions) return;
   
   // Get indicator handles
   int handleEmaFast = iMA(symbol, TradingTimeframe, EMA_Fast_Period, 0, MODE_EMA, PRICE_CLOSE);
   int handleEmaSlow = iMA(symbol, TradingTimeframe, EMA_Slow_Period, 0, MODE_EMA, PRICE_CLOSE);
   int handleRSI = iRSI(symbol, TradingTimeframe, RSI_Period, PRICE_CLOSE);
   
   if(handleEmaFast == INVALID_HANDLE || handleEmaSlow == INVALID_HANDLE || handleRSI == INVALID_HANDLE)
   {
      Print("Error: Failed to create indicator handles for ", symbol);
      return;
   }
   
   // Get indicator values
   double emaFast[1], emaSlow[1], rsi[1];
   
   if(CopyBuffer(handleEmaFast, 0, 0, 1, emaFast) < 1) 
   {
      IndicatorRelease(handleEmaFast);
      IndicatorRelease(handleEmaSlow);
      IndicatorRelease(handleRSI);
      return;
   }
   
   if(CopyBuffer(handleEmaSlow, 0, 0, 1, emaSlow) < 1) 
   {
      IndicatorRelease(handleEmaFast);
      IndicatorRelease(handleEmaSlow);
      IndicatorRelease(handleRSI);
      return;
   }
   
   if(CopyBuffer(handleRSI, 0, 0, 1, rsi) < 1) 
   {
      IndicatorRelease(handleEmaFast);
      IndicatorRelease(handleEmaSlow);
      IndicatorRelease(handleRSI);
      return;
   }
   
   // Release indicator handles
   IndicatorRelease(handleEmaFast);
   IndicatorRelease(handleEmaSlow);
   IndicatorRelease(handleRSI);
   
   double emaF = emaFast[0];
   double emaS = emaSlow[0];
   double rsiV = rsi[0];
   
   // Generate trading signals
   if(emaF > emaS && rsiV < RSI_Oversold) // Buy signal: Fast EMA above Slow EMA and RSI oversold
   {
      spreadData[index].tradeAttempts++;
      double lotSize = CalculateLotSize(symbol);
      if(lotSize > 0)
      {
         if(ExecuteTrade(ORDER_TYPE_BUY, symbol, lotSize, StopLoss_Pips, TakeProfit_Pips))
         {
            spreadData[index].lastTradeTime = TimeCurrent();
            spreadData[index].successfulTrades++;
            AddDashboardMessage("BUY " + symbol + " | Spread: " + DoubleToString(spreadData[index].spreadInPips, 1));
         }
      }
   }
   else if(emaF < emaS && rsiV > RSI_Overbought) // Sell signal: Fast EMA below Slow EMA and RSI overbought
   {
      spreadData[index].tradeAttempts++;
      double lotSize = CalculateLotSize(symbol);
      if(lotSize > 0)
      {
         if(ExecuteTrade(ORDER_TYPE_SELL, symbol, lotSize, StopLoss_Pips, TakeProfit_Pips))
         {
            spreadData[index].lastTradeTime = TimeCurrent();
            spreadData[index].successfulTrades++;
            AddDashboardMessage("SELL " + symbol + " | Spread: " + DoubleToString(spreadData[index].spreadInPips, 1));
         }
      }
   }
}

Hier stellen wir eine Spread-Effizienz-Ranking-Maschine vor, die bestimmt, welche Symbole zu einem bestimmten Zeitpunkt am Handel teilnehmen dürfen. In RankSymbolsBySpread() wird jedem Symbol ein zusammengesetzter Spread-Score zugewiesen, der auf zwei Faktoren der Ausführungsqualität basiert: dem absoluten Spread und dem Spread-ATR-Verhältnis. Beide Komponenten werden invertiert, sodass niedrigere Kosten zu höheren Werten führen, und dann unter Verwendung der gewichteten Bedeutung kombiniert, um die rohe Spanne hervorzuheben und gleichzeitig den Volatilitätskontext zu berücksichtigen. Nach der Berechnung der Punkte werden die Symbole in absteigender Reihenfolge sortiert, sodass die kosteneffizientesten Instrumente ganz oben auf der Prioritätenliste stehen.

Nach dem Ranking wendet der EA einen dynamischen Aktivierungsfilter an, indem er nur die obersten MaxActiveSymbols aktiviert und die übrigen als inaktiv markiert. Dieser Mechanismus stellt sicher, dass der EA keine Ressourcen oder Kapital für Symbole mit minderwertigen Ausführungsbedingungen verschwendet, selbst wenn diese ansonsten gültig sind. Anstatt Symbole dauerhaft auszuschließen, bewertet dieses System sie kontinuierlich neu und mischt sie um, wenn sich die Spreads entwickeln, sodass zuvor inaktive Symbole wieder in die Rotation aufgenommen werden können, wenn sich ihre Ausführungsqualität verbessert. So entsteht ein sich selbst ausgleichendes, anpassungsfähiges Universum handelbarer Symbole, das ausschließlich von der Kosteneffizienz in Echtzeit bestimmt wird.

Der zweite Teil, ExecuteTradingLogic(), wird nur für Symbole ausgeführt, die alle Spread- und Aktivierungsfilter bestanden haben, wodurch die Ausführungsqualität sauber von der Strategielogik getrennt wird. Es ruft die erforderlichen EMA- und RSI-Indikatoren ab, validiert die Datenverfügbarkeit und generiert dann Handelssignale auf der Grundlage von Trendausrichtung und Momentum-Erschöpfung. Wenn ein Signal bestätigt wird, zeichnet der EA Handelsversuche auf, berechnet die Positionsgröße, führt den Handel aus und aktualisiert die Performance-Metriken auf Symbolebene sowie die Dashboard-Meldungen. Diese Struktur stellt sicher, dass Handelsentscheidungen nur auf die bestplatzierten Symbole angewendet werden, was die Kernphilosophie des adaptiven, ausführungsorientierten Multi-Pair-Handels unterstreicht.

//+------------------------------------------------------------------+
//| Execute trade with CTrade                                        |
//+------------------------------------------------------------------+
bool ExecuteTrade(ENUM_ORDER_TYPE tradeType, string symbol, double lotSize, int stopLossPips, int takeProfitPips)
{
   // Get symbol info
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
   
   // Get current price
   double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
   double price = (tradeType == ORDER_TYPE_BUY) ? ask : bid;
   
   // Calculate pip size for different instruments
   double pipSize = CalculatePipSize(symbol, digits, point);
   
   // Use ATR for dynamic SL/TP if enabled
   double slDistance = 0, tpDistance = 0;
   
   if(UseATR_SL_TP)
   {
      double atr = iATR(symbol, TradingTimeframe, 14);
      if(atr > 0)
      {
         slDistance = atr * ATR_SL_Multiplier;
         tpDistance = atr * ATR_TP_Multiplier;
      }
   }
   
   // Fallback to fixed pips if ATR not used or failed
   if(slDistance == 0) slDistance = stopLossPips * pipSize;
   if(tpDistance == 0) tpDistance = takeProfitPips * pipSize;
   
   // Calculate SL and TP prices
   double sl = 0, tp = 0;
   
   if(slDistance > 0)
   {
      sl = (tradeType == ORDER_TYPE_BUY) ? price - slDistance : price + slDistance;
      sl = NormalizeDouble(sl, digits);
   }
   
   if(tpDistance > 0)
   {
      tp = (tradeType == ORDER_TYPE_BUY) ? price + tpDistance : price - tpDistance;
      tp = NormalizeDouble(tp, digits);
   }
   
   // Execute trade with CTrade
   bool success = false;
   
   if(tradeType == ORDER_TYPE_BUY)
   {
      success = Trade.Buy(lotSize, symbol, price, sl, tp, "Adaptive Spread EA");
   }
   else if(tradeType == ORDER_TYPE_SELL)
   {
      success = Trade.Sell(lotSize, symbol, price, sl, tp, "Adaptive Spread EA");
   }
   
   if(success)
   {
      PrintFormat("%s %s | Lot: %.2f | Price: %.5f | SL: %.5f | TP: %.5f | Spread: %.1f",
                  EnumToString(tradeType), symbol, lotSize, price, sl, tp, 
                  SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID));
      return true;
   }
   else
   {
      PrintFormat("Failed to open %s on %s | Error: %d", 
                  EnumToString(tradeType), symbol, GetLastError());
      return false;
   }
}

//+------------------------------------------------------------------+
//| Calculate pip size for different instruments                     |
//+------------------------------------------------------------------+
double CalculatePipSize(string symbol, int digits, double point)
{
   // Detect pip size automatically
   if(StringFind(symbol, "JPY") != -1)              // JPY pairs
      return (digits == 3) ? point * 10 : point;
   else if(StringFind(symbol, "XAU") != -1 || StringFind(symbol, "GOLD") != -1)   // Metals
      return 0.10;
   else if(StringFind(symbol, "BTC") != -1 || StringFind(symbol, "ETH") != -1)    // Cryptos
      return point * 100.0;
   else if(StringFind(symbol, "US") != -1 && digits <= 2)                         // Indices
      return point;
   else
      return (digits == 3 || digits == 5) ? point * 10 : point;                   // Default Forex
}

//+------------------------------------------------------------------+
//| Calculate position size based on risk                            |
//+------------------------------------------------------------------+
double CalculateLotSize(string symbol)
{
   double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   
   if(accountBalance <= 0) return minLot;
   
   // Simple lot calculation based on risk percentage
   double riskAmount = accountBalance * (RiskPerTrade / 100.0);
   double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
   
   if(tickValue <= 0)
   {
      // Fallback calculation
      double contractSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
      tickValue = (SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE) * contractSize) / SymbolInfoDouble(symbol, SYMBOL_POINT);
   }
   
   double pipSize = CalculatePipSize(symbol, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS), 
                                    SymbolInfoDouble(symbol, SYMBOL_POINT));
   double stopLossPoints = StopLoss_Pips * 10; // Convert pips to points
  
   if(stopLossPoints > 0 && tickValue > 0)
   {
      double lotSize = riskAmount / (stopLossPoints * tickValue);
      lotSize = NormalizeDouble(lotSize, 2);
      
      // Apply lot size limits
      lotSize = MathMax(lotSize, minLot);
      lotSize = MathMin(lotSize, maxLot);
      lotSize = MathRound(lotSize / lotStep) * lotStep;
      
      return lotSize;
   }
   
   return minLot;
}

In diesem Abschnitt definieren wir die Handelsausführungs- und Risikokontrollschicht des EA, beginnend mit ExecuteTrade(), die standardisiert, wie Aufträge unabhängig vom Symboltyp eröffnet werden. Die Funktion ruft zunächst symbolspezifische Preisangaben ab und ermittelt den korrekten Ausführungskurs auf der Grundlage der Handelsrichtung. Die Pip-Größe wird dann dynamisch berechnet, um Forex-Paare, Metalle, Indizes und Kryptowährungen ohne hart kodierte Annahmen zu unterstützen. Stop-Loss- und Take-Profit-Abstände werden entweder von ATR-basierten Volatilitätsmessungen oder von festen Pip-Werten abgeleitet, um die Robustheit bei unterschiedlichen Marktbedingungen zu gewährleisten. Sobald die Preisniveaus auf Symbolgenauigkeit normalisiert sind, werden die Trades mit der CTrade-Klasse ausgeführt, wobei sowohl erfolgreiche als auch fehlgeschlagene Ausführungen detailliert protokolliert werden, um Transparenz und Fehlersuche zu gewährleisten.

Die unterstützenden Funktionen CalculatePipSize() und CalculateLotSize() sorgen für eine konsistente Positionsgrößenbestimmung und ein einheitliches Risikomanagement bei heterogenen Instrumenten. CalculatePipSize() passt Pip-Definitionen automatisch auf der Grundlage von Symbolcharakteristika an, sodass der EA gemischte Anlageklassen präzise handeln kann. CalculateLotSize() berechnet dann die Positionsgröße anhand des Kontostands und des Risikoprozentsatzes, wobei das monetäre Risiko in ein Volumen umgerechnet wird und gleichzeitig die Beschränkungen des Brokers wie Mindest-, Höchst- und Schrittgrößen beachtet werden. Zusammen gewährleisten diese Funktionen, dass jeder Handel mit kontrolliertem Risiko, instrumentenbezogener Präzision und konsistentem Verhalten ausgeführt wird – und verstärken so das adaptive Multi-Symbol-Ausführungssystem des EA.

void UpdateDashboard()
{
   if(!ShowDashboard) return;
   
   // Update ranking
   for(int i = 0; i < MathMin(MaxActiveSymbols + 2, TotalPairs); i++)
   {
      string objName = "Dashboard_Rank_" + IntegerToString(i);
      string status = spreadData[i].isActive ? "Yes" : "No";
      
      string text = IntegerToString(i+1) + ". " + spreadData[i].symbol + 
                    " | Spread: " + DoubleToString(spreadData[i].spreadInPips, 1) + 
                    " | Score: " + DoubleToString(spreadData[i].spreadScore, 3) + 
                    " | Active: " + status;
      
      ObjectSetString(0, objName, OBJPROP_TEXT, text);
      ObjectSetInteger(0, objName, OBJPROP_COLOR, spreadData[i].statusColor);
   }
   
   // Update messages
   for(int i = 0; i < 10; i++)
   {
      string objName = "Dashboard_Msg_" + IntegerToString(i);
      ObjectSetString(0, objName, OBJPROP_TEXT, DashboardMessages[i]);
   }
}

void AddDashboardMessage(string message)
{
   // Shift messages up
   for(int i = 9; i > 0; i--)
   {
      DashboardMessages[i] = DashboardMessages[i-1];
   }
   
   // Add new message at the beginning
   DashboardMessages[0] = TimeToString(TimeCurrent(), TIME_SECONDS) + ": " + message;
}

//+------------------------------------------------------------------+
//| Chart Event Handler                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
   if(ShowDashboard && (id == CHARTEVENT_CHART_CHANGE || id == CHARTEVENT_CLICK))
   {
      UpdateDashboard();
   }
}

//+------------------------------------------------------------------+
//| Dashboard Functions                                              |
//+------------------------------------------------------------------+
void CreateDashboard()
{
   // Create main dashboard background
   ObjectCreate(0, "Dashboard_BG", OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_XDISTANCE, DashboardX);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_YDISTANCE, DashboardY);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_XSIZE, 400);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_YSIZE, 350);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_BGCOLOR, DashboardBGColor);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_BORDER_COLOR, clrGray);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "Dashboard_BG", OBJPROP_HIDDEN, true);
   
   // Create title
   ObjectCreate(0, "Dashboard_Title", OBJ_LABEL, 0, 0, 0);
   ObjectSetString(0, "Dashboard_Title", OBJPROP_TEXT, "=== Adaptive Spread EA ===");
   ObjectSetInteger(0, "Dashboard_Title", OBJPROP_XDISTANCE, DashboardX + 10);
   ObjectSetInteger(0, "Dashboard_Title", OBJPROP_YDISTANCE, DashboardY + 10);
   ObjectSetInteger(0, "Dashboard_Title", OBJPROP_COLOR, clrYellow);
   ObjectSetInteger(0, "Dashboard_Title", OBJPROP_FONTSIZE, FontSize + 2);
   ObjectSetString(0, "Dashboard_Title", OBJPROP_FONT, "Consolas");
   ObjectSetInteger(0, "Dashboard_Title", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   
   // Create ranking header
   ObjectCreate(0, "Dashboard_RankHeader", OBJ_LABEL, 0, 0, 0);
   ObjectSetString(0, "Dashboard_RankHeader", OBJPROP_TEXT, "=== Symbol Ranking ===");
   ObjectSetInteger(0, "Dashboard_RankHeader", OBJPROP_XDISTANCE, DashboardX + 10);
   ObjectSetInteger(0, "Dashboard_RankHeader", OBJPROP_YDISTANCE, DashboardY + 35);
   ObjectSetInteger(0, "Dashboard_RankHeader", OBJPROP_COLOR, clrYellow);
   ObjectSetInteger(0, "Dashboard_RankHeader", OBJPROP_FONTSIZE, FontSize);
   ObjectSetString(0, "Dashboard_RankHeader", OBJPROP_FONT, "Consolas");
   ObjectSetInteger(0, "Dashboard_RankHeader", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   
   // Create symbol ranking labels
   for(int i = 0; i < MaxActiveSymbols + 2; i++)
   {
      string objName = "Dashboard_Rank_" + IntegerToString(i);
      ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, DashboardX + 10);
      ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, DashboardY + 55 + (i * 20));
      ObjectSetInteger(0, objName, OBJPROP_COLOR, DashboardTextColor);
      ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, FontSize);
      ObjectSetString(0, objName, OBJPROP_FONT, "Consolas");
      ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   }
   
   // Create messages header
   ObjectCreate(0, "Dashboard_MsgHeader", OBJ_LABEL, 0, 0, 0);
   ObjectSetString(0, "Dashboard_MsgHeader", OBJPROP_TEXT, "=== Messages ===");
   ObjectSetInteger(0, "Dashboard_MsgHeader", OBJPROP_XDISTANCE, DashboardX + 10);
   ObjectSetInteger(0, "Dashboard_MsgHeader", OBJPROP_YDISTANCE, DashboardY + 180);
   ObjectSetInteger(0, "Dashboard_MsgHeader", OBJPROP_COLOR, clrYellow);
   ObjectSetInteger(0, "Dashboard_MsgHeader", OBJPROP_FONTSIZE, FontSize);
   ObjectSetString(0, "Dashboard_MsgHeader", OBJPROP_FONT, "Consolas");
   ObjectSetInteger(0, "Dashboard_MsgHeader", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   
   // Create message labels
   for(int i = 0; i < 10; i++)
   {
      string objName = "Dashboard_Msg_" + IntegerToString(i);
      ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, DashboardX + 10);
      ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, DashboardY + 200 + (i * 15));
      ObjectSetInteger(0, objName, OBJPROP_COLOR, DashboardTextColor);
      ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, FontSize);
      ObjectSetString(0, objName, OBJPROP_FONT, "Consolas");
      ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   }
}

void RemoveDashboard()
{
   ObjectsDeleteAll(0, "Dashboard_");
}

Dieser Code implementiert eine visuelle Echtzeit-Dashboard-Ebene, die die interne Entscheidungsfindung des EA direkt im Chart sichtbar macht. Die Funktion UpdateDashboard() aktualisiert die Rangliste der Symbole, indem sie die Spread-Werte, die Spread-Effizienz-Scores und den aktiven Status für die am besten platzierten Symbole anzeigt und dabei farbliche Hinweise verwendet, die den aktuellen Status der Handelbarkeit jedes Symbols widerspiegeln. Neben den Ranglistendaten zeigt das Dashboard auch ein fortlaufendes Meldungsprotokoll an, in dem wichtige Systemereignisse wie die Deaktivierung von Symbolen oder die Ausführung von Trades erfasst werden. Die Hilfsfunktion AddDashboardMessage() pflegt dieses Protokoll, indem sie ältere Nachrichten nach unten verschiebt und neue Einträge mit einem Zeitstempel versieht, sodass die aktuellsten und wichtigsten Informationen immer auf einen Blick sichtbar sind.

Die übrigen Funktionen behandeln den Lebenszyklus und die Interaktivität des Dashboards. OnChartEvent() stellt sicher, dass das Dashboard mit Chartaktualisierungen und Benutzerinteraktionen synchronisiert bleibt, indem es eine Aktualisierung erzwingt, wenn sich das Chart ändert oder angeklickt wird. CreateDashboard() erstellt die gesamte Oberfläche von Grund auf neu, einschließlich des Hintergrundpanels, der Abschnittsüberschriften, der Beschriftungen für die Rangfolge der Symbole und der Nachrichtenzeilen, die alle so positioniert und gestaltet sind, dass sie übersichtlich sind und das Chart möglichst wenig stören. Schließlich bietet RemoveDashboard() einen sauberen Abrissmechanismus, indem es alle dashboardbezogenen Objekte löscht, wenn der EA entfernt wird. So wird sichergestellt, dass keine visuellen Artefakte zurückbleiben und die Chartumgebung aufgeräumt und professionell bleibt.


Backtest-Ergebnisse

Die Tests wurden über ein etwa zweimonatiges Testfenster vom 19. November 2025 bis zum 17. Januar 2026 mit den folgenden Einstellungen durchgeführt:

Eingabe-Einstellungen

Nun die Equity-Kurve und die Backtest-Ergebnisse:

Eq-Kurve

BT-Ergebnisse


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir ein Adaptive Spread-Sensitivity Framework entwickelt und implementiert haben, das als intelligente Ausführungsschicht innerhalb eines dynamischen Multi-Pair-EA arbeitet. Das System überwacht kontinuierlich die Spreads in Echtzeit, normalisiert sie anhand des Volatilitätskontexts, ordnet die Symbole nach Kosteneffizienz und aktiviert oder deaktiviert die Instrumente dynamisch auf der Grundlage der aktuellen Ausführungsqualität. Durch die Trennung der Spread-Bewertung von der Handelslogik haben wir sichergestellt, dass die Symbolauswahl, die Priorisierung und die Schutzmechanismen anpassungsfähig, leichtgewichtig und skalierbar bleiben, sodass der EA den Fokus in hoher Frequenz zwischen den Symbolen wechseln kann, ohne die zugrunde liegenden Strategieregeln zu ändern.

Zusammenfassend lässt sich sagen, dass dieser Ansatz den Händlern ein leistungsfähiges Instrument an die Hand gibt, um die Handelskosten und das Ausführungsrisiko in schnelllebigen Multi-Symbol-Umgebungen zu kontrollieren. Anstatt alle Paare blind zu handeln, konzentriert der EA seine Aktivitäten auf intelligente Weise auf die effizientesten Märkte zu einem bestimmten Zeitpunkt, reduziert Slippage, vermeidet ungünstige Spread-Bedingungen und verbessert die allgemeine Handelsqualität. Für die Händler bedeutet dies eine sauberere Ausführung, eine bessere Kapitaleffizienz und ein widerstandsfähigeres automatisiertes System, das sich an die sich verändernde Marktmikrostruktur anpasst, anstatt durch statische Annahmen eingeschränkt zu sein.

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

Beigefügte Dateien |
Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Vom Einsteiger zum Experten: Statistische Validierung von Angebots- und Nachfragezonen Vom Einsteiger zum Experten: Statistische Validierung von Angebots- und Nachfragezonen
Heute decken wir die oft übersehene statistische Grundlage hinter den Handelsstrategien für Angebot und Nachfrage auf. Durch die Kombination von MQL5 mit Python über einen Jupyter-Notebook-Workflow führen wir eine strukturierte, datengesteuerte Untersuchung durch, die darauf abzielt, visuelle Marktannahmen in messbare Erkenntnisse zu verwandeln. Dieser Artikel behandelt den gesamten Forschungsprozess, einschließlich der Datenerfassung, der Python-basierten statistischen Analyse, des Algorithmusentwurfs, der Tests und der endgültigen Schlussfolgerungen. Um die Methodik und die Ergebnisse im Detail nachzuvollziehen, lesen Sie den vollständigen Artikel.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 6): Weiterentwicklung der RSI-Berechnungen mit Glättung, Farbwechsel und Multi-Timeframe-Unterstützung Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 6): Weiterentwicklung der RSI-Berechnungen mit Glättung, Farbwechsel und Multi-Timeframe-Unterstützung
In diesem Artikel erstellen wir einen vielseitigen RSI-Indikator in MQL5, der mehrere Varianten, Datenquellen und Glättungsmethoden für eine verbesserte Analyse unterstützt. Wir fügen Farbwechsel für farbliche Darstellungen, dynamische Grenzen für überkaufte/überverkaufte Zonen und Benachrichtigungen für Trendwarnungen hinzu. Es unterstützt mehrere Zeitrahmen mit Interpolation und bietet uns ein anpassbares RSI-Tool für verschiedene Strategien.