Den Marktstimmungsindikator automatisieren
Einführung
Im vorigen Artikel haben wir einen nutzerdefinierten Marktstimmungsindikator entwickelt, der Signale aus verschiedenen Zeitrahmen in einem einzigen, leicht ablesbaren Panel kombiniert. Wir analysierten Trends auf höheren Zeitebenen zusammen mit Strukturen auf niedrigeren Zeitebenen – wir untersuchten Kursbewegungen, Höchst- und Tiefststände, gleitende Durchschnitte und Volatilität – um den Markt in fünf eindeutige Stimmungen einzuordnen: aufwärts, abwärts, mehr und weniger Risiko oder neutral.
Die Automatisierung des nutzerdefinierten Indikators bringt mehrere entscheidende Vorteile für den Handel. Es gewährleistet eine kontinuierliche, objektive Überwachung der Stimmung ohne emotionale Voreingenommenheit und ermöglicht eine schnellere Reaktion auf Marktveränderungen. Wir können die Konsistenz der Analyse über verschiedene Zeiträume hinweg aufrechterhalten, durch die systematische Erkennung von Ausbruchs- oder Trendbestätigungssignalen frühzeitig Chancen erkennen und das Risiko effektiver zuweisen. Letztlich verwandelt die Automatisierung einen arbeitsintensiven, fehleranfälligen Prozess in einen effizienten EA, der datengestützte Entscheidungen unterstützt.
Ausführungslogik
Die Stimmung aufwärts mit mehr Risiko:
Die Aufwärts-Stimmung spiegelt eine starke Aufwärtsdynamik wider, bei der Trends und Preisstrukturen in einem größeren Zeitrahmen bestätigen, dass die Käufer die Kontrolle haben, während eine risikofreudige Stimmung auf ein größeres Anlegervertrauen und eine höhere Risikobereitschaft hindeutet, was die Preise häufig in die Höhe treibt. In beiden Fällen unterstützt das Marktumfeld eine Aufwärtsbewegung, und unsere Strategie besteht darin, einen Kauf zu tätigen, sobald der Indikator entweder einen Aufwärtsbewegung oder eine risikofreudige Stimmung signalisiert, da es wahrscheinlicher ist, dass der Kurs weiter steigt.

Die Stimmung abwärts mit weniger Risiko:
Die Abwärts-Stimmung spiegelt eine starke Abwärtsdynamik wider, bei der die Trends und Preisstrukturen auf höheren Zeitebenen bestätigen, dass der Markt von Verkäufern dominiert wird, während die risikoarme Stimmung eine Verschiebung in Richtung Sicherheit signalisiert, bei der die Anleger risikoreichere Anlagen meiden und die Preise nach unten treiben. In beiden Fällen unterstützt das Marktumfeld eine Abwärtsbewegung, und unsere Strategie besteht darin, eine Verkaufstransaktion durchzuführen, sobald der Indikator eine bärische oder risikoarme Stimmung signalisiert, da es wahrscheinlicher ist, dass der Kurs weiter fällt.

Die neutrale Stimmung:
Eine neutrale oder unruhige Stimmung tritt auf, wenn der Markt keine klare Richtung hat und sich die Kurse in einer Spanne konsolidieren oder unvorhersehbar zwischen den Niveaus schwanken. In diesen unsicheren Perioden ist die Wahrscheinlichkeit von Fehlsignalen und „Whipsaws“ höher, sodass wir den neutralen Wert als Filter verwenden, um den Abschluss von Handelsgeschäften zu vermeiden. Indem wir uns unter solchen Bedingungen aus dem Markt heraushalten, schützen wir unser Kapital und warten auf ein deutlicheres Aufwärts-, Abwärtssignal oder eine höhere oder niedrigere Risikostimmung, bevor wir aktiv werden.

