Entwicklung des Price Action Analysis Toolkit (Teil 37): Sentiment Tilt Meter
Einführung
Wir befinden uns jetzt in Teil 37 der Reihe über die Entwicklung des Price Action Analysis Toolkit. Mein übergreifendes Ziel war es immer, praktische Tools zu entwickeln, die Händler bei der Interpretation des Marktverhaltens unterstützen – und gegebenenfalls Aspekte dieser Analyse zu automatisieren. Bis heute haben wir eigenständige MQL5-Dienstprogramme, externe Integrationen und Hybridlösungen eingeführt. Heute erweitern wir das Toolkit um eine weitere wertvolle und pragmatische Ergänzung.
Datenvisualisierungen werden oft durch sich überschneidende Beschriftungen, zu viele Indikatoren und Ad-hoc-Anmerkungen unübersichtlich, wodurch die zugrundeliegenden Signalveränderungen und der Grundgedanke verdeckt werden. Diese Zweideutigkeit erschwert die Entscheidungsfindung, untergräbt das Vertrauen in regelbasierte Systeme und erschwert die Erstellung zuverlässiger Prüfpfade, die für eine disziplinierte Automatisierung unerlässlich sind.
Das Sentiment Tilt Meter (STM) geht dieses Problem an, indem es die Richtungsmetriken auf Kerzenebene über konfigurierbare Zeitrahmen zu einem einheitlichen, geglätteten Skalarwert von -100 bis +100 aggregiert. Dieser Wert wird in einem kompakten, kollisionsvermeidenden Armaturenbrett-Layout angezeigt. Dauerhafte Anmerkungen, wie z. B. Pfeile und Textbeschriftungen, sind genau mit den Zeitstempeln der Bar-Close verankert, sodass jeder Alarm unabhängig nach Zeitstempel und Kursniveau überprüft werden kann.
STM bietet explizite, parametrisierbare Regeln – wie z. B. Flip-Schwellenwerte, Größe und Dauer des Vorzeichenwechsels, Impulskriterien und Gewichtung pro Zeitrahmen – die es dem Nutzer ermöglichen, die Empfindlichkeit einzustellen und den Kompromiss zwischen Rauschunterdrückung und Reaktionsfähigkeit auszugleichen.
Wir beginnen mit einem Überblick über die Strategie, gefolgt von der MQL5-Implementierung, präsentieren die Tests und Ergebnisse und schließen mit einer kurzen Zusammenfassung. Siehe die folgende Inhaltsübersicht:
Überblick über die Strategie
Die Märkte sind verrauscht, und Rauschen ist der größte Feind einer konsistenten Entscheidungsfindung. Die Indikatoren häufen sich, die Bezeichnungen überschneiden sich, und es wird schnell unmöglich zu erkennen, was den Kurs bewegt hat und warum. Das Sentiment Tilt Meter (STM) wurde entwickelt, um diesen Nebel zu durchdringen: Es liefert eine einzige, überprüfbare Anzeige der kurzfristigen Marktneigung, zeigt die Beweise auf dem Chart und ermöglicht Ihnen, disziplinierte Entscheidungen zu treffen, ohne zu raten.
Die Stimmung ist die kurzfristige Tendenz des Marktes, d. h. der Netto-Kauf- oder Verkaufsdruck der letzten Kerzen. STM misst diese Neigung direkt anhand der Kursentwicklung, indem es jede geschlossene Kerze in eine kleine, interpretierbare Punktzahl umwandelt. Die Mikro-Merkmale sind:
- Kerzenkörper-Verhältnis (Candle Body Ratio, CBR)
![]()
- Prozentsatz der geschlossenen Position (Close Position Percent, CPP)

- Volatilitätsangepasster Abstand (Volatility-Adjusted Distance, VAD)
![]()
wobei ATR die Basislinie der jüngsten, wahren Spanne ist und
Clamp ( x , a , b ) =max ( a , min ( b , x ) ) \operatorname{clamp}(x,a,b)=\max(a,\min(b,x)) clamp(x,a,b)=max(a,min(b,x)).
Der Mini-Score jeder Kerze ist eine gewichtete Summe (zum Beispiel)
![]()
Normiert auf [ − 1 , + 1 ] [-1, +1] [−1,+1] und dann mit einfachen Konfidenz- und ruhigen Markt-Faktoren multipliziert. Die Bewertungen pro Zeitrahmen werden über die gesamte Stichprobe gemittelt und mit Hilfe von Nutzergewichtungen über die Zeitrahmen hinweg verschmolzen,
![]()
und schließlich exponentiell geglättet:
![]()
Nachstehend finden Sie die Preis- und Punkt-Charts:

Das Punkt-Chart zeigt den geglätteten Stimmungswert im Zeitverlauf an, wobei jedes rohe Vorzeichenkreuz mit der kleinen Markierungen × markiert wird und akzeptierte Flip-Signale – nach Anwendung von Magnituden-, Persistenz- und Momentumfiltern – mit größeren Punktmarkierungen hervorgehoben werden. Anhand dieser Visualisierung lassen sich kleinere verrauschte Fluktuationen, die zu falschen Signalen führen könnten, ebenso leicht erkennen wie die von den Filtern zugelassenen echten Umkehrungen.
Das Preis-Chart zeigt die Position dieser akzeptierten Signale innerhalb der Preisreihen. Pfeile und „BUY“ oder „SELL“ werden genau am Signalpreis positioniert, sodass sofort überprüft werden kann, ob ein Signal mit wichtigen Preisstrukturen wie Unterstützungs- und Widerstandsniveaus, Trendrichtungen oder nahe gelegenen Schwankungen übereinstimmt.
Um akzeptiert zu werden, sollte der EA Signale erzeugen, die sofort nachvollziehbar sind (Zeit und Preis), über Backtests und kurze Vorwärtstests hinweg robust sein und offensichtliche Falschmeldungen auf ein Niveau reduzieren, das Sie als handelbar ansehen. Wenn diese Kriterien erfüllt sind, hat das STM seine Aufgabe erfüllt – es liefert eine klare, abstimmbare und überprüfbare Einschätzung der kurzfristigen Marktneigung, auf die Sie sich im Rahmen eines disziplinierten Prozesses verlassen können.
MQL5-Implementierung
Bevor wir mit dem Programmieren beginnen, sollten Sie sich zwei Minuten Zeit nehmen, um Ihre Umgebung richtig einzurichten – das spart später viel Zeit. Stellen Sie sicher, dass MetaTrader 5 (MetaTrader 5) und MetaEditor installiert sind. MetaEditor kann als eigenständige Anwendung gestartet oder über MetaTrader 5 aufgerufen werden; beide Optionen sind zulässig.
So bereiten Sie Ihren Expert Advisor (EA) vor:
- Öffnen Sie den MetaEditor.
- Navigieren Sie zu Datei → Neu → Expert Advisor (Vorlage).
- Fügen Sie Ihren EA-Quellcode in die neu erstellte Datei ein.
- Speichern Sie die Datei unter einem beschreibenden, aussagekräftigen Namen.
- Kompilieren Sie den Code, indem Sie F7 drücken.
Überprüfen Sie, ob die Kompilierung ohne Fehler oder Warnungen abgeschlossen wurde. Wenn Probleme gemeldet werden, überprüfen und korrigieren Sie den Code und kompilieren Sie ihn erneut, bis der Compiler ein fehlerfreies Ergebnis meldet.
Zurück in MetaTrader 5:
- Laden Sie den EA in den Strategy Tester oder fügen Sie ihn direkt in einen Chart ein.
- Überwachen Sie das Dashboard, die Protokolle und die Signale.
- Auf den Registerkarten Experten und Journal finden Sie Laufzeitmeldungen und Debugging-Informationen.
Mit dieser Einrichtung wird sichergestellt, dass Ihre Umgebung für eine effiziente Entwicklung und Prüfung korrekt konfiguriert ist. Von diesem Punkt aus werden wir Schritt für Schritt vorgehen, um den Expert Advisor Sentiment Tilt Meter (STM) zu erstellen, wobei wir Sie durch jede Phase des Entwicklungsprozesses führen.
Im Kopf- und Metadatenblock werden die EA-Identität, das Copyright, der Link zum Autorenprofil und der Kompilierungsmodus (#property strict) angegeben. Diese Zeilen sind rein deklarativ, aber wichtig: Sie betten Versionskontrolle und Attribution in das kompilierte Programm ein und ermöglichen strengere Kompilierzeitprüfungen. Direkt danach bindet der EA die Standard-Handelshilfe Trade.mqh ein, sodass der Code Zugriff auf die CTrade-Klasse und andere handelsbezogene Helfer hat. Obwohl diese spezielle Datei CTrade g_trade nur spärlich (oder im gezeigten Code gar nicht) verwendet, ist sie durch die Einbindung der Bibliothek bereit, später ohne Neuordnung von Aufträgen zu platzieren.
//+------------------------------------------------------------------+ //| STM EA| //| Copyright 2025, MetaQuotes Ltd.| //| https://www.mql5.com/en/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict #include <Trade\Trade.mqh>
Der Eingabeblock definiert alle vom Nutzer konfigurierbaren Parameter. Die Eingaben sind nach Zweck gruppiert: Auswahl des Zeitrahmens, Abstimmung des Signalalgorithmus, visuelles Layout und Stil des Panels, Ton-/Druckwarnungen und Verhalten der Pfeile/Marker. Mit der primären TF und den optionalen zusätzlichen TFs kann der EA Signale aus mehreren Zeitrahmen fusionieren; die Gewichte (InpWeightPrimary, InpWeightExtra1, InpWeightExtra2) steuern das Fusionsverhältnis. Eine Glättung (InpSmoothAlpha) regelt die exponentielle Glättung der rohen Fusionsergebnisse; Schwellenwertvariablen (z. B. InpFlipThreshold, InpPositiveZone, InpNegativeZone) legen fest, wann der EA einen großen Flip betrachtet oder wann er in eine positive/negative Zone eingetreten ist.
Visuelle Eingaben steuern das Dashboard auf dem Chart: Größe, Ecken, Schriftarten, Farben, Anzahl der Histogrammbalken und Auffüllung des Bereichs. Mit den Pfeil-/Marker-Eingaben können Sie Signale, die auf dem Chartplatziert werden, aktivieren oder abstimmen, und mehrere Sicherheitsprüfungen (Mindestbalken, Größe des Vorzeichenwechsels, Momentum-Anforderungen) reduzieren Rauschen und falsche Umkehrungen. Alle Eingaben werden explizit eingegeben, sodass sie im EA-Dialog erscheinen und ohne Editieren von Code angepasst werden können.
// --- User inputs (layout / visuals tuned) ---------------------------- input ENUM_TIMEFRAMES InpTFPrimary = PERIOD_M5; input bool InpUseMultiTF = true; input ENUM_TIMEFRAMES InpExtraTF1 = PERIOD_M15; input ENUM_TIMEFRAMES InpExtraTF2 = PERIOD_H1; input int InpLookback = 20; input int InpSSWindow = 5; input int InpATRPeriod = 14; input double InpATRMultiplier = 1.0; input double InpVolQuietThreshold = 0.4; input int InpHistogramMaxBars = 24; input int InpPanelPadding = 10; input int InpPanelCorner = CORNER_LEFT_UPPER; input int InpPanelX = 5; input int InpPanelY = 25; input string InpFont = "Arial"; input int InpTitleFontSize = 12; input int InpScoreFontSize = 18; input int InpSmallFontSize = 9; input color InpPanelColor = clrBlack; input int InpPanelAlpha = 200; input bool InpEnableSound = true; input string InpSoundFile = "alert.wav"; input bool InpEnablePush = false; input double InpWeightPrimary = 0.6; input double InpWeightExtra1 = 0.2; input double InpWeightExtra2 = 0.2; input double InpSmoothAlpha = 0.28; input double InpFlipThreshold = 60.0; input double InpPositiveZone = 30.0; input double InpNegativeZone = -30.0; input int InpMinBarsForSignal = 3; // arrows / marker options input bool InpShowArrows = true; input int InpArrowFontSize = 16; input int InpArrowOffsetPoints = 8; input int InpMaxSignalsToKeep = 50; input bool InpSignalOnSignFlip = true; input double InpMinSignFlipAbs = 6.0; input int InpSignFlipHoldBars = 1; input bool InpRequireMomentum = true; input double InpMinMomentum = 0.5; //+------------------------------------------------------------------+Es folgen Konstanten und das Globale. Der EA konstruiert einen eindeutigen g_prefix pro Chart (Symbol + Chart-ID), um On-Chart-Objekte zu benennen und Kollisionen mit anderen EAs oder manuellen Objekten zu vermeiden. Es gibt Handles für ATR-Indikatoren für jeden Zeitrahmen, Puffer für das Histogramm (g_hist_buffer) und Indizes zur Verwaltung der zirkulären Speicherung. Globale Werte behalten auch den Status des geglätteten Wertes (g_smoothed_score, g_prev_smoothed), den letzten Alarmwert, der zur Erkennung von größeren Ausreißern (flips) verwendet wurde, und den aktuellen g_zone_state (1, -1 oder 0). CTradeg_trade wird deklariert, wenn der EA erweitert wird, um Aufträge zu platzieren. Durch das Speichern von g_last_signal_text und g_last_signal_time kann die Nutzeroberfläche die letzte Aktion auf dem Panel anzeigen.
// globals long g_chart_id = 0; string g_prefix = ""; string ui_bg_name = ""; string ui_shadow_name = ""; string ui_title_name = ""; string ui_score_name = ""; string ui_zone_name = ""; string ui_hist_base = ""; string ui_recent_name = ""; string ui_advice_name = ""; string ui_signal_name = ""; int g_atr_handle_primary = INVALID_HANDLE; int g_atr_handle_extra1 = INVALID_HANDLE; int g_atr_handle_extra2 = INVALID_HANDLE; double g_hist_buffer[]; int g_hist_idx = 0; int g_hist_count = 0; double g_smoothed_score = 0.0; double g_prev_smoothed = 0.0; double g_last_alert_score = 0.0; int g_zone_state = 0; CTrade g_trade; string g_last_signal_text = "None"; datetime g_last_signal_time = 0; const string BASE_HIST = "STM_HBAR_"; //+------------------------------------------------------------------+Es gibt ein paar kleine Dienstprogramme, die die Nutzeroberfläche und den Anzeigecode robuster machen. ARGB_uint setzt eine ARGB-Ganzzahl ohne Vorzeichen aus einem Alphawert und einer MQL-Farbe zusammen, sodass halbtransparente Rechteckhintergründe sauber angegeben werden können. EstimateTextWidth ist ein billiger Schätzer für die Pixelbreite von Zeichenketten, der auf der Schriftgröße und einem abgestimmten Faktor für die Zeichenbreite basiert; dies wird verwendet, um eine Überlappung der Beschriftung zu vermeiden, wenn das Zonenabzeichen und die aktuelle Zeichenkette platziert werden. Diese Annäherungen sind sinnvoll für ein On-Chart-Panel, bei dem das genaue Layout nicht entscheidend ist, Kollisionen aber unangenehm sind.
// ARGB helper - returns a color as unsigned int uint ARGB_uint(int a, color c) { uint u = ((uint)c) & 0x00FFFFFF; return ((((uint)a) & 0xFF) << 24) | u; } // approximate text width estimator (pixels) int EstimateTextWidth(string txt,int fontSize) { if(StringLen(txt) <= 0) return 6; double factor = 0.58; int w = (int)MathRound(StringLen(txt) * fontSize * factor); return MathMax(8, w); } //+------------------------------------------------------------------+DrawText und DrawCell sind sichere Wrapper zum Erstellen und Konfigurieren von Chart-Beschriftungen und Rechteckbeschriftungen. Sie zentralisieren das Muster der Objekterstellung und Eigenschaftseinstellung (Ecke, x/y-Abstand, Schriftart, Auswahl-Flag, z-Reihenfolge), sodass jedes UI-Label denselben Konventionen folgt. Durch die Verwendung dieser Hilfsprogramme werden doppelte Textbausteine vermieden und ein konsistentes Verhalten sichergestellt, wenn das Panel neu erstellt oder aktualisiert wird.
// safe DrawText - sets font explicitly and uses exact x/y void DrawText(string id,string txt,int x,int y,color clr,int sz) { if(ObjectFind(g_chart_id,id) < 0) ObjectCreate(g_chart_id,id,OBJ_LABEL,0,0,0); ObjectSetInteger(g_chart_id,id,OBJPROP_CORNER,InpPanelCorner); ObjectSetInteger(g_chart_id,id,OBJPROP_XDISTANCE,x); ObjectSetInteger(g_chart_id,id,OBJPROP_YDISTANCE,y); ObjectSetInteger(g_chart_id,id,OBJPROP_COLOR,(int)clr); ObjectSetInteger(g_chart_id,id,OBJPROP_FONTSIZE,sz); ObjectSetString(g_chart_id,id,OBJPROP_FONT,InpFont); ObjectSetString(g_chart_id,id,OBJPROP_TEXT,txt); ObjectSetInteger(g_chart_id,id,OBJPROP_SELECTABLE,false); ObjectSetInteger(g_chart_id,id,OBJPROP_ZORDER,0); } // small helper to draw rectangle label cell void DrawCell(string id,int x,int y,int w,int h,color bg,color br) { if(ObjectFind(g_chart_id,id) < 0) ObjectCreate(g_chart_id,id,OBJ_RECTANGLE_LABEL,0,0,0); ObjectSetInteger(g_chart_id,id,OBJPROP_CORNER,InpPanelCorner); ObjectSetInteger(g_chart_id,id,OBJPROP_XDISTANCE,x); ObjectSetInteger(g_chart_id,id,OBJPROP_YDISTANCE,y); ObjectSetInteger(g_chart_id,id,OBJPROP_XSIZE,w); ObjectSetInteger(g_chart_id,id,OBJPROP_YSIZE,h); ObjectSetInteger(g_chart_id,id,OBJPROP_BGCOLOR,(int)bg); ObjectSetInteger(g_chart_id,id,OBJPROP_BORDER_COLOR,(int)br); ObjectSetInteger(g_chart_id,id,OBJPROP_SELECTABLE,false); ObjectSetInteger(g_chart_id,id,OBJPROP_ZORDER,1); }CreateUIObjects und DeleteUIObjects verwalten den Lebenszyklus für alle UI-Elemente. CreateUIObjects erstellt vorab den Hintergrund, Schatten, Titel, Punktestand, Zone, zuletzt verwendete Objekte und Histogrammbezeichnung und legt Standardschriftarten und die Z-Reihenfolge fest. DeleteUIObjects bereinigt sie beim Deinit – eine nützliche Praxis, die das Chart aufgeräumt lässt, wenn der EA entfernt wird. Durch die Vorerstellung einer festen Anzahl von Histogrammbeschriftungsobjekten (basierend auf InpHistogramMaxBars) wird die häufige Erstellung von Objekten zur Laufzeit vermieden, was die Leistung und Stabilität verbessert.
void CreateUIObjects() { int default_w = 200; int default_h = 80; int scol = (int)ARGB_uint(InpShadowAlpha, InpShadowColor); DrawCell(ui_shadow_name, InpPanelX + 3, InpPanelY + 3, default_w, default_h, (color)scol, InpGridClr); DrawCell(ui_bg_name, InpPanelX, InpPanelY, default_w, default_h, InpPanelBG, InpGridClr); DrawText(ui_title_name, "", InpPanelX + InpPanelPadding, InpPanelY + InpPanelPadding, InpTxtClr, InpTitleFontSize); DrawText(ui_score_name, "", InpPanelX + InpPanelPadding, InpPanelY + InpPanelPadding + InpTitleFontSize + 4, InpTxtClr, InpScoreFontSize); DrawText(ui_zone_name, "", InpPanelX + default_w - InpPanelPadding - 80, InpPanelY + InpPanelPadding + 4, InpTxtClr, InpSmallFontSize); DrawText(ui_advice_name, "", InpPanelX + InpPanelPadding, InpPanelY + InpPanelPadding + InpTitleFontSize + InpScoreFontSize + 8, InpTxtClr, InpSmallFontSize); DrawText(ui_recent_name, "", InpPanelX + InpPanelPadding, InpPanelY + default_h - InpPanelPadding - 18, InpTxtClr, InpSmallFontSize); DrawText(ui_signal_name, "", InpPanelX + InpPanelPadding + 120, InpPanelY + InpPanelPadding + InpTitleFontSize + InpScoreFontSize + 8, InpTxtClr, InpSmallFontSize); for(int i = 0; i < InpHistogramMaxBars; i++) { string name = ui_hist_base + IntegerToString(i); if(ObjectFind(g_chart_id, name) < 0) { ObjectCreate(g_chart_id, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(g_chart_id, name, OBJPROP_CORNER, InpPanelCorner); ObjectSetString(g_chart_id, name, OBJPROP_FONT, InpFont); ObjectSetInteger(g_chart_id, name, OBJPROP_FONTSIZE, InpSmallFontSize); ObjectSetInteger(g_chart_id, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(g_chart_id, name, OBJPROP_ZORDER,0); } } } void DeleteUIObjects() { if(g_chart_id == 0) return; if(ObjectFind(g_chart_id, ui_shadow_name) >= 0) ObjectDelete(g_chart_id, ui_shadow_name); if(ObjectFind(g_chart_id, ui_bg_name) >= 0) ObjectDelete(g_chart_id, ui_bg_name); if(ObjectFind(g_chart_id, ui_title_name) >= 0) ObjectDelete(g_chart_id, ui_title_name); if(ObjectFind(g_chart_id, ui_score_name) >= 0) ObjectDelete(g_chart_id, ui_score_name); if(ObjectFind(g_chart_id, ui_zone_name) >= 0) ObjectDelete(g_chart_id, ui_zone_name); if(ObjectFind(g_chart_id, ui_recent_name) >= 0) ObjectDelete(g_chart_id, ui_recent_name); if(ObjectFind(g_chart_id, ui_advice_name) >= 0) ObjectDelete(g_chart_id, ui_advice_name); if(ObjectFind(g_chart_id, ui_signal_name) >= 0) ObjectDelete(g_chart_id, ui_signal_name); for(int i = 0; i < InpHistogramMaxBars; i++) { string name = ui_hist_base + IntegerToString(i); if(ObjectFind(g_chart_id, name) >= 0) ObjectDelete(g_chart_id, name); } }
In RefreshUI werden das Panel-Layout und das Histogramm tatsächlich berechnet und gezeichnet. Es misst die Schrifthöhen und reserviert Platz für Titel, Partitur, kleinen Text und eine Histogrammzeile; es berechnet die Breite des Panels dynamisch, sodass das Histogramm hineinpasst, erzwingt aber eine sinnvolle Mindestbreite. Es zeichnet die Schatten- und Hintergrundrechtecke, berechnet die Platzierung der Zonenabzeichen unter Vermeidung von Titelüberschneidungen (mit EstimateTextWidth) und wählt Farben für die geglättete Bewertung (scoreCol) auf der Grundlage von Schwellenwerten. Die Zeile „Recent:“ wird aus den jüngsten Bars des primären Zeitrahmens (Close vs. Open und deren Punktbereiche) generiert und mit Ellipsis abgeschnitten, falls sie überlaufen würde.
Für das Histogramm wird die Anzahl der Textblock-Glyphen berechnet, um die Größe visuell darzustellen, jede Glyphe wird mit einem zuvor erstellten Kennzeichnungsobjekt verbunden und die Farben werden je nach Vorzeichen ausgewählt. Diese Routine ist defensiv (sie prüft den verfügbaren Platz und verkleinert die Abstände, wenn nötig) und wurde entwickelt, um Überschneidungen der Kennzeichnungen zu vermeiden und gleichzeitig das Panel kompakt und lesbar zu halten.
void RefreshUI(double rawScore, double smoothScore) { int title_h = InpTitleFontSize + 6; int score_h = InpScoreFontSize + 8; int small_h = InpSmallFontSize + 4; int hist_area_h = 22; int gap_between_sections = 6; int hist_px_step = 7; int hist_px_width = InpHistogramMaxBars * hist_px_step; int panel_width = MathMax(340, hist_px_width + InpPanelPadding*2 + 30); int extra_recent_space = small_h + 12; int panel_height = InpPanelPadding*2 + title_h + score_h + small_h + hist_area_h + (gap_between_sections * 4) + extra_recent_space; int scol = (int)ARGB_uint(InpShadowAlpha, InpShadowColor); DrawCell(ui_shadow_name, InpPanelX + 3, InpPanelY + 3, panel_width, panel_height, (color)scol, InpGridClr); DrawCell(ui_bg_name, InpPanelX, InpPanelY, panel_width, panel_height, InpPanelBG, InpGridClr); int x_start = InpPanelX + InpPanelPadding; int y_title = InpPanelY + InpPanelPadding; string titleText = StringFormat("Sentiment Tilt Meter — %s [%s]", Symbol(), TFToText(InpTFPrimary)); DrawText(ui_title_name, titleText, x_start, y_title, InpTxtClr, InpTitleFontSize); // ... zone, score, advice, recent and histogram painting (omitted here for brevity) // The full EA code contains the rest and iterates label objects to draw histogram blocks. }TFToText ist ein einfaches Hilfsmittel, das die ENUM_TIMEFRAMES auf kurze menschliche Textbezeichnungen (M1, M5, M15, H1 usw.) abbildet. Sie wird im Titel verwendet, damit der Nutzer immer weiß, auf welchen primären Zeitrahmen sich der EA bezieht.
string TFToText(ENUM_TIMEFRAMES 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 IntegerToString((int)tf); } }
OnInit führt die Initialisierung durch: Es erfasst die Chart-ID und erstellt das Objektpräfix, ändert die Größe und initialisiert den Histogrammpuffer, erstellt ATR-Indikator-Handles (iATR) für die angeforderten Zeitrahmen (nur, wenn der Multi-TF-Modus aktiviert ist), berechnet ein anfängliches Raw Fused Sentiment (ComputeFusedSentiment), legt den geglätteten Wert und den Zonenstatus fest, erstellt UI-Objekte und zeichnet das anfängliche Panel mit RefreshUI und installiert einen 1-Sekunden-Timer (EventSetTimer(1)), damit OnTimer neue Primärbalken bemerken kann. Der Entwurf initialisiert die Glättung mit dem ersten Rohwert, damit der erste angezeigte geglättete Wert stabil ist. Beachten Sie, dass der Code UpdateZoneState frühzeitig aufruft, damit die Ausgangszone korrekt ist.
OnDeinit räumt auf, wenn der EA entfernt wird: Es beendet den Timer, löscht UI-Objekte und entfernt alle von diesem EA erstellten Signalmarker (unter Verwendung der Namenskonvention g_prefix + „SIG_“). Es gibt auch die Handles des ATR-Indikators mit IndicatorRelease frei. Auf diese Weise bleiben die Ressourcen sauber und es wird vermieden, dass Texte auf dem Chart zurückbleiben oder Indikator-Handle übrig bleiben.
int OnInit() { g_chart_id = ChartID(); g_prefix = Symbol() + "_" + IntegerToString((int)g_chart_id) + "_"; ui_bg_name = g_prefix + "BG"; ui_shadow_name = g_prefix + "SHDW"; ui_title_name = g_prefix + "TITLE"; ui_score_name = g_prefix + "SCORE"; ui_zone_name = g_prefix + "ZONE"; ui_hist_base = g_prefix + BASE_HIST; ui_recent_name = g_prefix + "RECENT"; ui_advice_name = g_prefix + "ADVICE"; ui_signal_name = g_prefix + "LASTSIG"; ArrayResize(g_hist_buffer, InpHistogramMaxBars); ArrayInitialize(g_hist_buffer, 0.0); g_hist_idx = 0; g_hist_count = 0; g_atr_handle_primary = iATR(Symbol(), InpTFPrimary, InpATRPeriod); if(InpUseMultiTF) { g_atr_handle_extra1 = iATR(Symbol(), InpExtraTF1, InpATRPeriod); g_atr_handle_extra2 = iATR(Symbol(), InpExtraTF2, InpATRPeriod); } double raw_init = ComputeFusedSentiment(); g_smoothed_score = raw_init; g_prev_smoothed = g_smoothed_score; g_last_alert_score = raw_init; UpdateZoneState(raw_init); CreateUIObjects(); RefreshUI(raw_init, g_smoothed_score); EventSetTimer(1); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { EventKillTimer(); DeleteUIObjects(); // delete signal markers created by this EA (prefix-based) string name; int total = ObjectsTotal(g_chart_id); for(int i = total - 1; i >= 0; --i) { name = ObjectName(g_chart_id, i); if(StringFind(name, g_prefix + "SIG_") == 0) ObjectDelete(g_chart_id, name); } if(g_atr_handle_primary != INVALID_HANDLE) IndicatorRelease(g_atr_handle_primary); if(g_atr_handle_extra1 != INVALID_HANDLE) IndicatorRelease(g_atr_handle_extra1); if(g_atr_handle_extra2 != INVALID_HANDLE) IndicatorRelease(g_atr_handle_extra2); }Der EA verwendet OnTimer als seinen Hauptabfragemechanismus. Er prüft den Zeitstempel des aktuellen Primär-TF-Balkens (iTime(Symbol(), InpTFPrimary, 0)) und ruft HandleNewPrimaryBar auf, wenn sich dieser Zeitstempel vom zuletzt verarbeiteten Wert unterscheidet. Bei diesem Ansatz wird die Verarbeitung bei jedem Timer-Tick vermieden und nur einmal pro neuem abgeschlossenen Balken ausgeführt: ein gutes Muster, wenn Sie balkenbasierte Signale wünschen und einen Timer statt OnTick verwenden.
datetime g_last_primary_time = 0; void OnTimer() { datetime t = iTime(Symbol(), InpTFPrimary, 0); if(t == g_last_primary_time) return; g_last_primary_time = t; HandleNewPrimaryBar(); } void HandleNewPrimaryBar() { double raw = ComputeFusedSentiment(); double alpha = MathMax(0.0, MathMin(1.0, InpSmoothAlpha)); g_prev_smoothed = g_smoothed_score; g_smoothed_score = alpha * raw + (1.0 - alpha) * g_smoothed_score; g_hist_buffer[g_hist_idx] = g_smoothed_score; g_hist_idx = (g_hist_idx + 1) % InpHistogramMaxBars; if(g_hist_count < InpHistogramMaxBars) g_hist_count++; RefreshUI(raw, g_smoothed_score); ProcessAlerts(raw, g_smoothed_score); }
HandleNewPrimaryBar berechnet über ComputeFusedSentiment ein frisches Raw Fused Sentiment, wendet eine exponentielle Glättung mit dem konfigurierten Alpha an (g_smoothed_score = alpha * raw + (1-alpha) * prev), speichert den geglätteten Wert im zirkulären Histogrammpuffer, aktualisiert die Indizes und die Anzahl, aktualisiert die Nutzeroberfläche und ruft ProcessAlerts auf, um zu prüfen, ob dieser neue geglättete Wert Meldungen oder Signale auslösen sollte. Der zirkuläre Puffer wird mit g_hist_idx modulo InpHistogramMaxBars implementiert, und g_hist_count verfolgt, wie viele Einträge gültig sind. Auf diese Weise wird eine fortlaufende Historie für das visuelle Histogramm und für die Vorzeichenprüfung aufrechterhalten.
ComputeFusedSentiment ist der oberste Aggregator für die Bewertung. Es normalisiert die absoluten Werte der drei Gewichtseingänge, sodass sie sich zu 1 summieren (mit einem sicheren Rückfall auf primär, nur wenn die Summe Null ist). Dann wird ComputeTFScore für den primären und, falls aktiviert, für die beiden zusätzlichen Zeitrahmen aufgerufen. Schließlich wird ein gewichteter Mittelwert berechnet und die fusionierte Ausgabe auf den Bereich [-100, 100] begrenzt. Dadurch bleiben die nachgelagerte Nutzeroberfläche und die Schwellenwertlogik unabhängig von der rohen Pro-TF-Skala konsistent.
double ComputeFusedSentiment() { double w1 = MathAbs(InpWeightPrimary); double w2 = MathAbs(InpWeightExtra1); double w3 = MathAbs(InpWeightExtra2); double sum = w1 + w2 + w3; if(sum <= 0.0) { w1 = 1.0; w2 = 0.0; w3 = 0.0; sum = 1.0; } w1 /= sum; w2 /= sum; w3 /= sum; double s1 = ComputeTFScore(Symbol(), InpTFPrimary, InpLookback, InpSSWindow, g_atr_handle_primary); if(!InpUseMultiTF) return s1; double s2 = ComputeTFScore(Symbol(), InpExtraTF1, InpLookback, InpSSWindow, g_atr_handle_extra1); double s3 = ComputeTFScore(Symbol(), InpExtraTF2, InpLookback, InpSSWindow, g_atr_handle_extra2); double fused = s1 * w1 + s2 * w2 + s3 * w3; return MathMax(-100.0, MathMin(100.0, fused)); } double ComputeTFScore(string sym, ENUM_TIMEFRAMES tf, int lookback, int ssWindow, int atrHandle) { MqlRates rates[]; int needed = MathMax(lookback, ssWindow) + 6; int copied = CopyRates(sym, tf, 0, needed, rates); if(copied <= 0) return 0.0; double atr = 0.0001; if(atrHandle != INVALID_HANDLE) { double abuf[]; if(CopyBuffer(atrHandle, 0, 1, 1, abuf) > 0) atr = abuf[0] * InpATRMultiplier; } else { int rc = MathMin(10, copied - 1); double ar = 0.0; for(int i = 1; i <= rc; i++) ar += (rates[i].high - rates[i].low); if(rc > 0) ar /= rc; if(ar > 0) atr = ar * 0.6; } int limit = MathMin(ssWindow, copied - 1); double avgRange = 0.0; int vr = MathMin(10, copied - 1); for(int k = 1; k <= vr; k++) avgRange += (rates[k].high - rates[k].low); if(vr > 0) avgRange /= vr; double sumSS = 0.0; int count = 0; for(int i = 1; i <= limit; i++) { double open = rates[i].open; double close = rates[i].close; double high = rates[i].high; double low = rates[i].low; double range = high - low; if(range <= 0.0) continue; double dir = (close > open) ? 1.0 : -1.0; double cbr = (MathAbs(close - open) / range) * dir; double cpp = (((close - low) / range) - 0.5) * 2.0 * dir; double vad = 0.0; if(atr > 0.0) vad = (range / atr) - 1.0; vad = MathMax(-1.0, MathMin(3.0, vad)); double ss = cbr * 0.4 + cpp * 0.3 + vad * 0.3; ss = MathMax(-1.0, MathMin(1.0, ss)); bool conf = false; if((i - 1) >= 0 && (i - 1) <= copied - 1) { double nOpen = rates[i - 1].open; double nClose = rates[i - 1].close; if(dir > 0.0 && nClose > nOpen) conf = true; if(dir < 0.0 && nClose < nOpen) conf = true; } double confFactor = conf ? 1.0 : 0.6; double volFactor = 1.0; if(atr > 0.0 && avgRange / atr < InpVolQuietThreshold) volFactor = 0.6; double finalSS = ss * confFactor * volFactor; sumSS += finalSS; count++; } if(count == 0) return 0.0; double avgSS = sumSS / count; return avgSS * 100.0; }
ComputeTFScore ist der Ort, an dem die wichtigsten Signalmerkmale pro Zeitrahmen berechnet werden. Er kopiert einen Block aktueller MqlRates-Balken aus dem angeforderten Zeitrahmen und berechnet einen ATR-Proxy: Wenn ein ATR-Handle existiert, kopiert er den Indikatorpuffer; andernfalls erstellt er einen einfachen Average True Range Fallback. Der Code berechnet einen avgRange und iteriert dann über bis zu ssWindow vergangene Balken, um drei normalisierte Merkmale pro Balken abzuleiten: cbr ((Close vs. Open relativ zum Range, Erfassung von Richtung und Stärke), cpp (Close-Position innerhalb des Balkens normalisiert und nach Richtung verzerrt) und vad (volumen-/volatilitätsbereinigter Abstand: Range relativ zum ATR minus eins). Jedes Merkmal wird gewichtet (0,4, 0,3 bzw. 0,3), auf [-1,1] normiert und dann mit zwei Konfidenzfaktoren multipliziert: confFactor, der prüft, ob der vorherige Balken die Richtung bestätigt hat, und volFactor, der den Beitrag reduziert, wenn der Markt im Vergleich zur ATR ungewöhnlich ruhig ist.
Die endgültige Stimmungsprobe der „Pin-Bar“ finalSS wird über das Fenster hinweg akkumuliert und gemittelt und dann mit 100 skaliert, um einen praktischen prozentualen Bereich zu erhalten. Kurz gesagt: Die Routine kodiert die Richtungsabhängigkeit, die Nähe innerhalb des Balkens, die Größe der relativen Spanne und die einfache Cross-Bar-Bestätigung in einer einzigen Punktzahl pro TB.
RecentSmoothedSignHold ist ein Hilfsmittel, das von der Vorzeichenumkehr-Logik verwendet wird, um sicherzustellen, dass die letzten geglätteten Balkenwerte alle das neue Vorzeichen haben. Es durchsucht den zirkulären g_hist_buffer rückwärts und gibt false zurück, wenn einer der erforderlichen geglätteten Werte den Vorzeichentest nicht besteht. Dadurch wird verhindert, dass ein einziger störender Balken das Signal umspringen lässt.
ProcessAlerts ist die Entscheidungsmaschine für Nachrichten und Chart-signale. Zunächst wird geprüft, ob die Historie ausreicht (InpMinBarsForSignal), und dann wird geprüft, ob ein großer abrupter Flip vorliegt (absolute Differenz zu g_last_alert_score übersteigt InpFlipThreshold). In diesem Fall wird ein Alarm gesendet, der Zonenstatus aktualisiert und optional eine Textmarkierung BUY/SELL gezeichnet. Wenn es sich nicht um einen großen Flip handelt, wird die neue Zone aus InpPositiveZone/InpNegativeZone berechnet. Wenn InpSignalOnSignFlip aktiviert ist, prüft es auch auf Vorzeichenumkehr (positiv→negativ oder negativ→positiv) und wendet drei Akzeptanztests an: minimale absolute Größe des neuen geglätteten Werts (InpMinSignFlipAbs), kürzliche Vorzeichenhaltung (InpSignFlipHoldBars) und optionaler Impuls (InpRequireMomentum + InpMinMomentum). Nur wenn alle Tests erfolgreich sind, wird die Vorzeichenumkehr als echtes Signal akzeptiert, die Kennzeichnung auf das Chart gezeichnet, g_last_alert_score aktualisiert und g_zone_state gesetzt .
Wenn sich die Zone normal ändert (Eintritt in positive oder negative Zonen), sendet der Code ebenfalls Warnmeldungen, zeichnet das letzte Signal und die Uhrzeit auf und setzt eine Markierung. Das Ergebnis ist eine mehrschichtige, konservative Signalisierungsstrategie, die klarere strukturelle Veränderungen gegenüber bewegtes Rauschen bevorzugt.
bool RecentSmoothedSignHold(int bars, int desiredSign) { if(bars <= 1) return true; if(g_hist_count < bars) return false; int max = InpHistogramMaxBars; int idx = (g_hist_idx - 1 + max) % max; for(int i = 0; i < bars; i++) { double v = g_hist_buffer[(idx - i + max) % max]; if(desiredSign == 1 && v <= 0.0) return false; if(desiredSign == -1 && v >= 0.0) return false; } return true; } void ProcessAlerts(double rawScore, double smoothScore) { if(g_hist_count < InpMinBarsForSignal) { PrintFormat("%s: waiting hist_count=%d (need %d)", __FUNCTION__, g_hist_count, InpMinBarsForSignal); return; } // large flip detection if(MathAbs(smoothScore - g_last_alert_score) >= InpFlipThreshold) { string m = StringFormat("STM Flip: %.1f -> %.1f on %s", g_last_alert_score, smoothScore, Symbol()); SendAlert(m); g_last_alert_score = smoothScore; UpdateZoneState(smoothScore); int newZoneFlip = 0; if(smoothScore >= InpPositiveZone) newZoneFlip = 1; else if(smoothScore <= InpNegativeZone) newZoneFlip = -1; if(newZoneFlip != 0) { g_last_signal_text = (newZoneFlip == 1) ? "BUY" : "SELL"; g_last_signal_time = TimeCurrent(); DrawSignalOnChart(newZoneFlip, smoothScore); PruneOldSignals(InpMaxSignalsToKeep); } RefreshUI(rawScore, g_smoothed_score); return; } }UpdateZoneState ist eine triviale Funktion, die g_zone_state anhand eines geglätteten Scores und der konfigurierten positiven/negativen Zonenschwellenwerte auf 1, -1 oder 0 setzt. SendAlert zentralisiert die Zustellung von Alarmen: Abspielen eines Tons, wenn aktiviert, Senden einer Push-Benachrichtigung, wenn aktiviert, Aufruf von Alert() (lokaler Terminaldialog) und Print() für das Protokoll. Die Zentralisierung von Warnmeldungen erleichtert künftige Erweiterungen (z. B. E-Mail oder Webhooks).
void UpdateZoneState(double smoothScore) { if(smoothScore >= InpPositiveZone) g_zone_state = 1; else if(smoothScore <= InpNegativeZone) g_zone_state = -1; else g_zone_state = 0; } void SendAlert(string msg) { if(InpEnableSound) PlaySound(InpSoundFile); if(InpEnablePush) SendNotification(msg); Alert(msg); Print(__FILE__, ": ", msg); }
DrawSignalOnChart sorgt dafür, dass der BUY/SELL-Text und das Pfeilsymbol genau am letzten geschlossenen Primärbalken (Index 1) platziert werden. Es leitet einen eindeutigen Objektnamen aus dem Zeitstempel und g_hist_idx ab, sodass mehrere Signale unterscheidbar sind, berechnet einen kleinen Kursversatz, um die Textbeschriftung über/unter dem Pfeil zu platzieren, während der Pfeil genau auf dem Preis bleibt, und erstellt zwei Chart-Textobjekte: eines für die Textbeschriftung und eines für die Pfeilglyphe. Der EA unterstützt eine einfache Auswahl von Pfeilsymbolen und stellt Schriftarten, Farben, Z-Reihenfolge und Nicht-Auswählbarkeit ein, damit diese Anmerkungen die manuelle Arbeit des Händlers am Chart nicht beeinträchtigen. Nach der Erstellung eines Signals wird PruneOldSignals aufgerufen, um sicherzustellen, dass das Chart nicht mit zu vielen Kennzeichnungen überladen ist.
PruneOldSignals ist pragmatisches Housekeeping: Es zählt alle Chart-Objekte auf, wählt diejenigen aus, die mit dem EA-Präfix + SIG_ beginnen, extrahiert den im Objektnamen kodierten Zeitstempel, sortiert nach Zeitstempel (durch wiederholtes Entfernen des Minimums) und löscht die ältesten, bis nur noch Keep übrig sind. Dadurch wird gewährleistet, dass nur die neuesten InpMaxSignalsToKeep-Marker erhalten bleiben, das Chart nicht unübersichtlich wird und die Enumeration von Objekten für vernünftige Beibehaltungswerte kostengünstig bleibt.
void DrawSignalOnChart(int zone, double score) { datetime tm = iTime(Symbol(), InpTFPrimary, 1); double price = iClose(Symbol(), InpTFPrimary, 1); if(tm == 0 || price == 0.0) return; double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT); double textOffsetPts = MathMax(1, InpArrowOffsetPoints); double textOffset = point * textOffsetPts * 1.5; string name = g_prefix + "SIG_" + IntegerToString((int)tm) + "_" + IntegerToString(g_hist_idx); string arrName = g_prefix + "SIG_ARR_" + IntegerToString((int)tm) + "_" + IntegerToString(g_hist_idx); if(ObjectFind(g_chart_id, name) >= 0) ObjectDelete(g_chart_id, name); if(ObjectFind(g_chart_id, arrName) >= 0) ObjectDelete(g_chart_id, arrName); double text_price = (zone == 1) ? price + textOffset : price - textOffset; if(ObjectCreate(g_chart_id, name, OBJ_TEXT, 0, tm, text_price)) { string txt = (zone == 1) ? "BUY" : ((zone == -1) ? "SELL" : "NEUTRAL"); color col = (zone == 1) ? clrLime : ((zone == -1) ? clrRed : clrSilver); ObjectSetString(g_chart_id, name, OBJPROP_TEXT, txt); ObjectSetInteger(g_chart_id, name, OBJPROP_COLOR, (int)col); ObjectSetInteger(g_chart_id, name, OBJPROP_FONTSIZE, 12); ObjectSetString(g_chart_id, name, OBJPROP_FONT, InpFont); ObjectSetInteger(g_chart_id, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(g_chart_id, name, OBJPROP_ZORDER, 2); } if(InpShowArrows) { string arrowTxt = (zone == 1) ? "▲" : ((zone == -1) ? "v" : "■"); if(ObjectCreate(g_chart_id, arrName, OBJ_TEXT, 0, tm, price)) { color acol = (zone == 1) ? clrLime : ((zone == -1) ? clrRed : clrSilver); ObjectSetString(g_chart_id, arrName, OBJPROP_TEXT, arrowTxt); ObjectSetInteger(g_chart_id, arrName, OBJPROP_COLOR, (int)acol); ObjectSetInteger(g_chart_id, arrName, OBJPROP_FONTSIZE, InpArrowFontSize); ObjectSetString(g_chart_id, arrName, OBJPROP_FONT, InpFont); ObjectSetInteger(g_chart_id, arrName, OBJPROP_SELECTABLE, false); ObjectSetInteger(g_chart_id, arrName, OBJPROP_ZORDER, 3); } } PruneOldSignals(InpMaxSignalsToKeep); } void PruneOldSignals(int keep) { if(keep <= 0) return; int total = ObjectsTotal(g_chart_id); string names[]; int times[]; int n = 0; string sigPrefix = g_prefix + "SIG_"; for(int i = 0; i < total; i++) { string nm = ObjectName(g_chart_id, i); if(StringFind(nm, sigPrefix) == 0) { string rest = StringSubstr(nm, StringLen(sigPrefix)); int pos = StringFind(rest, "_"); if(pos > 0) { string tsStr = StringSubstr(rest, 0, pos); int ts = (int)StringToInteger(tsStr); ArrayResize(names, n+1); ArrayResize(times, n+1); names[n] = nm; times[n] = ts; n++; } } } if(n <= keep) return; while(n > keep) { int minIdx = 0; for(int j = 1; j < n; j++) if(times[j] < times[minIdx]) minIdx = j; ObjectDelete(g_chart_id, names[minIdx]); for(int k = minIdx; k < n-1; k++) { names[k] = names[k+1]; times[k] = times[k+1]; } ArrayResize(names, n-1); ArrayResize(times, n-1); n--; } }
Schließlich enthält der EA mehrere defensive und UX-orientierte Design-Entscheidungen: eindeutige Präfixierung von Objektnamen, um Kollisionen zu vermeiden, Fallback-ATR-Berechnung, wenn Indikator-Handles ausfallen, Normierung von fusionierten Scores auf einen festen Bereich, Glättung mit einem konfigurierbaren Alpha, um Rauschen zu reduzieren, und geschichtete Signalakzeptanz (Flip-Schwelle, Vorzeichen-Flip-Magnitude/Hold/Momentum, Zone Entry). Der UI-Code beinhaltet eine Kollisionsvermeidung bei der Platzierung des Zonen-Badges und des aktuellen Textes, und das Histogramm verwendet Textblöcke anstelle von Grafiken, sodass es schlank und mit früheren MetaTrader 5-Builds kompatibel ist.
Tests und Ergebnisse
In diesem Abschnitt werden wir die getesteten Ergebnisse unseres EAs untersuchen, und zwar sowohl die Backtest-Ergebnisse als auch die Live-Performance-Metriken.
Nachfolgend sehen Sie ein GIF des Backtests zum Crash 300 Index. Das Dashboard ist eine kompakte, prüfungsfreundliche Zusammenfassung des STM-Status: Die oberste Zeile nennt das Tool, das Instrument (Crash 300 Index) und den aktiven Zeitrahmen (M5). Darunter zeigt ein großer, farbkodierter numerischer Stimmungswert die geglättete Marktneigung auf einer Skala von -100...+100 an, während ein kurzes Bias-Label (z.B. BULL, BEAR, NEUTRAL) einen unmittelbaren qualitativen Eindruck vermittelt. Direkt unter dem Ergebnis wird das letzte Signal angezeigt (z. B. „Bias: Long BUY @ 16:30“), sodass Sie die Richtung und den genauen Zeitstempel des Balkens überprüfen können. Ein horizontaler Stärkebalken veranschaulicht das Ausmaß, und eine kompakte Zeile „Letzte“ listet die letzten Kerzenmetriken auf, um das Ergebnis im Kontext zu sehen. Alle Elemente sind an geschlossenen Balken verankert, sodass jede Meldung und jedes Etikett zu einem genauen Zeitpunkt und Preis rückverfolgbar ist.

Während des Live-Tests des Crash 1000 Index gab der EA ein KAUF-Signal aus, das sich nach einer kurzen Haltezeit als richtig erwies, wenn es mit dem Zeitstempel des geschlossenen Balkens verglichen wurde. Das Dashboard zeigte eine deutlich positive Tendenz, und die KAUF-Markierung war genau am Close des Balkens und am Preis verankert, sodass die Bewegung und ihr Timing auf dem Chart vollständig nachvollziehbar sind. Dieses Ergebnis veranschaulicht die Fähigkeit des STM, einen dauerhaften kurzfristigen Aufwärtstrend zu erkennen – Glättungs- und Momentum-Kontrollen filterten das Rauschen und halfen, einen frühen Whipsaw zu vermeiden – während das Handelsmanagement (Positionsgröße, Stopps, Gewinnziele) weiterhin dem Händler überlassen blieb.
- Live-Test auf dem Crash 300 Index
Schlussfolgerung
Das Sentiment Tilt Meter (STM) hat genau das getan, wofür es entwickelt wurde: kurzfristige Verzerrungen des Crash 300 und des Crash 1000 klar und zuverlässig aufgezeigt. Die Signale sind leicht zu überprüfen – jeder Pfeil und jede Kennzeichnung ist an einen geschlossenen Balken und einen Preis gebunden – so können Sie überprüfen, was wann passiert ist, ohne zu raten. Die Glättung und die Momentum-Kontrollen verhindern, dass die Anzeige bei kleinen „ping-pong“, einem geringem Auf und Ab, reagiert, und die Gewichtung pro TF lässt Sie die Horizonte bevorzugen, die für Ihren Stil wichtig sind.
Dieses Tool ist ein Signalgenerator und ein Aufzeichnungsgerät, kein vollständiges Handelssystem. Es zeigt Ihnen an, wenn der Markt in eine Schieflage gerät, aber es kann nicht die Größe von Positionen bestimmen, Stopps setzen oder Ausstiege für Sie verwalten. Die Ergebnisse variieren je nach Instrument und Sitzung; Crash-Indizes haben sich in diesen Tests gut verhalten, aber Devisen, Aktien oder Futures benötigen möglicherweise eine andere Abstimmung.
Verwenden Sie STM als Filter oder als Bestätigungsebene. Die Signale werden im CSV-Format protokolliert, ein Backtests mit mehreren Symbolen ausgeführt und einen kurzen Forward-Test gemacht, und erst dann sollte eine Automatisierung der Eingaben in Betracht gezogen werden. Wenn Sie auf der Grundlage seiner Signale handeln, verpacken Sie sie in einen einfachen Risikoplan – feste R, klare Stop-Regeln und eine Überprüfung auf einen höheren TF – damit eine unruhige Sitzung nicht einen ganzen Monat lang Vorteile zunichte machen kann.
Kurz gesagt: STM liefert eine saubere, überprüfbare Anzeige der kurzfristigen Marktentwicklung. Es beschleunigt die Entscheidungsfindung und bewahrt die Disziplin. Legen Sie Risikokontrollen und Überprüfungsschritte darüber, bevor Sie mit echtem Geld handeln.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19137
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.
Aufbau eines Handelssystems (Teil 2): Die Wissenschaft der Positionsbestimmung
Automatisieren von Handelsstrategien in MQL5 (Teil 26): Aufbau eines Pin Bar Averaging Systems für den Handel mit mehreren Positionen
Automatisieren von Handelsstrategien in MQL5 (Teil 27): Erstellen eines Price Action Harmonic Pattern der Krabbe mit visuellem Feedback
MetaTrader Tick-Info-Zugang von MQL5-Diensten zur Python-Anwendung über Sockets
- 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.