English 日本語
preview
Manuelle Backtest leicht gemacht: Aufbau eines nutzerdefinierten Toolkits für Strategietester in MQL5

Manuelle Backtest leicht gemacht: Aufbau eines nutzerdefinierten Toolkits für Strategietester in MQL5

MetaTrader 5Tester |
21 6
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

Die Backtests von Handelsstrategien sind Eckpfeiler des erfolgreichen Handels, aber die Automatisierung jeder Idee kann sich einschränkend anfühlen, während es bei manuellen Tests oft an Struktur und Präzision mangelt. Wie wäre es, wenn Sie die Kontrolle über den manuellen Handel mit der Leistung des Strategy Testers MetaTrader 5 kombinieren könnten? In diesem Artikel stellen wir Ihnen einen nutzerdefinierten Expert Advisor (EA) in der Sprache MetaQuotes Language 5 (MQL5) vor, der die manuellen Backtests in einen intuitiven, effizienten Prozess verwandelt und Ihnen ein Toolkit zur Verfügung stellt, mit dem Sie Strategien nach Ihren Vorstellungen testen können. Wir gehen diese Schritte in dieser Reihenfolge durch:

  1. Der Plan: Entwurf eines Toolkits für die manuellen Backtests
  2. Implementierung in MQL5: Das Toolkit zum Leben erwecken
  3. Backtests in Aktion: Verwendung des Toolkits
  4. Schlussfolgerung

Am Ende werden Sie eine praktische Lösung haben, um Ihre Handelsideen schnell und sicher im Strategy Tester zu testen und zu verfeinern.


Der Plan: Entwurf eines Toolkits für die manuellen Backtests

Unser Ziel ist es, ein Toolkit zu entwickeln, das die manuelle Kontrolle mit der schnellen Backtest-Geschwindigkeit des Strategy Testers im MetaTrader 5 verbindet und die langsamen Echtzeit-Ticks des traditionellen manuellen Testens umgeht. Wir werden das Programm mit Schaltflächen auf dem Chart ausstatten, um Kauf- oder Verkaufstransaktionen auszulösen, Losgrößen anzupassen, Stop-Loss- (SL) und Take-Profit-Levels (TP) festzulegen und alle Positionen über eine Panik-Schaltfläche zu schließen - voll integrierbar mit jeder Strategie, von Indikatoren und japanischen Candlestick-Mustern bis hin zu Preisaktionen, die alle mit dem beschleunigten Tempo des Testers arbeiten. Dieses flexible Setup ermöglicht es uns, jeden Handelsansatz interaktiv, schnell und präzise zu testen und die Strategieverfeinerung in einer simulierten Umgebung zu optimieren. Kurz gesagt, hier ist eine Visualisierung dessen, was wir anstreben:

BILD DES PLANS


Implementierung in MQL5: Das Toolkit zum Leben erwecken

Um das Programm in MQL5 zu erstellen, müssen wir die Programm-Metadaten definieren, dann einige Nutzer-Eingabeparameter festlegen und schließlich einige Bibliotheksdateien einfügen, die es uns ermöglichen, die Handelsaktivitäten durchzuführen.

//+------------------------------------------------------------------+
//|                       Manual backtest toolkit in Strategy Tester |
//|                        Copyright 2025, Forex Algo-Trader, Allan. |
//|                                 "https://t.me/Forex_Algo_Trader" |
//+------------------------------------------------------------------+
#property copyright "Forex Algo-Trader, Allan"
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"
#property description "This EA Enables manual backtest in the strategy tester"
#property strict //--- Enforce strict coding rules to catch errors early

#define BTN_BUY "BTN BUY" //--- Define the name for the Buy button
#define BTN_SELL "BTN SELL" //--- Define the name for the Sell button
#define BTN_P "BTN P" //--- Define the name for the button that increase lot size
#define BTN_M "BTN M" //--- Define the name for the button that decrease lot size
#define BTN_LOT "BTN LOT" //--- Define the name for the lot size display button
#define BTN_CLOSE "BTN CLOSE" //--- Define the name for the button that close all positions
#define BTN_SL "BTN SL" //--- Define the name for the Stop Loss display button
#define BTN_SL1M "BTN SL1M" //--- Define the name for the button that slightly lower Stop Loss
#define BTN_SL2M "BTN SL2M" //--- Define the name for the button that greatly lower Stop Loss
#define BTN_SL1P "BTN SL1P" //--- Define the name for the button that slightly raise Stop Loss
#define BTN_SL2P "BTN SL2P" //--- Define the name for the button that greatly raise Stop Loss
#define BTN_TP "BTN TP" //--- Define the name for the Take Profit display button
#define BTN_TP1M "BTN TP1M" //--- Define the name for the button that slightly lower Take Profit
#define BTN_TP2M "BTN TP2M" //--- Define the name for the button that greatly lower Take Profit
#define BTN_TP1P "BTN TP1P" //--- Define the name for the button that slightly raise Take Profit
#define BTN_TP2P "BTN TP2P" //--- Define the name for the button that greatly raise Take Profit
#define BTN_YES "BTN YES" //--- Define the name for the button that confirm a trade
#define BTN_NO "BTN NO" //--- Define the name for the button that cancel a trade
#define BTN_IDLE "BTN IDLE" //--- Define the name for the idle button between Yes and No
#define HL_SL "HL SL" //--- Define the name for the Stop Loss horizontal line
#define HL_TP "HL TP" //--- Define the name for the Take Profit horizontal line

#include <Trade/Trade.mqh> //--- Bring in the Trade library needed for trading functions
CTrade obj_Trade; //--- Create a trading object to handle trade operations

bool tradeInAction = false; //--- Track whether a trade setup is currently active
bool isHaveTradeLevels = false; //--- Track whether Stop Loss and Take Profit levels are shown

input double init_lot = 0.03;
input int slow_pts = 10;
input int fast_pts = 100;