Die ersten Schritte
//+------------------------------------------------------------------+ //| Mark_Sent_Auto.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" #include <Trade/Trade.mqh> CTrade trade;
In diesem Code bringt die Zeile #include <Trade/Trade.mqh> die MetaTrader 5-Handelsbibliothek ein, die die integrierte CTrade-Klasse bereitstellt. Diese Klasse enthält alle wesentlichen Funktionen, die für die Automatisierung der Handelsausführung erforderlich sind, z. B. das Eröffnen, Schließen, Ändern oder Verwalten von Positionen und Aufträgen. Nachdem wir die Bibliothek eingebunden haben, erstellen wir eine Instanz der Klasse mit CTrade trade;. Dieses Objekt fungiert als unsere Schnittstelle zum Handelssystem – über es können wir Methoden wie trade.Buy(), trade.Sell() oder trade.PositionClose() aufrufen. Da unser Ziel darin besteht, Handelsentscheidungen auf der Grundlage des Stimmungsindikators zu automatisieren, ist es notwendig, diese Bibliothek einzubinden und ein CTrade-Objekt zu definieren, um Handelsgeschäfte tatsächlich programmatisch zu senden und zu verwalten.
//+------------------------------------------------------------------+ //| Input parameters | //+------------------------------------------------------------------+ input group "Timeframe Settings" input ENUM_TIMEFRAMES HigherTF = PERIOD_H4; // Higher Timeframe input ENUM_TIMEFRAMES LowerTF1 = PERIOD_H1; // Lower Timeframe 1 input ENUM_TIMEFRAMES LowerTF2 = PERIOD_M30; // Lower Timeframe 2 input group "Indicator Settings" input int MAPeriod = 200; // EMA Period input int SwingLookback = 5; // Swing Lookback Bars input double ATRThreshold = 0.002; // ATR Threshold for Neutral input group "Trading Settings" input double LotSize = 0.1; // Trade Lot Size input int StopLossPips = 50; // Stop Loss in Pips input int TakeProfitPips = 100; // Take Profit in Pips input int MagicNumber = 12345; // Magic Number for trades input int Slippage = 3; // Slippage in points input group "Trailing Stop Parameters" input bool UseTrailingStop = true; // Enable trailing stops input int BreakEvenAtPips = 500; // Move to breakeven at this profit (pips) input int TrailStartAtPips = 600; // Start trailing at this profit (pips) input int TrailStepPips = 100; // Trail by this many pips input group "Risk Management" input bool UseRiskManagement = true; // Enable Risk Management input double RiskPercent = 2.0; // Risk Percentage per Trade input int MinBarsBetweenTrades = 3; // Minimum bars between trades input group "Visual Settings" input bool ShowPanel = true; // Show Information Panel input int PanelCorner = 1; // Panel Corner: 0=TopLeft,1=TopRight,2=BottomLeft,3=BottomRight input int PanelX = 10; // Panel X Position input int PanelY = 10; // Panel Y Position input string FontFace = "Arial"; // Font Face input int FontSize = 10; // Font Size input color BullishColor = clrLimeGreen; // Bullish Color input color BearishColor = clrRed; // Bearish Color input color RiskOnColor = clrDodgerBlue; // Risk-On Color input color RiskOffColor = clrDarkGray; // Risk-Off Color input color NeutralColor = clrGold; // Neutral Color
Im ersten Abschnitt werden die Eingaben für die Zeitrahmenkonfiguration und die Indikatoreinstellungen zusammengefasst. Durch die Definition von HigherTF, LowerTF1 und LowerTF2 kann der Expert Advisor die Marktstimmung auf mehreren Ebenen des Preisgeschehens analysieren, z. B. einen höheren Zeitrahmen für die Trendrichtung und niedrigere Zeitrahmen für die Bestätigung der Struktur. Unter den Indikatoreinstellungen bieten Parameter wie MAPeriod (der Zeitraum für den Exponential Moving Average), SwingLookback (die Anzahl der Balken, die auf Swing-Hochs und -Tiefs geprüft werden) und ATRThreshold (zur Erkennung neutraler oder unruhiger Märkte) Händlern die Möglichkeit, die Stimmungserkennung flexibel zu gestalten.
Der zweite Abschnitt kontrolliert das Handelsverhalten und das Risikomanagement. Eingaben wie LotSize, StopLossPips und TakeProfitPips definieren die wichtigsten Parameter für die Handelsausführung, während MagicNumber dem EA hilft, seine Handelsgeschäfte von anderen zu unterscheiden, und Slippage die Toleranz bei der Auftragsausführung verwaltet. Trailing-Stop-Parameter (UseTrailingStop, BreakEvenAtPips, TrailStartAtPips, TrailStepPips) ermöglichen eine dynamische Absicherung von Gewinnen, sobald sich ein Handel günstig entwickelt. Darüber hinaus stellt der Risikomanagement-Block (UseRiskManagement, RiskPercent, MinBarsBetweenTrades) sicher, dass die Positionsgröße mit dem Eigenkapital des Kontos übereinstimmt, ein übermäßiges Engagement verhindert wird und ein Mindestabstand zwischen den Geschäften für Disziplin sorgt.
Der letzte Abschnitt befasst sich mit der visuellen Darstellung des Stimmungsindikators. Mit Optionen wie ShowPanel, PanelCorner, PanelX und PanelY kann der Händler festlegen, wo das Stimmung-Dashboard im Chart angezeigt wird. Die Anpassung der Schriftart (FontFace, FontSize) gewährleistet die Lesbarkeit, während farbcodierte Eingaben (BullishColor, BearishColor, RiskOnColor, RiskOffColor, NeutralColor) sofortige visuelle Anhaltspunkte für jeden Stimmungstyp liefern. Diese Anpassung verbessert die Übersichtlichkeit und Nutzerfreundlichkeit und ermöglicht es Händlern, die Marktbedingungen schnell und auf einen Blick zu beurteilen.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int higherTFHandle, lowerTF1Handle, lowerTF2Handle; double higherTFMA[], lowerTF1MA[], lowerTF2MA[]; datetime lastUpdateTime = 0; string indicatorName = "MarketSentEA"; int currentSentiment = 0; int previousSentiment = 0; datetime lastTradeTime = 0; double global_PipValueInPoints; // Stores pip value in points //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { indicatorName = "MarketSentEA_" + IntegerToString(ChartID()); // Create handles for MA indicators on different timeframes higherTFHandle = iMA(_Symbol, HigherTF, MAPeriod, 0, MODE_EMA, PRICE_CLOSE); lowerTF1Handle = iMA(_Symbol, LowerTF1, MAPeriod, 0, MODE_EMA, PRICE_CLOSE); lowerTF2Handle = iMA(_Symbol, LowerTF2, MAPeriod, 0, MODE_EMA, PRICE_CLOSE); // Set array as series ArraySetAsSeries(higherTFMA, true); ArraySetAsSeries(lowerTF1MA, true); ArraySetAsSeries(lowerTF2MA, true); //global_PipValueInPoints = GetPipValueInPoints(); // Create panel if enabled if(ShowPanel) CreatePanel(); // Check for trading permission if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) Print("Error: Trading is not allowed! Check AutoTrading permission."); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Delete all objects ObjectsDeleteAll(0, indicatorName); IndicatorRelease(higherTFHandle); IndicatorRelease(lowerTF1Handle); IndicatorRelease(lowerTF2Handle); }
Hier werden globale Variablen definiert, die der Expert Advisor während seiner Ausführung verwendet. Die Handles (higherTFHandle, lowerTF1Handle und lowerTF2Handle) werden erstellt, um Verweise auf Indikatoren des gleitenden Durchschnitts (MA) über verschiedene Zeitrahmen zu speichern. Die Arrays (higherTFMA, lowerTF1MA, lowerTF2MA) enthalten die berechneten Werte dieser Indikatoren für die spätere Analyse. Variablen wie lastUpdateTime, lastTradeTime und currentSentiment/previousSentiment helfen dabei, nachzuvollziehen, wann die letzten Berechnungen oder Handelsgeschäfte durchgeführt wurden und Stimmungsänderungen zu verwalten. Die Zeichenkette indicatorName identifiziert die auf dem Chart gezeichneten Objekte eindeutig, während global_PipValueInPoints Pip-to-Point-Umrechnungen für die Berechnung von Risiko und Handelsgröße speichert.
Die Funktion OnInit() initialisiert den Expert Advisor. Es legt einen eindeutigen Indikatornamen fest (der an die Chart-ID gebunden ist), erstellt MA-Handles für die gewählten Zeitrahmen und konfiguriert die Datenarrays als Zeitreihen. Es enthält auch eine Logik zur Erstellung eines Panels. Die Funktion OnDeinit() ist für die Aufräumarbeiten zuständig, wenn der EA entfernt wird – sie löscht die mit dem Indikator verbundenen Chartobjekte und gibt die MA-Handles frei, um Ressourcen freizugeben. Zusammen sorgen diese Funktionen dafür, dass der EA sauber startet und beendet wird, ohne Unordnung zu hinterlassen oder unnötig Speicher zu verbrauchen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Update only on new bar or every 10 seconds if(TimeCurrent() - lastUpdateTime < 10) return; lastUpdateTime = TimeCurrent(); // Get MA values if(CopyBuffer(higherTFHandle, 0, 0, 3, higherTFMA) < 3 || CopyBuffer(lowerTF1Handle, 0, 0, 3, lowerTF1MA) < 3 || CopyBuffer(lowerTF2Handle, 0, 0, 3, lowerTF2MA) < 3) { Print("Error copying indicator buffers"); return; } // Get current prices double higherTFPrice = iClose(_Symbol, HigherTF, 0); double lowerTF1Price = iClose(_Symbol, LowerTF1, 0); double lowerTF2Price = iClose(_Symbol, LowerTF2, 0); // Determine higher timeframe bias int higherTFBias = GetHigherTFBias(higherTFPrice, higherTFMA[0]); // Determine lower timeframe structures bool lowerTF1Bullish = IsBullishStructure(LowerTF1, SwingLookback); bool lowerTF1Bearish = IsBearishStructure(LowerTF1, SwingLookback); bool lowerTF2Bullish = IsBullishStructure(LowerTF2, SwingLookback); bool lowerTF2Bearish = IsBearishStructure(LowerTF2, SwingLookback); // Check for breakouts (BOS) bool lowerTF1Breakout = HasBreakout(LowerTF1, SwingLookback, higherTFBias); bool lowerTF2Breakout = HasBreakout(LowerTF2, SwingLookback, higherTFBias); // Determine final sentiment previousSentiment = currentSentiment; currentSentiment = DetermineSentiment( higherTFBias, lowerTF1Bullish, lowerTF1Bearish, lowerTF1Breakout, lowerTF2Bullish, lowerTF2Bearish, lowerTF2Breakout ); // Update panel if enabled if(ShowPanel) UpdatePanel(higherTFBias, currentSentiment); // Check if we should trade CheckForTradingOpportunity(); if(UseTrailingStop) ManageOpenTrades(); }
Die Funktion OnTick() ist die zentrale Ausführungsschleife des Expert Advisors und wird jedes Mal ausgeführt, wenn der Markt einen neuen Tick erzeugt. Um die Leistung zu optimieren, wird sie nur einmal alle 10 Sekunden oder bei einem neuen Balken aktualisiert, um unnötige Berechnungen zu vermeiden. Zunächst werden die Werte des gleitenden Durchschnitts aus den vordefinierten Zeitrahmen mit Hilfe von CopyBuffer abgerufen und mit den aktuellen Preisen verglichen, die mit iClose() ermittelt wurden. Auf der Grundlage dieser Daten wird die Tendenz in den höheren Zeitrahmen ermittelt, während in den niedrigeren Zeitrahmen Auf oder Abwärtsstrukturen sowie potenzielle Ausbrüche identifiziert werden.
Die Funktion bestimmt dann die allgemeine Marktstimmung, indem sie diese Bedingungen kombiniert, aktualisiert das Chart-Panel, falls es aktiviert ist, und prüft auf mögliche Handelsmöglichkeiten. Wenn Trailing Stops aktiviert sind, verwaltet es offene Handelsgeschäfte dynamisch, um Gewinne zu schützen. Dadurch wird sichergestellt, dass der EA kontinuierlich die Stimmung analysiert, die Visualisierungen aktualisiert und die Handelsgeschäfte strukturiert und automatisiert ausführt.
//+------------------------------------------------------------------+ //| Determine higher timeframe bias | //+------------------------------------------------------------------+ int GetHigherTFBias(double price, double maValue) { double deviation = MathAbs(price - maValue) / maValue; if(price > maValue && deviation > ATRThreshold) return 1; // Bullish else if(price < maValue && deviation > ATRThreshold) return -1; // Bearish else return 0; // Neutral } //+------------------------------------------------------------------+ //| Check for bullish structure (HH, HL) | //+------------------------------------------------------------------+ bool IsBullishStructure(ENUM_TIMEFRAMES tf, int lookback) { // Get swing highs and lows int swingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback * 2, 1); int swingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback * 2, 1); // Get previous swings for comparison int prevSwingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback, lookback + 1); int prevSwingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback, lookback + 1); if(swingHighIndex == -1 || swingLowIndex == -1 || prevSwingHighIndex == -1 || prevSwingLowIndex == -1) return false; double swingHigh = iHigh(_Symbol, tf, swingHighIndex); double swingLow = iLow(_Symbol, tf, swingLowIndex); double prevSwingHigh = iHigh(_Symbol, tf, prevSwingHighIndex); double prevSwingLow = iLow(_Symbol, tf, prevSwingLowIndex); // Check for higher high and higher low return (swingHigh > prevSwingHigh && swingLow > prevSwingLow); } //+------------------------------------------------------------------+ //| Check for bearish structure (LH, LL) | //+------------------------------------------------------------------+ bool IsBearishStructure(ENUM_TIMEFRAMES tf, int lookback) { // Get swing highs and lows int swingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback * 2, 1); int swingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback * 2, 1); // Get previous swings for comparison int prevSwingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback, lookback + 1); int prevSwingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback, lookback + 1); if(swingHighIndex == -1 || swingLowIndex == -1 || prevSwingHighIndex == -1 || prevSwingLowIndex == -1) return false; double swingHigh = iHigh(_Symbol, tf, swingHighIndex); double swingLow = iLow(_Symbol, tf, swingLowIndex); double prevSwingHigh = iHigh(_Symbol, tf, prevSwingHighIndex); double prevSwingLow = iLow(_Symbol, tf, prevSwingLowIndex); // Check for lower high and lower low return (swingHigh < prevSwingHigh && swingLow < prevSwingLow); } //+------------------------------------------------------------------+ //| Check for breakout of structure | //+------------------------------------------------------------------+ bool HasBreakout(ENUM_TIMEFRAMES tf, int lookback, int higherTFBias) { // Get recent swing points int swingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback, 1); int swingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback, 1); if(swingHighIndex == -1 || swingLowIndex == -1) return false; double swingHigh = iHigh(_Symbol, tf, swingHighIndex); double swingLow = iLow(_Symbol, tf, swingLowIndex); double currentPrice = iClose(_Symbol, tf, 0); // Check for breakout based on higher timeframe bias if(higherTFBias == 1) // Bullish bias - look for breakout above swing high return (currentPrice > swingHigh); else if(higherTFBias == -1) // Bearish bias - look for breakout below swing low return (currentPrice < swingLow); return false; }
Die Funktion GetHigherTFBias() vergleicht den aktuellen Kurs mit einem gleitenden Durchschnittswert auf dem höheren Zeitrahmen, um den vorherrschenden Trend zu bestimmen. Durch die Berechnung der relativen Abweichung zwischen den beiden Werten wird sichergestellt, dass kleine Schwankungen in der Nähe des gleitenden Durchschnitts als neutral behandelt werden. Wenn der Kurs deutlich über dem gleitenden Durchschnitt liegt, liefert die Funktion den Wert 1 für einen Aufwärtstrend, während ein Kurs deutlich darunter den Wert -1 für einen Abwärtstrend liefert. Ist keine der beiden Bedingungen erfüllt, wird die Vorspannung standardmäßig auf 0 gesetzt, was einem neutralen Zustand entspricht. Dadurch erhält der EA einen klaren Richtungskontext, bevor er die unteren Zeitrahmen analysiert.
Die Funktionen IsBullishStructure() und IsBearishStructure() untersuchen die hohen und tiefen Umkehrpunkte auf dem angegebenen Zeitrahmen, um strukturelle Muster zu bestätigen. Bei einer Aufwärtsstruktur prüft der Code, ob der jüngste hohe Umkehrpunkt höher ist als der vorherige und ob der jüngste tiefe Umkehrpunkt ebenfalls höher ist als der vorherige, was auf höhere Hochs und höhere Tiefs hinweist (HH, HL). Umgekehrt bestätigt die Abwärtsprüfung niedrigere Hochs und niedrigere Tiefs (LH, LL). Diese strukturellen Validierungen fügen eine weitere Bestätigungsebene hinzu und stellen sicher, dass die Handelsgeschäfte nicht nur mit der Tendenz des gleitenden Durchschnitts, sondern auch mit der tatsächlichen Marktstruktur übereinstimmen.
Die Funktion HasBreakout() sucht nach einem Ausbruch des Kurses aus den letzten Umkehrebenen in Richtung der höheren Zeitrahmen-Bias. Bei einem Aufwärtstrend wird beispielsweise bestätigt, ob der aktuelle Kurs das jüngste Hoch durchbrochen hat, was auf eine Fortsetzung des Trends hindeutet. Bei einer Abwärtstrend wird geprüft, ob der Kurs unter den letzten Umkehrpunkt gefallen ist. Liegt kein klarer Trend vor, wird kein Ausbruch als gültig angesehen. Diese Funktion verhindert verfrühte Handelsgeschäfte, indem sie sicherstellt, dass ein Einstieg nur dann erfolgt, wenn die Preisbewegung sowohl mit der Struktur als auch mit dem Momentum auf höheren Zeitebenen übereinstimmt, was die Zuverlässigkeit der Signale erhöht.
//+------------------------------------------------------------------+ //| Determine final sentiment | //+------------------------------------------------------------------+ int DetermineSentiment(int higherTFBias, bool tf1Bullish, bool tf1Bearish, bool tf1Breakout, bool tf2Bullish, bool tf2Bearish, bool tf2Breakout) { // Bullish sentiment if(higherTFBias == 1 && tf1Bullish && tf2Bullish) return 1; // Bullish // Bearish sentiment if(higherTFBias == -1 && tf1Bearish && tf2Bearish) return -1; // Bearish // Risk-on sentiment if(higherTFBias == 1 && (tf1Breakout || tf2Breakout)) return 2; // Risk-On // Risk-off sentiment if(higherTFBias == -1 && (tf1Breakout || tf2Breakout)) return -2; // Risk-Off // Neutral/choppy sentiment return 0; // Neutral } //+------------------------------------------------------------------+ //| Check for trading opportunity | //+------------------------------------------------------------------+ void CheckForTradingOpportunity() { // Check if enough time has passed since last trade if(TimeCurrent() - lastTradeTime < PeriodSeconds(_Period) * MinBarsBetweenTrades) return; // Check if sentiment has changed if(currentSentiment == previousSentiment) return; // Close existing positions if sentiment changed significantly if((previousSentiment > 0 && currentSentiment < 0) || (previousSentiment < 0 && currentSentiment > 0)) { CloseAllPositions(); } // Execute new trades based on sentiment switch(currentSentiment) { case 1: // Bullish - Buy case 2: // Risk-On - Buy ExecuteTrade(ORDER_TYPE_BUY, _Symbol); lastTradeTime = TimeCurrent(); break; case -1: // Bearish - Sell case -2: // Risk-Off - Sell ExecuteTrade(ORDER_TYPE_SELL, _Symbol); lastTradeTime = TimeCurrent(); break; case 0: // Neutral - Close all positions CloseAllPositions(); break; } } //+------------------------------------------------------------------+ //| Execute trade with risk parameters | //+------------------------------------------------------------------+ void ExecuteTrade(ENUM_ORDER_TYPE tradeType, string symbol) { double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double price = (tradeType == ORDER_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID); // Convert StopLoss and TakeProfit from pips to actual price distances double sl_distance = StopLossPips * point; double tp_distance = TakeProfitPips * point; double sl = (tradeType == ORDER_TYPE_BUY) ? price - sl_distance : price + sl_distance; double tp = (tradeType == ORDER_TYPE_BUY) ? price + tp_distance : price - tp_distance; trade.PositionOpen(symbol, tradeType, LotSize, price, sl, tp, NULL); }
Die Funktion DetermineSentiment() konsolidiert Signale aus höheren und niedrigeren Zeitrahmen zu einer einzigen Sentiment-Klassifizierung. Wenn der höhere Zeitrahmen steigt und beide niedrigeren Zeitrahmen Aufwärtsstrukturen bestätigen, gibt die Funktion ein Aufwärtsstimmung zurück. Ähnlich verhält es sich, wenn sich alle Bedingungen auf der bärischen Seite ausrichten, was auf eine Abwärtsstimmung hindeutet. Wenn der höhere Zeitrahmen steigt ist, aber nur Ausbrüche auf den niedrigeren Zeitrahmen auftreten, wird die Stimmung als risikofreudig eingestuft, während ein Abwärtstrend mit Ausbrüchen ein risikoarmes Verhalten signalisiert. Wenn keine dieser Bedingungen erfüllt ist, wird die Funktion auf neutral zurückgesetzt, was einen unruhigen oder unentschlossenen Markt darstellt. Diese strukturierte Hierarchie stellt sicher, dass die Stimmung konsistent und kontextabhängig ist und sowohl Trend- als auch Ausbruchsbedingungen widerspiegelt.
Die Funktion CheckForTradingOpportunity() reagiert auf die Stimmungssignale, um den Handel zu automatisieren. Sie erzwingt Regeln wie zeitliche Abstände zwischen den Geschäften (MinBarsBetweenTrades) und reagiert nur, wenn sich die Stimmung gegenüber dem vorherigen Zustand ändert. Wenn die Stimmung umschlägt, werden bestehende Positionen geschlossen, bevor neue Handelsgeschäfte platziert werden, um eine Anpassung an die aktuellen Marktaussichten zu gewährleisten. Je nach Stimmung führt der EA Kaufaufträge für steigende oder risikofreudige Bedingungen aus, Verkaufsaufträge für fallende oder risikoarme Bedingungen, oder er schließt alle Handelsgeschäfte während neutraler Phasen. Die Funktion ExecuteTrade() verwaltet die Orderplatzierung mit Risikokontrollen, indem sie Stop-Loss- und Take-Profit-Levels in Form von Preisen berechnet, während die Hilfsfunktion PipsToPrice() die Kompatibilität mit Symbolen sicherstellt, die unterschiedliche Pip-Formate verwenden (z.B. 3/5-stellige Preise). Zusammen bilden diese Funktionen einen vollständigen Kreislauf von der Stimmungserkennung bis zur automatisierten, risikogesteuerten Handelsausführung.
//+------------------------------------------------------------------+ //| Trailing stop function | //+------------------------------------------------------------------+ void ManageOpenTrades() { if(!UseTrailingStop) return; int total = PositionsTotal(); for(int i = total - 1; i >= 0; i--) { // get ticket (PositionGetTicket returns ulong; it also selects the position) ulong ticket = PositionGetTicket(i); // correct usage. :contentReference[oaicite:3]{index=3} if(ticket == 0) continue; // ensure the position is selected (recommended) if(!PositionSelectByTicket(ticket)) continue; // Optional: only operate on same symbol or your EA's magic number if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; // if(PositionGetInteger(POSITION_MAGIC) != MyMagicNumber) continue; // read position properties AFTER selecting double open_price = PositionGetDouble(POSITION_PRICE_OPEN); double current_price= PositionGetDouble(POSITION_PRICE_CURRENT); // current price for the position. :contentReference[oaicite:4]{index=4} double current_sl = PositionGetDouble(POSITION_SL); double current_tp = PositionGetDouble(POSITION_TP); ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // pip size double pip_price = PipsToPrice(1); // profit in pips (use current_price returned above) double profit_price = (pos_type == POSITION_TYPE_BUY) ? (current_price - open_price) : (open_price - current_price); double profit_pips = profit_price / pip_price; if(profit_pips <= 0) continue; // get broker min stop distance (in price units) double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double stop_level_points = (double)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); // integer property. :contentReference[oaicite:5]{index=5} double stopLevelPrice = stop_level_points * point; // get market Bid/Ask for stop-level checks double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // ------------------------- // 1) Move to breakeven // ------------------------- if(profit_pips >= BreakEvenAtPips) { double breakeven = open_price; // small adjustment to help account for spread/commissions (optional) if(pos_type == POSITION_TYPE_BUY) breakeven += point; else breakeven -= point; // Check stop-level rules: for BUY SL must be >= (bid - stopLevelPrice) distance below bid if(pos_type == POSITION_TYPE_BUY) { if((bid - breakeven) >= stopLevelPrice) // allowed by server { if(breakeven > current_sl) // only move SL up { if(!trade.PositionModify(ticket, NormalizeDouble(breakeven, _Digits), current_tp)) PrintFormat("PositionModify failed (BE) ticket %I64u error %d", ticket, GetLastError()); } } } else // SELL { if((breakeven - ask) >= stopLevelPrice) { if(current_sl == 0.0 || breakeven < current_sl) // move SL down { if(!trade.PositionModify(ticket, NormalizeDouble(breakeven, _Digits), current_tp)) PrintFormat("PositionModify failed (BE) ticket %I64u error %d", ticket, GetLastError()); } } } } // end breakeven // ------------------------- // 2) Trailing in steps after TrailStartAtPips // ------------------------- if(profit_pips >= TrailStartAtPips) { double extra_pips = profit_pips - TrailStartAtPips; int step_count = (int)(extra_pips / TrailStepPips); // compute desired SL relative to open_price (as per your original request) double desiredOffsetPips = (double)(TrailStartAtPips + step_count * TrailStepPips); double new_sl_price; if(pos_type == POSITION_TYPE_BUY) { new_sl_price = open_price + PipsToPrice((int)desiredOffsetPips); // ensure new SL respects server min distance from current Bid if((bid - new_sl_price) < stopLevelPrice) new_sl_price = bid - stopLevelPrice; if(new_sl_price > current_sl) // only move SL up { if(!trade.PositionModify(ticket, NormalizeDouble(new_sl_price, _Digits), current_tp)) PrintFormat("PositionModify failed (Trail Buy) ticket %I64u error %d", ticket, GetLastError()); } } else // SELL { new_sl_price = open_price - PipsToPrice((int)desiredOffsetPips); // ensure new SL respects server min distance from current Ask if((new_sl_price - ask) < stopLevelPrice) new_sl_price = ask + stopLevelPrice; if(current_sl == 0.0 || new_sl_price < current_sl) // only move SL down (more profitable) { if(!trade.PositionModify(ticket, NormalizeDouble(new_sl_price, _Digits), current_tp)) PrintFormat("PositionModify failed (Trail Sell) ticket %I64u error %d", ticket, GetLastError()); } } } } } //+------------------------------------------------------------------+ //| Close all positions | //+------------------------------------------------------------------+ void CloseAllPositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == MagicNumber) { MqlTradeRequest request = {}; MqlTradeResult result = {}; request.action = TRADE_ACTION_DEAL; request.symbol = _Symbol; request.volume = PositionGetDouble(POSITION_VOLUME); request.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; request.price = SymbolInfoDouble(_Symbol, (request.type == ORDER_TYPE_BUY) ? SYMBOL_ASK : SYMBOL_BID); request.deviation = Slippage; request.magic = MagicNumber; request.comment = "MarketSentEA Close"; if(OrderSend(request, result)) { if(result.retcode == TRADE_RETCODE_DONE) Print("Position closed successfully. Ticket: ", ticket); else Print("Error closing position. Code: ", result.retcode); } } } } }
Dieser Teil des EA konzentriert sich auf zwei wichtige Risikomanagement-Mechanismen: Trailing-Stop-Management und erzwungene Positionsschließung. Die Funktion ManageOpenTrades() stellt zunächst sicher, dass Trailing-Stops nur angewendet werden, wenn sie aktiviert sind, und durchläuft dann alle offenen Positionen. Er prüft die Rentabilität in Pips, wendet einen Breakeven-Stopp an, sobald der Mindestgewinn erreicht ist, und fährt mit schrittweisem Trailing fort, wenn sich die Position weiter in den Gewinn bewegt. Beschränkungen des Brokers bezüglich der Stop-Level und Spread-Überlegungen sind integriert, um sicherzustellen, dass alle Stop-Modifikationen gültig und sicher sind.
Zusammen bilden diese Funktionen ein robustes Ausstiegs- und Schutzsystem für Handelsgeschäfte, das das Risiko reduziert und gleichzeitig die Gewinne maximiert. Durch die Kombination der Logik eines Break-Even, dem schrittweisem Trailing und einem zuverlässigen Mechanismus zur Schließung aller Positionen, wenn die Stimmung oder die Strategieregeln dies erfordern, kann der EA das Kapital unter ungünstigen Bedingungen schützen und Gewinne sichern, wenn sich die Handelsgeschäfte günstig entwickeln. Dies verbessert letztlich die Konsistenz, die Widerstandsfähigkeit und die langfristige Rentabilität der Strategie.
//+------------------------------------------------------------------+ //| Calculate lot size with risk management | //+------------------------------------------------------------------+ double CalculateLotSize(double stopLossPrice) { if(!UseRiskManagement) return LotSize; double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double riskAmount = accountBalance * RiskPercent / 100.0; double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); // Calculate stop loss in points double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double slPoints = MathAbs(currentPrice - stopLossPrice) / point; // Calculate lot size double lotSize = riskAmount / (slPoints * tickValue); // Normalize lot size double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); lotSize = MathMax(minLot, MathMin(maxLot, lotSize)); lotSize = MathRound(lotSize / lotStep) * lotStep; return lotSize; } //+------------------------------------------------------------------+ //| Helper: convert timeframe to string | //+------------------------------------------------------------------+ string TFtoString(int tf) { switch(tf) { case PERIOD_M1: return "M1"; case PERIOD_M5: return "M5"; case PERIOD_M15: return "M15"; case PERIOD_M30: return "M30"; case PERIOD_H1: return "H1"; case PERIOD_H4: return "H4"; case PERIOD_D1: return "D1"; case PERIOD_W1: return "W1"; case PERIOD_MN1: return "MN"; default: return "TF?"; } } //+------------------------------------------------------------------+ //| Create information panel | //+------------------------------------------------------------------+ void CreatePanel() { //--- background panel string bg = indicatorName + "_BG"; ObjectCreate(0, bg, OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, bg, OBJPROP_XDISTANCE, PanelX); ObjectSetInteger(0, bg, OBJPROP_YDISTANCE, PanelY); ObjectSetInteger(0, bg, OBJPROP_XSIZE, 200); ObjectSetInteger(0, bg, OBJPROP_YSIZE, 120); ObjectSetInteger(0, bg, OBJPROP_CORNER, PanelCorner); ObjectSetInteger(0, bg, OBJPROP_BGCOLOR, clrBlack); ObjectSetInteger(0, bg, OBJPROP_BORDER_TYPE, BORDER_FLAT); ObjectSetInteger(0, bg, OBJPROP_BORDER_COLOR, clrWhite); ObjectSetInteger(0, bg, OBJPROP_BACK, true); ObjectSetInteger(0, bg, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, bg, OBJPROP_HIDDEN, true); ObjectSetInteger(0, bg, OBJPROP_ZORDER, 0); //--- title string title = indicatorName + "_Title"; ObjectCreate(0, title, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, title, OBJPROP_XDISTANCE, PanelX + 10); ObjectSetInteger(0, title, OBJPROP_YDISTANCE, PanelY + 10); ObjectSetInteger(0, title, OBJPROP_CORNER, PanelCorner); ObjectSetString (0, title, OBJPROP_TEXT, "Market Sentiment EA"); ObjectSetInteger(0, title, OBJPROP_COLOR, clrWhite); ObjectSetString (0, title, OBJPROP_FONT, FontFace); ObjectSetInteger(0, title, OBJPROP_FONTSIZE, FontSize); ObjectSetInteger(0, title, OBJPROP_BACK, false); ObjectSetInteger(0, title, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, title, OBJPROP_HIDDEN, true); ObjectSetInteger(0, title, OBJPROP_ZORDER, 1); //--- timeframe labels + sentiment placeholders string timeframes[3] = { TFtoString(HigherTF), TFtoString(LowerTF1), TFtoString(LowerTF2) }; for(int i=0; i<3; i++) { string tfLabel = indicatorName + "_TF" + IntegerToString(i); ObjectCreate(0, tfLabel, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, tfLabel, OBJPROP_XDISTANCE, PanelX + 10); ObjectSetInteger(0, tfLabel, OBJPROP_YDISTANCE, PanelY + 30 + i * 20); ObjectSetInteger(0, tfLabel, OBJPROP_CORNER, PanelCorner); ObjectSetString (0, tfLabel, OBJPROP_TEXT, timeframes[i] + ":"); ObjectSetInteger(0, tfLabel, OBJPROP_COLOR, clrLightGray); ObjectSetString (0, tfLabel, OBJPROP_FONT, FontFace); ObjectSetInteger(0, tfLabel, OBJPROP_FONTSIZE, FontSize); ObjectSetInteger(0, tfLabel, OBJPROP_BACK, false); ObjectSetInteger(0, tfLabel, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, tfLabel, OBJPROP_HIDDEN, true); ObjectSetInteger(0, tfLabel, OBJPROP_ZORDER, 1); string sentLabel = indicatorName + "_Sentiment" + IntegerToString(i); ObjectCreate(0, sentLabel, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, sentLabel, OBJPROP_XDISTANCE, PanelX + 100); ObjectSetInteger(0, sentLabel, OBJPROP_YDISTANCE, PanelY + 30 + i * 20); ObjectSetInteger(0, sentLabel, OBJPROP_CORNER, PanelCorner); ObjectSetString (0, sentLabel, OBJPROP_TEXT, "N/A"); ObjectSetInteger(0, sentLabel, OBJPROP_COLOR, NeutralColor); ObjectSetString (0, sentLabel, OBJPROP_FONT, FontFace); ObjectSetInteger(0, sentLabel, OBJPROP_FONTSIZE, FontSize); ObjectSetInteger(0, sentLabel, OBJPROP_BACK, false); ObjectSetInteger(0, sentLabel, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, sentLabel, OBJPROP_HIDDEN, true); ObjectSetInteger(0, sentLabel, OBJPROP_ZORDER, 1); } //--- final sentiment label string fnl = indicatorName + "_Final"; ObjectCreate(0, fnl, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, fnl, OBJPROP_XDISTANCE, PanelX + 10); ObjectSetInteger(0, fnl, OBJPROP_YDISTANCE, PanelY + 100); ObjectSetInteger(0, fnl, OBJPROP_CORNER, PanelCorner); ObjectSetString (0, fnl, OBJPROP_TEXT, "Final: Neutral"); ObjectSetInteger(0, fnl, OBJPROP_COLOR, NeutralColor); ObjectSetString (0, fnl, OBJPROP_FONT, FontFace); ObjectSetInteger(0, fnl, OBJPROP_FONTSIZE, FontSize + 2); ObjectSetInteger(0, fnl, OBJPROP_BACK, false); ObjectSetInteger(0, fnl, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, fnl, OBJPROP_HIDDEN, true); ObjectSetInteger(0, fnl, OBJPROP_ZORDER, 1); } //+------------------------------------------------------------------+ //| Update information panel | //+------------------------------------------------------------------+ void UpdatePanel(int higherTFBias, int sentiment) { // Update higher timeframe sentiment string higherTFSentiment = "Neutral"; color higherTFColor = NeutralColor; if(higherTFBias == 1) { higherTFSentiment = "Bullish"; higherTFColor = BullishColor; } else if(higherTFBias == -1) { higherTFSentiment = "Bearish"; higherTFColor = BearishColor; } ObjectSetString(0, indicatorName + "_Sentiment0", OBJPROP_TEXT, higherTFSentiment); ObjectSetInteger(0, indicatorName + "_Sentiment0", OBJPROP_COLOR, higherTFColor); // Update final sentiment string finalSentiment = "Neutral"; color finalColor = NeutralColor; switch(sentiment) { case 1: finalSentiment = "Bullish"; finalColor = BullishColor; break; case -1: finalSentiment = "Bearish"; finalColor = BearishColor; break; case 2: finalSentiment = "Risk-On"; finalColor = RiskOnColor; break; case -2: finalSentiment = "Risk-Off"; finalColor = RiskOffColor; break; default: finalSentiment = "Neutral"; finalColor = NeutralColor; } ObjectSetString(0, indicatorName + "_Final", OBJPROP_TEXT, "Final: " + finalSentiment); ObjectSetInteger(0, indicatorName + "_Final", OBJPROP_COLOR, finalColor); }
Die Funktion CalculateLotSize ermöglicht eine dynamische Positionsgrößenbestimmung auf der Grundlage von Kontostand und Risikoprozentsatz. Anstatt eine feste Losgröße zu verwenden, wird das angemessene Volumen berechnet, indem der Abstand zwischen Einstieg und Stop-Loss gemessen, in Punkte umgerechnet und das Risikokapital durch den potenziellen Verlust pro Pip geteilt wird. Dadurch wird sichergestellt, dass jeder Handel unabhängig von der Volatilität oder dem Preis des Instruments einen gleichbleibenden Teil des Eigenkapitals riskiert, während die endgültige Losgröße an die Broker-Limits wie Mindest-, Höchst- und Schrittweite angepasst wird.
Die Hilfsfunktion TFtoString übersetzt numerische Zeitrahmenkonstanten in menschenlesbare Zeichenketten. Dies ist wichtig für die Anzeige aussagekräftiger Informationen in Panels, Dashboards oder Protokollen, sodass Händler schnell erkennen können, auf welchen Zeitrahmen oder welche Signale Bezug genommen wird. Durch die Umwandlung dieser ganzzahligen Codes in Klartext, z. B. „M5“ oder „H1“, wird der Code nutzerfreundlicher und vermeidet Verwirrung bei der Verfolgung mehrerer Zeitrahmen.
Die Funktionen CreatePanel und UpdatePanel behandeln die visuelle Schnittstelle des EA. CreatePanel initialisiert ein sauberes Dashboard mit Platzhaltern für Hintergrund, Titel und Stimmung für jeden überwachten Zeitraum sowie einen abschließenden Stimmungsstatus. UpdatePanel aktualisiert diese Kennzeichnung dynamisch mit der aktuellen Tendenz des höheren Zeitrahmens und der endgültigen berechneten Stimmung, wobei sowohl der Text als auch die Farbe geändert werden, um die Zustände aufwärts, abwärts, mehr und weniger Risiko oder neutral visuell darzustellen. Zusammen bilden sie ein intuitives Live-Überwachungspanel, das es Händlern ermöglicht, die Marktbedingungen schnell einzuschätzen, ohne Rohdaten zu scannen.
Backtest-Ergebnisse
Die Backtests wurde für den 1H-Zeitrahmen über ein etwa zweimonatiges Testfenster (17. Februar 2025 bis 06. Mai 2025) mit den folgenden Einstellungen bewertet:



