
Manuelle Backtest leicht gemacht: Aufbau eines nutzerdefinierten Toolkits für Strategietester in MQL5
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:
- Der Plan: Entwurf eines Toolkits für die manuellen Backtests
- Implementierung in MQL5: Das Toolkit zum Leben erwecken
- Backtests in Aktion: Verwendung des Toolkits
- 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:
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.
Wenn wir das Programm ausführen, erhalten wir die folgende Ausgabe.
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.
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.
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.
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





- 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.
Wie sieht es mit unterschiedlichen Zeitrahmen aus?
Hallo. Derzeit nur ein Zeitrahmen. Vielleicht versuchen, dass in naher Zukunft.
Danke, es ist ein nützliches Werkzeug
Sicher. Willkommen und danke auch für das Feedback.
Ich wollte wissen, wie man einen mq4-Indikator in einen mq5-Indikator umwandelt.
Sehen Sie sich den neuen Artikel an: Manuelles Backtesting leicht gemacht: Aufbau eines benutzerdefinierten Toolkits für Strategy Tester in MQL5.
Autor: Allan Munene Mutiiria