Hier beginnen wir mit der Definition einer Reihe interaktiver Schaltflächen wie „BTN_BUY“ und „BTN_SELL“ unter Verwendung des Schlüsselworts #define, um den Handel zu starten, wann immer wir wollen, was uns eine direkte Kontrolle über die Einstiegspunkte gibt, während wir mit „BTN_P“ und „BTN_M“ die „init_lot“-Größe - anfangs auf 0,03 - nach oben oder unten anpassen können, um unserer Risikobereitschaft zu entsprechen. „BTN_CLOSE“ ist unser Notausstieg, mit dem wir alle Positionen im Handumdrehen schließen können, und „tradeInAction“ zeigt uns an, ob wir gerade einen Handel einrichten, und „isHaveTradeLevels“ signalisiert uns, wann Stop Loss und Take Profit aktiv sind.

Anschließend greifen wir auf die Klasse „CTrade“ aus „<Trade/Trade.mqh>“ zurück, um ein „obj_Trade“-Objekt zu erstellen, das eine reibungslose und effiziente Ausführung des Handels ermöglicht. Um uns noch mehr Flexibilität zu geben, fügen wir anpassbare Eingaben wie „slow_pts“ bei 10 und „fast_pts“ bei 100 hinzu, sodass wir unsere Stop-Loss- und Take-Profit-Levels im laufenden Betrieb feinabstimmen können, um sicherzustellen, dass sich unser Toolkit an die jeweilige Strategie anpasst, die wir testen. Da wir nun die Schaltflächen des Panels erstellen müssen, erstellen wir eine Funktion mit allen möglichen Eingaben, um sie wiederverwenden und anpassen zu können.

//+------------------------------------------------------------------+
//| Create button function                                           |
//+------------------------------------------------------------------+
void CreateBtn(string objName,int xD,int yD,int xS,int yS,string txt,
               int fs=13,color clrTxt=clrWhite,color clrBg=clrBlack,
               color clrBd=clrBlack,string font="Calibri"){
   ObjectCreate(0,objName,OBJ_BUTTON,0,0,0); //--- Create a new button object on the chart
   ObjectSetInteger(0,objName,OBJPROP_XDISTANCE, xD); //--- Set the button's horizontal position
   ObjectSetInteger(0,objName,OBJPROP_YDISTANCE, yD); //--- Set the button's vertical position
   ObjectSetInteger(0,objName,OBJPROP_XSIZE, xS); //--- Set the button's width
   ObjectSetInteger(0,objName,OBJPROP_YSIZE, yS); //--- Set the button's height
   ObjectSetInteger(0,objName,OBJPROP_CORNER, CORNER_LEFT_UPPER); //--- Position the button from the top-left corner
   ObjectSetString(0,objName,OBJPROP_TEXT, txt); //--- Set the text displayed on the button
   ObjectSetInteger(0,objName,OBJPROP_FONTSIZE, fs); //--- Set the font size of the button text
   ObjectSetInteger(0,objName,OBJPROP_COLOR, clrTxt); //--- Set the color of the button text
   ObjectSetInteger(0,objName,OBJPROP_BGCOLOR, clrBg); //--- Set the background color of the button
   ObjectSetInteger(0,objName,OBJPROP_BORDER_COLOR,clrBd); //--- Set the border color of the button
   ObjectSetString(0,objName,OBJPROP_FONT,font); //--- Set the font style of the button text

   ChartRedraw(0); //--- Refresh the chart to show the new button
}

Hier definieren wir die Funktion „CreateBtn“, um jede Schaltfläche - wie „BTN_BUY“ oder „BTN_SELL“ - im Chart zu erstellen, wobei wir Eingaben wie „objName“ für die Identität der Schaltfläche, „xD“ und „yD“ für die horizontale und vertikale Position, „xS“ und „yS“ für die Breite und Höhe und „txt“ für die Beschriftung, die angezeigt werden soll, wie „BUY“ oder „SELL“. Um dies zu erreichen, verwenden wir die Funktion ObjectCreate, um ein neues OBJ_BUTTON-Objekt auf dem Chart zu platzieren, wobei wir der Einfachheit halber die Koordinaten (0,0,0) als Basis festlegen. Dann positionieren wir es genau mit ObjectSetInteger, um „OBJPROP_XDISTANCE“ auf „xD“ und „OBJPROP_YDISTANCE“ auf „yD“ einzustellen. So stellen wir sicher, dass es genau dort sitzt, wo wir es brauchen, und wir passen die Größe mit „OBJPROP_XSIZE“ für „xS“ und „OBJPROP_YSIZE“ für „yS“ an unser Design an.

Wir verankern es in der linken oberen Ecke mit „OBJPROP_CORNER“, das auf CORNER_LEFT_UPPER gesetzt ist, um das Layout konsistent zu machen, und wir verwenden ObjectSetString, um „OBJPROP_TEXT“ als „txt“ zuzuweisen, damit die Schaltfläche ihren Zweck deutlich macht. Für den Stil stellen wir „OBJPROP_FONTSIZE“ auf „fs“ (Standardwert: 13), „OBJPROP_COLOR“ auf „clrTxt“ (Standardwert: weiß) für den Text, OBJPROP_BGCOLOR auf „clrBg“ (standardmäßig schwarz) für den Hintergrund und „OBJPROP_BORDER_COLOR“ auf „clrBd“ (standardmäßig schwarz) für die Umrandung, während „OBJPROP_FONT“ „font“ (standardmäßig „Calibri“) für ein sauberes Aussehen erhält. Zum Schluss verwenden wir die Funktion ChartRedraw, um das Chart mit der Fenster-ID „0“ zu aktualisieren, sodass unsere neue Schaltfläche sofort angezeigt wird und wir mit ihr im Strategy Tester interagieren können. Wir können die Funktion nun immer dann aufrufen, wenn wir eine Schaltfläche erstellen wollen, und wir beginnen damit, sie im OnInit-Ereignishandler aufzurufen.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   CreateBtn(BTN_P,150,45,40,25,CharToString(217),15,clrBlack,clrWhite,clrBlack,"Wingdings"); //--- Make the button to increase lot size with an up arrow
   CreateBtn(BTN_LOT,190,45,60,25,string(init_lot),12,clrWhite,clrGray,clrBlack); //--- Make the button showing the current lot size
   CreateBtn(BTN_M,250,45,40,25,CharToString(218),15,clrBlack,clrWhite,clrBlack,"Wingdings"); //--- Make the button to decrease lot size with a down arrow
   CreateBtn(BTN_BUY,110,70,110,30,"BUY",15,clrWhite,clrGreen,clrBlack); //--- Make the Buy button with a green background
   CreateBtn(BTN_SELL,220,70,110,30,"SELL",15,clrWhite,clrRed,clrBlack); //--- Make the Sell button with a red background
   CreateBtn(BTN_CLOSE,110,100,220,30,"PANIC BUTTON (X)",15,clrWhite,clrBlack,clrBlack); //--- Make the emergency button to close all trades
         
   return(INIT_SUCCEEDED); //--- Tell the system the EA start up successfully
}