Schlussfolgerung
Zusammenfassend lässt sich sagen, dass wir einen vollständigen Rahmen zur Automatisierung des Marktstimmungsindikators entworfen und implementiert haben. Dazu gehörte die Entwicklung einer Logik zur Erkennung von Stimmungen über mehrere Zeitrahmen hinweg, die Definition klarer Kategorien wie aufwärts, abwärts, mehr und weniger Risiko oder neutral sowie die Kodierung der Handelsausführungsregeln, um auf diese Signale zu reagieren. Darüber hinaus haben wir wesentliche Funktionen wie Risikomanagement durch dynamische Losgrößenbestimmung, Trailing-Stop-Funktionen zur Gewinnsicherung und ein Echtzeit-Informationspanel zur visuellen Darstellung der Stimmungslage integriert. Insgesamt verwandelten diese Komponenten den Indikator in ein vollautomatisches Handelssystem, das sowohl zur Analyse als auch zur Ausführung fähig ist.
Zusammenfassend lässt sich sagen, dass diese Automatisierung den Händlern hilft, indem sie ihnen emotionale Entscheidungen abnimmt und für Konsistenz bei der Durchführung und Verwaltung von Geschäften sorgt. Durch die Kombination von Stimmungsanalyse, strukturierten Handelsregeln und integriertem Risikomanagement können Händler den Markt mit einem disziplinierteren Ansatz steuern. Das System bietet eine übersichtliche visuelle Schnittstelle für Transparenz, während es den Handel automatisch in Übereinstimmung mit der Stimmung ausführt. So können Händler Zeit sparen, Fehler minimieren und mit einem professionellen Tool arbeiten, das sich an wechselnde Marktbedingungen anpasst.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19609
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.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Beherrschung der Fair Value Gaps: Bildung, Logik und automatisierter Handel von Ausbrüchen und Marktstrukturverschiebungen
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Aufbau von KI-gesteuerten Handelssystemen in MQL5 (Teil 1): Implementierung der JSON-Verarbeitung für KI-APIs
- 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.