Hier beginnen wir unser Toolkit für die manuellen Backtests mit dem OnInit und richten seine Schnittstelle im Strategy Tester ein. Wir verwenden die Funktion „CreateBtn“, um „BTN_P“ bei „xD“ 150, „yD“ 45 mit einem Aufwärtspfeil von CharToString(217) in „Wingdings“, „BTN_LOT“ bei „xD“ 190 mit „init_lot“ und „BTN_M“ bei „xD“ 250 mit einem Abwärtspfeil von „CharToString(218)“ - alle für die Kontrolle der Losgröße gestylt. Dann fügen wir „BTN_BUY“ bei „xD“ 110, „yD“ 70 mit „BUY“ auf „clrGreen“, „BTN_SELL“ bei „xD“ 220 mit „SELL“ auf „clrRed“, und „BTN_CLOSE“ bei „xD“ 110, „yD“ 100 als „PANIC BUTTON (X)“ auf „clrBlack“, bevor der Erfolg mit „return“ und INIT_SUCCEEDED gemeldet wird. Die Wingdings-Schriftart, die wir für die Icons verwenden, stammt aus der bereits definierten Zeichentabelle von MQL5, die unten abgebildet ist.

WINGDINGS

Wenn wir das Programm ausführen, erhalten wir die folgende Ausgabe.

SCHNITTSTELLE DER BUTTONS

Da wir den grundlegenden Hintergrund festgelegt haben, müssen wir die Schaltflächenzustände und die Werte auslesen, damit wir sie für Handelszwecke verwenden können. Wir brauchen also auch dafür einige Funktionen.

int GetState(string Name){return (int)ObjectGetInteger(0,Name,OBJPROP_STATE);} //--- Get whether a button is pressed or not
string GetValue(string Name){return ObjectGetString(0,Name,OBJPROP_TEXT);} //--- Get the text shown on an object
double GetValueHL(string Name){return ObjectGetDouble(0,Name,OBJPROP_PRICE);} //--- Get the price level of a horizontal line

Hier definieren wir die Funktion „GetState“, um Schaltflächenklicks zu überprüfen, wobei wir die Funktion ObjectGetInteger mit „OBJPROP_STATE“ verwenden, um zurückzugeben, ob „Name“ gedrückt wurde, „GetValue“ um den Text von „Name“ mit „ObjectGetString“ mit „OBJPROP_TEXT“ zu holen, und „GetValueHL“, um die Preisstufen von „Name“ mit ObjectGetDouble mit OBJPROP_PRICE für eine präzise Handelskontrolle zu erfassen. Wir können nun die Funktionen verwenden, um die Schaltflächenzustände in OnTick zu erhalten, da wir die Ereignisbehandlung von OnChartEvent im Strategy Tester nicht direkt verwenden können. Wie wir das erreichen, erfahren Sie hier.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get the current Ask price and adjust it to the right decimal places
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get the current Bid price and adjust it to the right decimal places

   if (GetState(BTN_BUY)==true || GetState(BTN_SELL)){ //--- Check if either the Buy or Sell button is clicked
      tradeInAction = true; //--- Set trade setup to active
   }
}

Hier verwenden wir OnTick, um die Echtzeit-Aktionen unseres Toolkits im Strategietester zu steuern, wo wir die Funktion NormalizeDouble mit SymbolInfoDouble verwenden, um „Ask“ auf den aktuellen „SYMBOL_ASK“ und „Bid“ auf den SYMBOL_BID-Kurs zu setzen. Preis und „Bid“ auf SYMBOL_BID, beide auf die Genauigkeit von _Digits angepasst, und, wenn „GetState“ „BTN_BUY“ oder „BTN_SELL“ wahr sind, setzen wir „tradeInAction“ auf wahr, um unseren Handelsaufbau zu starten. Dies ist der Punkt, an dem wir zusätzliche Handelsstufen benötigen, damit wir die Stufen festlegen und dynamisch anpassen können. Dafür brauchen wir eine Funktion.

//+------------------------------------------------------------------+
//| Create high low function                                         |
//+------------------------------------------------------------------+
void createHL(string objName,datetime time1,double price1,color clr){
   if (ObjectFind(0,objName) < 0){ //--- Check if the horizontal line doesn’t already exist
      ObjectCreate(0,objName,OBJ_HLINE,0,time1,price1); //--- Create a new horizontal line at the specified price
      ObjectSetInteger(0,objName,OBJPROP_TIME,time1); //--- Set the time property (though not critical for HLINE)
      ObjectSetDouble(0,objName,OBJPROP_PRICE,price1); //--- Set the price level of the horizontal line
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr); //--- Set the color of the line (red for SL, green for TP)
      ObjectSetInteger(0,objName,OBJPROP_STYLE,STYLE_DASHDOTDOT); //--- Set the line style to dash-dot-dot
      ChartRedraw(0); //--- Refresh the chart to display the new line
   }
}

Zunächst definieren wir die Funktion „createHL“, um horizontale Linien für unser Toolkit im Strategietester zu zeichnen, wobei wir die Funktion ObjectFind verwenden, um zu prüfen, ob „objName“ existiert, und wenn das Ergebnis kleiner als 0 ist, verwenden wir die Funktion ObjectCreate, um eine „OBJ_HLINE“ bei „time1“ und „price1“ zu erstellen. Wir verwenden die Funktion ObjectSetInteger, um „OBJPROP_TIME“ auf „time1“ und „OBJPROP_COLOR“ auf „clr“ und „OBJPROP_STYLE“ auf „STYLE_DASHDOTDOT“ mit der Funktion ObjectSetDouble und „OBJPROP_PRICE“ auf „price1“ zu setzen, und mit der Funktion ChartRedraw aktualisieren wir das Chart mit „0“, um es anzuzeigen. Dann integrieren wir diese Funktion in eine andere Funktion zur nahtlosen Erstellung der Handelsstufen wie unten dargestellt.

//+------------------------------------------------------------------+
//| Create trade levels function                                     |
//+------------------------------------------------------------------+
void CreateTradeLevels(){
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get unnoticed the current Ask price, adjusted for digits
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get the current Bid price, adjusted for digits
   
   string level_SL,level_TP; //--- Declare variables to hold SL and TP levels as strings
   if (GetState(BTN_BUY)==true){ //--- Check if the Buy button is active
      level_SL = string(Bid-100*_Point); //--- Set initial Stop Loss 100 points below Bid for Buy
      level_TP = string(Bid+100*_Point); //--- Set initial Take Profit 100 points above Bid for Buy
   }
   else if (GetState(BTN_SELL)==true){ //--- Check if the Sell button is active
      level_SL = string(Ask+100*_Point); //--- Set initial Stop Loss 100 points above Ask for Sell
      level_TP = string(Ask-100*_Point); //--- Set initial Take Profit 100 points below Ask for Sell
   }
   
   createHL(HL_SL,0,double(level_SL),clrRed); //--- Create a red Stop Loss line at the calculated level
   createHL(HL_TP,0,double(level_TP),clrGreen); //--- Create a green Take Profit line at the calculated level

   CreateBtn(BTN_SL,110,135,110,23,"SL: "+GetValue(HL_SL),13,clrRed,clrWhite,clrRed); //--- Make a button showing the Stop Loss level
   CreateBtn(BTN_TP,220,135,110,23,"TP: "+GetValue(HL_TP),13,clrGreen,clrWhite,clrGreen); //--- Make a button showing the Take Profit level
   
   CreateBtn(BTN_SL1M,110,158,27,20,"-",17,clrBlack,clrWhite,clrGray); //--- Make a button to slightly lower Stop Loss
   CreateBtn(BTN_SL2M,137,158,27,20,"--",17,clrBlack,clrWhite,clrGray); //--- Make a button to greatly lower Stop Loss
   CreateBtn(BTN_SL2P,164,158,27,20,"++",17,clrBlack,clrWhite,clrGray); //--- Make a button to greatly raise Stop Loss
   CreateBtn(BTN_SL1P,191,158,27,20,"+",17,clrBlack,clrWhite,clrGray); //--- Make a button to slightly raise Stop Loss
   
   CreateBtn(BTN_TP1P,222,158,27,20,"+",17,clrBlack,clrWhite,clrGray); //--- Make a button to slightly raise Take Profit
   CreateBtn(BTN_TP2P,249,158,27,20,"++",17,clrBlack,clrWhite,clrGray); //--- Make a button to greatly raise Take Profit
   CreateBtn(BTN_TP2M,276,158,27,20,"--",17,clrBlack,clrWhite,clrGray); //--- Make a button to greatly lower Take Profit
   CreateBtn(BTN_TP1M,303,158,27,20,"-",17,clrBlack,clrWhite,clrGray); //--- Make a button to slightly lower Take Profit
   
   CreateBtn(BTN_YES,110,178,70,30,CharToString(254),20,clrWhite,clrDarkGreen,clrWhite,"Wingdings"); //--- Make a green checkmark button to confirm the trade
   CreateBtn(BTN_NO,260,178,70,30,CharToString(253),20,clrWhite,clrDarkRed,clrWhite,"Wingdings"); //--- Make a red X button to cancel the trade
   CreateBtn(BTN_IDLE,180,183,80,25,CharToString(40),20,clrWhite,clrBlack,clrWhite,"Wingdings"); //--- Make a neutral button between Yes and No
}

Hier definieren wir die Funktion „CreateTradeLevels“, um unsere Handelsstufen einzurichten, wobei wir die Funktion NormalizeDouble mit SymbolInfoDouble verwenden, um „Ask“ auf „SYMBOL_ASK“ und „Bid“ auf „SYMBOL_BID“ zu setzen, angepasst auf _Digits Nachkommastellen, und „level_SL“ und „level_TP“ als Strings deklarieren. Wenn „GetState“ „BTN_BUY“ als wahr anzeigt, setzen wir „level_SL“ auf „Bid-100_Point“ und „level_TP“ auf „Bid+100_Point“, aber wenn „BTN_SELL“ wahr ist, setzen wir „level_SL“ auf „Ask+100_Point“ und „level_TP“ auf „Ask-100_Point“.

Wir verwenden die Funktion „createHL“, um „HL_SL“ mit „double(level_SL)“ in „clrRed“ und „HL_TP“ mit „double(level_TP)“ in „clrGreen“ zu zeichnen. Dann verwenden wir die Funktion „CreateBtn“, um Schaltflächen wie „BTN_SL“ mit dem Text „GetValue(HL_SL)“, „BTN_TP“ mit „GetValue(HL_TP)“ und Anpassungsschaltflächen „BTN_SL1M“, „BTN_SL2M“, „BTN_SL2P“, „BTN_SL1P“, „BTN_TP1P“, „BTN_TP2P“, „BTN_TP2M“, und „BTN_TP1M“ mit Symbolen wie „-“ und „+“ sowie „BTN_YES“, „BTN_NO“ und „BTN_IDLE“ unter Verwendung von CharToString für Bestätigungs-, Abbruch- und neutrale Optionen in „Wingdings“. Mit der Funktion können wir sie aufrufen, wenn die Schaltflächen für Kaufen oder Verkaufen angeklickt werden, um die Einrichtung der Handelsstufe zu initialisieren.

if (!isHaveTradeLevels){ //--- Check if trade levels aren't already on the chart
   CreateTradeLevels(); //--- Add Stop Loss and Take Profit levels and controls to the chart
   isHaveTradeLevels = true; //--- Mark that trade levels are now present
}

Wenn dies der Fall ist, verwenden wir die Funktion „CreateTradeLevels“, um Stop-Loss- und Take-Profit-Kontrollen auf dem Chart zu platzieren, und aktualisieren dann „isHaveTradeLevels“ auf true, um anzuzeigen, dass sie aktiv sind. Nach der Kompilierung erhalten wir folgendes Ergebnis.

HANDELSEBENE

Als Nächstes müssen wir die Schaltflächen für die Handelsstufen zum Leben erwecken, indem wir dafür sorgen, dass sie reagieren und das tun, was sie tun müssen. Wie wir das erreichen, erfahren Sie hier.

if (tradeInAction){ //--- Continue if a trade setup is active

   // SL SLOW/FAST BUTTONS
   if (GetState(BTN_SL1M)){ //--- Check if the small Stop Loss decrease button is clicked
      ObjectSetDouble(0,HL_SL,OBJPROP_PRICE,GetValueHL(HL_SL)-slow_pts*_Point); //--- Move the Stop Loss down by a small amount
      ObjectSetInteger(0,BTN_SL1M,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }
   if (GetState(BTN_SL2M)){ //--- Check if the large Stop Loss decrease button is clicked
      ObjectSetDouble(0,HL_SL,OBJPROP_PRICE,GetValueHL(HL_SL)-fast_pts*_Point); //--- Move the Stop Loss down by a large amount
      ObjectSetInteger(0,BTN_SL2M,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }
   if (GetState(BTN_SL1P)){ //--- Check if the small Stop Loss increase button is clicked
      ObjectSetDouble(0,HL_SL,OBJPROP_PRICE,GetValueHL(HL_SL)+slow_pts*_Point); //--- Move the Stop Loss up by a small amount
      ObjectSetInteger(0,BTN_SL1P,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }
   if (GetState(BTN_SL2P)){ //--- Check if the large Stop Loss increase button is clicked
      ObjectSetDouble(0,HL_SL,OBJPROP_PRICE,GetValueHL(HL_SL)+fast_pts*_Point); //--- Move the Stop Loss up by a large amount
      ObjectSetInteger(0,BTN_SL2P,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }
   
   // TP SLOW/FAST BUTTONS
   if (GetState(BTN_TP1M)){ //--- Check if the small Take Profit decrease button is clicked
      ObjectSetDouble(0,HL_TP,OBJPROP_PRICE,GetValueHL(HL_TP)-slow_pts*_Point); //--- Move the Take Profit down by a small amount
      ObjectSetInteger(0,BTN_TP1M,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }
   if (GetState(BTN_TP2M)){ //--- Check if the large Take Profit decrease button is clicked
      ObjectSetDouble(0,HL_TP,OBJPROP_PRICE,GetValueHL(HL_TP)-fast_pts*_Point); //--- Move the Take Profit down by a large amount
      ObjectSetInteger(0,BTN_TP2M,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }
   if (GetState(BTN_TP1P)){ //--- Check if the small Take Profit increase button is clicked
      ObjectSetDouble(0,HL_TP,OBJPROP_PRICE,GetValueHL(HL_TP)+slow_pts*_Point); //--- Move the Take Profit up by a small amount
      ObjectSetInteger(0,BTN_TP1P,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }
   if (GetState(BTN_TP2P)){ //--- Check if the large Take Profit increase button is clicked
      ObjectSetDouble(0,HL_TP,OBJPROP_PRICE,GetValueHL(HL_TP)+fast_pts*_Point); //--- Move the Take Profit up by a large amount
      ObjectSetInteger(0,BTN_TP2P,OBJPROP_STATE,false); //--- Turn off the button press state
      ChartRedraw(0); //--- Refresh the chart to show the change
   }

}

Hier verwalten wir Stop Loss- und Take Profit-Anpassungen in unserem Toolkit, wenn „tradeInAction“ wahr ist, wobei wir die Funktion „GetState“ verwenden, um zu prüfen, ob Schaltflächen wie „BTN_SL1M“, „BTN_SL2M“, „BTN_SL1P“ oder „BTN_SL2P“ angeklickt werden, und passen „HL_SL“, „slow_pts_Point“ oder „fast_pts_Point“ an. Wir verwenden die Funktion ObjectSetDouble mit „OBJPROP_PRICE“ und „GetValueHL“, setzen dann OBJPROP_STATE mit der Funktion ObjectSetInteger auf false zurück und aktualisieren das Chart mit der Funktion ChartRedraw, und passen „BTN_TP1M“, „BTN_TP2M“, „BTN_TP1P“ oder „BTN_TP2P“ für „HL_TP“ an. Sobald die Levels festgelegt sind, können wir die Platzierung bestätigen und die entsprechenden Positionen eröffnen. Anschließend können wir das Handelslevel-Setup löschen, aber zuerst brauchen wir eine Funktion zum Löschen des Level-Setup-Panels.

//+------------------------------------------------------------------+
//| Delete objects function                                          |
//+------------------------------------------------------------------+
void DeleteObjects_SLTP(){
   ObjectDelete(0,HL_SL); //--- Remove the Stop Loss line from the chart
   ObjectDelete(0,HL_TP); //--- Remove the Take Profit line from the chart
   ObjectDelete(0,BTN_SL); //--- Remove the Stop Loss display button
   ObjectDelete(0,BTN_TP); //--- Remove the Take Profit display button
   ObjectDelete(0,BTN_SL1M); //--- Remove the small Stop Loss decrease button
   ObjectDelete(0,BTN_SL2M); //--- Remove the large Stop Loss decrease button
   ObjectDelete(0,BTN_SL1P); //--- Remove the small Stop Loss increase button
   ObjectDelete(0,BTN_SL2P); //--- Remove the large Stop Loss increase button
   ObjectDelete(0,BTN_TP1P); //--- Remove the small Take Profit increase button
   ObjectDelete(0,BTN_TP2P); //--- Remove the large Take Profit increase button
   ObjectDelete(0,BTN_TP2M); //--- Remove the large Take Profit decrease button
   ObjectDelete(0,BTN_TP1M); //--- Remove the small Take Profit decrease button
   ObjectDelete(0,BTN_YES); //--- Remove the confirm trade button
   ObjectDelete(0,BTN_NO); //--- Remove the cancel trade button
   ObjectDelete(0,BTN_IDLE); //--- Remove the idle button
   
   ChartRedraw(0); //--- Refresh the chart to show all objects removed
}

Hier behandeln wir die Bereinigung in unserem Toolkit mit der Funktion „DeleteObjects_SLTP“, wobei wir die Funktion ObjectDelete verwenden, um „HL_SL,“ „HL_TP,“ „BTN_SL,“ „BTN_TP,“ „BTN_SL1M,“ „BTN_SL2M,“ „BTN_SL1P,“ „BTN_SL2P“, „BTN_TP1P“, „BTN_TP2P“, „BTN_TP2M“, „BTN_TP1M“, „BTN_YES“, „BTN_NO“ und „BTN_IDLE“ aus dem Chart entfernen und verwenden dann die Funktion ChartRedraw mit „0“, um alles zu aktualisieren und zu löschen. Wir können diese Funktion nun in unserer Auftragsvergabe-Logik verwenden.

// BUY ORDER PLACEMENT
if (GetState(BTN_BUY) && GetState(BTN_YES)){ //--- Check if both Buy and Yes buttons are clicked
   obj_Trade.Buy(double(GetValue(BTN_LOT)),_Symbol,Ask,GetValueHL(HL_SL),GetValueHL(HL_TP)); //--- Place a Buy order with set lot size, SL, and TP
   DeleteObjects_SLTP(); //--- Remove all trade level objects from the chart
   isHaveTradeLevels = false; //--- Mark that trade levels are no longer present
   ObjectSetInteger(0,BTN_YES,OBJPROP_STATE,false); //--- Turn off the Yes button press state
   ObjectSetInteger(0,BTN_BUY,OBJPROP_STATE,false); //--- Turn off the Buy button press state
   tradeInAction = false; //--- Mark the trade setup as complete
   ChartRedraw(0); //--- Refresh the chart to reflect changes
}
// SELL ORDER PLACEMENT
else if (GetState(BTN_SELL) && GetState(BTN_YES)){ //--- Check if both Sell and Yes buttons are clicked
   obj_Trade.Sell(double(GetValue(BTN_LOT)),_Symbol,Bid,GetValueHL(HL_SL),GetValueHL(HL_TP)); //--- Place a Sell order with set lot size, SL, and TP
   DeleteObjects_SLTP(); //--- Remove all trade level objects from the chart
   isHaveTradeLevels = false; //--- Mark that trade levels are no longer present
   ObjectSetInteger(0,BTN_YES,OBJPROP_STATE,false); //--- Turn off the Yes button press state
   ObjectSetInteger(0,BTN_SELL,OBJPROP_STATE,false); //--- Turn off the Sell button press state
   tradeInAction = false; //--- Mark the trade setup as complete
   ChartRedraw(0); //--- Refresh the chart to reflect changes
}
else if (GetState(BTN_NO)){ //--- Check if the No button is clicked to cancel
   DeleteObjects_SLTP(); //--- Remove all trade level objects from the chart
   isHaveTradeLevels = false; //--- Mark that trade levels are no longer present
   ObjectSetInteger(0,BTN_NO,OBJPROP_STATE,false); //--- Turn off the No button press state
   ObjectSetInteger(0,BTN_BUY,OBJPROP_STATE,false); //--- Turn off the Buy button press state
   ObjectSetInteger(0,BTN_SELL,OBJPROP_STATE,false); //--- Turn off the Sell button press state
   tradeInAction = false; //--- Mark the trade setup as canceled
   ChartRedraw(0); //--- Refresh the chart to reflect changes
}

Wir führen die Handelsgeschäfte in unserem Toolkit innerhalb des Strategy Testers aus, wo wir die Funktion „GetState“ verwenden, um zu prüfen, ob „BTN_BUY“ und „BTN_YES“ wahr sind und dann die Methode „obj_Trade.Buy“ mit „double(GetValue(BTN_LOT))“, _Symbol, „Ask“, „GetValueHL(HL_SL)“ und „GetValueHL(HL_TP)“, um einen Kaufauftrag zu erteilen, oder wenn „BTN_SELL“ und „BTN_YES“ wahr sind, verwenden wir stattdessen „obj_Trade.Sell“ mit „Bid“ stattdessen, und in beiden Fällen verwenden wir die Funktion „DeleteObjects_SLTP“, um Objekte zu löschen, setzen „isHaveTradeLevels“ und „tradeInAction“ auf false, verwenden die Funktion ObjectSetInteger, um OBJPROP_STATE auf „BTN_YES“, „BTN_BUY“ oder „BTN_SELL“ auf false zurückzusetzen, und die Funktion ChartRedraw zu verwenden, um das Chart zu aktualisieren, aber wenn „BTN_NO“ wahr ist, brechen wir den Vorgang ab, indem wir die Objekte löschen und die Zustände auf ähnliche Weise zurücksetzen. In ähnlicher Weise behandeln wir die Tasten zur Erhöhung oder Verringerung des Handelsvolumens:

if (GetState(BTN_P)==true){ //--- Check if the lot size increase button is clicked
   double newLot = (double)GetValue(BTN_LOT); //--- Get the current lot size as a number
   double lotStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); //--- Get the minimum lot size change allowed
   newLot += lotStep; //--- Increase the lot size by one step
   newLot = NormalizeDouble(newLot,2); //--- Round the new lot size to 2 decimal places
   newLot = newLot > 0.1 ? lotStep : newLot; //--- Ensure lot size doesn't exceed 0.1, otherwise reset to step
   ObjectSetString(0,BTN_LOT,OBJPROP_TEXT,string(newLot)); //--- Update the lot size display with the new value
   ObjectSetInteger(0,BTN_P,OBJPROP_STATE,false); //--- Turn off the increase button press state
   ChartRedraw(0); //--- Refresh the chart to show the new lot size
}
if (GetState(BTN_M)==true){ //--- Check if the lot size decrease button is clicked
   double newLot = (double)GetValue(BTN_LOT); //--- Get the current lot size as a number
   double lotStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); //--- Get the minimum lot size change allowed
   newLot -= lotStep; //--- Decrease the lot size by one step
   newLot = NormalizeDouble(newLot,2); //--- Round the new lot size to 2 decimal places
   newLot = newLot < lotStep ? lotStep : newLot; //--- Ensure lot size doesn't go below minimum, otherwise set to step
   ObjectSetString(0,BTN_LOT,OBJPROP_TEXT,string(newLot)); //--- Update the lot size display with the new value
   ObjectSetInteger(0,BTN_M,OBJPROP_STATE,false); //--- Turn off the decrease button press state
   ChartRedraw(0); //--- Refresh the chart to show the new lot size
}

Hier passen wir die Losgrößen an, beginnend mit der Erhöhung, bei der wir die Funktion „GetState“ verwenden, um zu prüfen, ob „BTN_P“ wahr ist, dann verwenden wir „GetValue“, um „newLot“ aus „BTN_LOT“ zu setzen“ und SymbolInfoDouble, um „lotStep“ aus SYMBOL_VOLUME_STEP zu erhalten, addieren „lotStep“ zu „newLot“ und verwenden NormalizeDouble, um den Wert auf 2 Dezimalstellen zu runden und ihn bei „lotStep“ zu begrenzen, wenn er über 0.1 liegt, bevor wir ObjectSetString verwenden, um „OBJPROP_TEXT“ von „BTN_LOT“ zu aktualisieren, und ObjectSetInteger, um den „OBJPROP_STATE“ von „BTN_P“ auf false zurückzusetzen, gefolgt von ChartRedraw zur Aktualisierung.

Für die Reduktion verwenden wir „GetState“, um „BTN_M“ zu überprüfen, subtrahieren „lotStep“ von „newLot“, nachdem wir es auf die gleiche Weise geholt haben, und halten es bei mindestens „lotStep“, und wenden die gleichen Funktionsschritte ObjectSetString, „ObjectSetInteger“ und „ChartRedraw“ an, um „BTN_LOT“ zu aktualisieren und „BTN_M“ zurückzusetzen. Für die Paniktaste müssen wir eine Funktion definieren, die alle offenen Positionen schließt, wenn sie angeklickt wird.

//+------------------------------------------------------------------+
//| Close all positions function                                     |
//+------------------------------------------------------------------+
void closeAllPositions(){
   for (int i=PositionsTotal()-1; i>=0; i--){ //--- Loop through all open positions, starting from the last one
      ulong ticket = PositionGetTicket(i); //--- Get the ticket number of the current position
      if (ticket > 0){ //--- Check if the ticket is valid
         if (PositionSelectByTicket(ticket)){ //--- Select the position by its ticket number
            if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position is for the current chart symbol
               obj_Trade.PositionClose(ticket); //--- Close the selected position
            }
         }
      }
   }
}

Wir behandeln das Schließen aller Positionen mit der Funktion „closeAllPositions“, wobei wir die Funktion PositionsTotal verwenden, um eine Schleife von der letzte Position, „i-1“, bis 0 zu bilden, dann die Funktion PositionGetTicket für die Tickets des Index „i“, und, wenn das Ticket gültig ist, verwenden wir PositionSelectByTicket, um die Position auszuwählen. Dann verwenden wir PositionGetString, um zu prüfen, ob POSITION_SYMBOL mit _Symbol übereinstimmt, bevor wir mit der Methode „obj_Trade.PositionClose“ die Position mit dem Ticket schließen. Dann können wir diese Funktion aufrufen, wenn der Panikknopf angeklickt wird, um alle Positionen zu schließen.

if (GetState(BTN_CLOSE)==true){ //--- Check if the close all positions button is clicked
   closeAllPositions(); //--- Close all open trades
   ObjectSetInteger(0,BTN_CLOSE,OBJPROP_STATE,false); //--- Turn off the close button press state
   ChartRedraw(0); //--- Refresh the chart to reflect closed positions
}

Um die Schließung aller Geschäfte zu verwalten, verwenden wir die Funktion „GetState“, um zu prüfen, ob „BTN_CLOSE“ wahr ist, und wenn ja, verwenden wir die Funktion „closeAllPositions“, um alle offenen Positionen zu schließen, dann verwenden wir die Funktion ObjectSetInteger, um den OBJPROP_STATE von „BTN_CLOSE“ auf false zu setzen, und die Funktion ChartRedraw mit „0“, um das Chart zu aktualisieren. Nach dem Kompilieren und Ausführen des Programms erhalten wir das folgende Ergebnis.

ENDGÜLTIGES ERGEBNIS

Aus dem Bild können wir ersehen, dass wir die Handelsniveaus festlegen und Positionen dynamisch eröffnen können, um unser Ziel zu erreichen. Jetzt muss das Programm nur noch gründlich getestet werden, und das wird im nächsten Punkt behandelt.


Backtests in Aktion: Verwendung des Toolkits

Wir testen unser Toolkit im Strategy Tester von MetaTrader 5, indem wir das Programm laden, unsere Einstellungen auswählen und es starten - schauen Sie sich das Graphics Interchange Format (GIF) unten an, um die Schaltflächen Kaufen, Verkaufen und Anpassen blitzschnell in Aktion zu sehen. Klicken Sie auf Kaufen oder Verkaufen, passen Sie Stop Loss, Take Profit und Losgröße an und bestätigen Sie dann mit Ja oder brechen Sie den Handel mit Nein ab. Hier ist sie.

TESTER BACKTEST GIF


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir ein Toolkits für die manuellen Backtests entwickelt haben, das die praktische Kontrolle mit der Geschwindigkeit des Strategy Testers in MQL5 verbindet und das Testen von Handelsideen vereinfacht. Wir haben gezeigt, wie man es entwirft, programmiert und verwendet, um den Handel mit Schaltflächen anzupassen - alles maßgeschneidert für schnelle, präzise Simulationen. Sie können dieses Toolkit an Ihre Bedürfnisse anpassen und Ihre Backtest-Erfahrung damit verbessern.

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

Letzte Kommentare | Zur Diskussion im Händlerforum (6)
Allan Munene Mutiiria
Allan Munene Mutiiria | 16 Apr. 2025 in 14:31
Mogulh Chilyalya Kiti #:
Wie sieht es mit unterschiedlichen Zeitrahmen aus?

Hallo. Derzeit nur ein Zeitrahmen. Vielleicht versuchen, dass in naher Zukunft.

Blessing Dumbura
Blessing Dumbura | 16 Apr. 2025 in 15:47
Vielen Dank für dieses nützliche Instrument
Allan Munene Mutiiria
Allan Munene Mutiiria | 16 Apr. 2025 in 18:26
Blessing Dumbura #:
Danke, es ist ein nützliches Werkzeug

Sicher. Willkommen und danke auch für das Feedback.

Dontrace
Dontrace | 7 Mai 2025 in 11:08

Ich wollte wissen, wie man einen mq4-Indikator in einen mq5-Indikator umwandelt.

zigooo
zigooo | 17 Juni 2025 in 17:16
Ich habe versucht, es so zu modifizieren, dass es schnell Aufträge mit Lotsize und tp/sl öffnen kann, entsprechend den EA-Eingabeparametern. Es funktioniert im realen Markt, aber nicht im Backtest-Modus. Was ist die Lösung?
Klassische Strategien neu interpretieren (Teil 14): Hochwahrscheinliche Setups Klassische Strategien neu interpretieren (Teil 14): Hochwahrscheinliche Setups
Hochwahrscheinliche Setups sind in unserer Trading-Community gut bekannt, aber leider sind sie nicht gut definiert. In diesem Artikel wollen wir einen empirischen und algorithmischen Weg finden, um genau zu definieren, was ein Hochwahrscheinlichkeits-Setup ist, und um diese zu identifizieren und auszunutzen. Durch die Verwendung von Gradient Boosting Trees haben wir gezeigt, wie der Leser die Leistung einer beliebigen Handelsstrategie verbessern und unserem Computer die genaue Aufgabe auf sinnvollere und explizitere Weise mitteilen kann.
Handel mit dem MQL5 Wirtschaftskalender (Teil 7): Vorbereitung auf Strategietests mit der ressourcenbasierten Analyse von Nachrichtenereignissen Handel mit dem MQL5 Wirtschaftskalender (Teil 7): Vorbereitung auf Strategietests mit der ressourcenbasierten Analyse von Nachrichtenereignissen
In diesem Artikel bereiten wir unser MQL5-Handelssystem für Strategietests vor, indem wir Wirtschaftskalenderdaten als Ressource für nicht-live Analysen einbinden. Wir implementieren das Laden von Ereignissen und die Filterung nach Zeit, Währung und Auswirkung und validieren sie dann im Strategy Tester. Dies ermöglicht effektive Backtests von nachrichtengesteuerten Strategien.
Entwicklung des Price Action Analysis Toolkit (Teil 21): Das Tool Market Structure Flip Detector Entwicklung des Price Action Analysis Toolkit (Teil 21): Das Tool Market Structure Flip Detector
Der Market Structure Flip Detector Expert Advisor (EA) agiert als Ihr aufmerksamer Partner, der ständig die Veränderungen der Marktstimmung beobachtet. Durch die Verwendung von Average True Range (ATR)-basierten Schwellenwerten erkennt es effektiv Strukturumkehrungen und kennzeichnet jedes höhere Tief und niedrigere Hoch mit klaren Indikatoren. Dank der schnellen Ausführung und der flexiblen API von MQL5 bietet dieses Tool eine Echtzeitanalyse, die die Anzeige für eine optimale Lesbarkeit anpasst und ein Live-Dashboard zur Überwachung der Anzahl und des Timings von Flips bereitstellt. Darüber hinaus sorgen anpassbare Ton- und Push-Benachrichtigungen dafür, dass Sie über kritische Signale informiert bleiben, sodass Sie sehen können, wie einfache Eingaben und Hilfsroutinen Kursbewegungen in umsetzbare Strategien verwandeln können.
Entwicklung eines Toolkits zur Analyse von Preisaktionen (Teil 20): Externer Fluss (IV) - Correlation Pathfinder Entwicklung eines Toolkits zur Analyse von Preisaktionen (Teil 20): Externer Fluss (IV) - Correlation Pathfinder
Der Correlation Pathfinder bietet als Teil der Serie der Entwicklung eines Toolkits zur Analyse von Preisaktionen einen neuen Ansatz zum Verständnis der Dynamik von Währungspaaren. Dieses Tool automatisiert die Datenerfassung und -analyse und bietet einen Einblick in die Wechselwirkungen zwischen Paaren wie EUR/USD und GBP/USD. Verbessern Sie Ihre Handelsstrategie mit praktischen Echtzeit-Informationen, die Ihnen helfen, Risiken zu managen und Chancen effektiver zu erkennen.