Erstellen eines Expert Advisors, der mit verschiedenen Instrumenten handelt
Einleitung
Die technische Seite der Implementierung des Programmcodes, damit ein einzelner Expert Advisor, der an ein einzelnes Diagramm angehängt wird, gleichzeitig mit unterschiedlichen Vermögenswerten handeln kann, war im Allgemeinen auch in MQL4 kein großes Problem. Doch erst mit dem MetaTrader 5 Client Terminal erhielten Händler endlich die Gelegenheit, die Arbeit solcher Mechanismen mithilfe von Strategietestern vollständig zu analysieren.
Also werden Mehrwährungsmechanismen jetzt beliebter denn je und wir können steigendes Interesse an der Erstellung solcher Handelssysteme prognostizieren. Das Hauptproblem bei der Implementierung solcher Roboter ist allerdings, dass ihre Ausmaße im Programmcode sich ausdehnen, und zwar im besten Fall in einer arithmetischen Progression, was für den typischen Programmierer nicht leicht zu erfassen ist.
In diesem Beitrag schreiben wir einen einfachen Expert Advisor für mehrere Währungen, in dem die strukturellen Mängel vielleicht nicht vollständig behoben, aber dennoch reduziert werden.
1. Umsetzung eines einfachen Systems zur Trendverfolgung
Im Grunde genommen, könnten wir mit einem möglichst einfachen Handelssystem beginnen, das den Trend basierend auf dem im Terminal eingebauten Indikator Triple Exponential Moving Average verfolgt. Dabei handelt es sich um einen äußerst simplen Algorithmus, der keine besonderen Kommentare benötigt und den wir jetzt in den Programmcode einbauen werden.
Doch zuallererst möchte ich die allgemeinsten Schlussfolgerungen über den Expert Advisor erläutern. Es ist sinnvoll, mit dem Block eingehender Parameter des Expert Advisors zu beginnen, deklariert auf globaler Ebene.
Also müssen wir zuerst die Vermögenswerte auswählen, mit denen wir arbeiten werden. Dies kann mithilfe der Zeileneingabevariablen bewerkstelligt werden, in denen die Symbole der Vermögenswerte gespeichert werden können. Nun wäre es schön, über einen Handelsverbotsschalter für jeden Vermögenswert zu verfügen. Damit könnten Handelstätigkeiten nach Vermögenswert deaktiviert werden.
Natürlich sollte jeder Vermögenswert mit seinen jeweiligen Handelsparametern Stop Loss, Take Profit, dem Volumen der offenen Position und Abweichungen verbunden werden. Und aus offensichtlichen Gründen müssen die Eingabeparameter des Indikators Triple Exponential Moving Average für jeden zu handelnden Vermögenswert individuell festgelegt sein.
Hier sehen Sie einen fertigen Block von Eingabevariablen für nur einen Vermögenswert, ausgeführt in Übereinstimmung mit diesen Überlegungen. Die verbleibenden Blöcke unterscheiden sich nur hinsichtlich der Zahlen in den Namen der Eingabeparameter des Expert Advisors. Für dieses Beispiel habe ich mich auf nur zwölf Vermögenswerte eingeschränkt, obwohl es idealerweise keine softwareseitigen Begrenzungen für die Menge solcher Blöcke gibt.
Wir brauchen nur etwas, womit wir handeln können! Und das Allerwichtigste: Unser PC muss über genügend Ressourcen verfügen, um dieses Problem zu lösen.
input string Symb0 = "EURUSD"; input bool Trade0 = true; input int Per0 = 15; input ENUM_APPLIED_PRICE ApPrice0 = PRICE_CLOSE; input int StLoss0 = 1000; input int TkProfit0 = 2000; input double Lots0 = 0.1; input int Slippage0 = 30;
Da wir nun die Variablen auf globaler Ebene kennen, können wir mit der Konstruktion des Codes innerhalb der Funktion OnTick() fortfahren. Die vernünftigste Option wäre in diesem Fall eine Aufteilung des Algorithmus zum Erhalten von Handelssignalen und des tatsächlichen handelnden Teils des Expert Advisors in zwei benutzerdefinierte Funktionen.
Und da der Expert Advisor mit zwölf Vermögenswerten gleichzeitig arbeitet, muss es auch zwölf Aufrufe dieser Funktionen innerhalb des OnTick()-Blocks geben.
Natürlich sollte der erste Eingabeparameter dieser Funktionen eine eindeutige Zahl sein, unter der diese zu handelnden Vermögenswerte aufgelistet werden. Der zweite Eingabeparameter ist aus offensichtlichen Gründen der Zeilenname des zu handelnden Vermögenswerts.
Die Rolle des dritten Parameters erfüllt eine logische Variable, die den Handel auflöst. Als Nächstes folgen für den Algorithmus zum Bestimmen von Handelssignalen die Eingangssignale des Indikators und für die Handelsfunktion die Distanz zu den Pending Orders, das Volumen der Position und die Abweichung (zulässige Abweichung vom Eröffnungspreis der Position).
Für die Übertragung der Handelssignale von einer Funktion zur anderen sollten statische Arrays als Parameter der Funktion festgelegt werden, die ihre Werte per Verweis beziehen. Dies ist die finale Version des empfohlenen Codes für die OnTick()-Funktion.
void OnTick() { //--- declare variables arrays for trade signals static bool UpSignal[12], DnSignal[12], UpStop[12], DnStop[12]; //--- get trade signals TradeSignalCounter( 0, Symb0, Trade0, Per0, ApPrice0, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 1, Symb1, Trade1, Per1, ApPrice1, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 2, Symb2, Trade2, Per2, ApPrice2, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 3, Symb3, Trade3, Per3, ApPrice3, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 4, Symb4, Trade4, Per4, ApPrice4, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 5, Symb5, Trade5, Per5, ApPrice5, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 6, Symb6, Trade6, Per6, ApPrice6, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 7, Symb7, Trade7, Per7, ApPrice7, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 8, Symb8, Trade8, Per8, ApPrice8, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 9, Symb9, Trade9, Per9, ApPrice9, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(10, Symb10, Trade10, Per10, ApPrice10, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(11, Symb11, Trade11, Per11, ApPrice11, UpSignal, DnSignal, UpStop, DnStop); //--- perform trade operations TradePerformer( 0, Symb0, Trade0, StLoss0, TkProfit0, Lots0, Slippage0, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 1, Symb1, Trade1, StLoss1, TkProfit1, Lots1, Slippage1, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 2, Symb2, Trade2, StLoss2, TkProfit2, Lots2, Slippage2, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 3, Symb3, Trade3, StLoss3, TkProfit3, Lots3, Slippage3, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 4, Symb4, Trade4, StLoss4, TkProfit4, Lots4, Slippage4, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 5, Symb5, Trade5, StLoss5, TkProfit5, Lots5, Slippage5, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 6, Symb6, Trade6, StLoss6, TkProfit6, Lots6, Slippage6, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 7, Symb7, Trade7, StLoss7, TkProfit7, Lots7, Slippage7, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 8, Symb8, Trade8, StLoss8, TkProfit8, Lots8, Slippage8, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 9, Symb9, Trade9, StLoss9, TkProfit9, Lots9, Slippage9, UpSignal, DnSignal, UpStop, DnStop); TradePerformer(10, Symb10, Trade10, StLoss10, TkProfit10, Lots10, Slippage10, UpSignal, DnSignal, UpStop, DnStop); TradePerformer(11, Symb11, Trade11, StLoss11, TkProfit11, Lots11, Slippage11, UpSignal, DnSignal, UpStop, DnStop); //--- }
Innerhalb der Funktion TradeSignalCounter() muss nur das Handle des technischen Indikators Triple Exponential Moving Average einmal beim Start jedes Vermögenswerts abgerufen werden und anschließend bei jeder Änderung des Balkens, um die Handelssignale zu berechnen.
Dieses relativ simple Schema zur Umsetzung im Code bekommt immer mehr kleine Details.
bool TradeSignalCounter(int Number, string Symbol_, bool Trade, int period, ENUM_APPLIED_PRICE ApPrice, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- declare variable to store final size of variables arrays static int Size_=0; //--- declare array to store handles of indicators as static variable static int Handle[]; static int Recount[],MinBars[]; double TEMA[4],dtema1,dtema2; //--- initialization if(Number+1>Size_) // Entering the initialization block only on first start { Size_=Number+1; // For this number entering the block is prohibited //--- change size of variables arrays ArrayResize(Handle,Size_); ArrayResize(Recount,Size_); ArrayResize(MinBars,Size_); //--- determine minimum number of bars, sufficient for calculation MinBars[Number]=3*period; //--- setting array elements to 0 DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- use array as timeseries ArraySetAsSeries(TEMA,true); //--- get indicator's handle Handle[Number]=iTEMA(Symbol_,0,period,0,ApPrice); } //--- check if number of bars is sufficient for calculation if(Bars(Symbol_,0)<MinBars[Number])return(true); //--- get trade signals if(IsNewBar(Number,Symbol_,0) || Recount[Number]) // Entering the block on bar change or on failed copying of data { DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- using indicator's handles, copy values of indicator's //--- buffers into static array, specially prepared for this purpose if(CopyBuffer(Handle[Number],0,0,4,TEMA)<0) { Recount[Number]=true; // As data were not received, we should return // into this block (where trade signals are received) on next tick! return(false); // Exiting the TradeSignalCounter() function without receiving trade signals } //--- all copy operations from indicator buffer are successfully completed Recount[Number]=false; // We may not return to this block until next change of bar int Digits_ = int(SymbolInfoInteger(Symbol_,SYMBOL_DIGITS)+4); dtema2 = NormalizeDouble(TEMA[2] - TEMA[3], Digits_); dtema1 = NormalizeDouble(TEMA[1] - TEMA[2], Digits_); //---- determining the input signals if(dtema2 > 0 && dtema1 < 0) DnSignal[Number] = true; if(dtema2 < 0 && dtema1 > 0) UpSignal[Number] = true; //---- determining the output signals if(dtema1 > 0) DnStop[Number] = true; if(dtema1 < 0) UpStop[Number] = true; } //----+ return(true); }
Diesbezüglich erweist sich der Code der Funktion TradePerformer() als ziemlich einfach:
bool TradePerformer(int Number, string Symbol_, bool Trade, int StLoss, int TkProfit, double Lots, int Slippage, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- close opened positions if(UpStop[Number])BuyPositionClose(Symbol_,Slippage); if(DnStop[Number])SellPositionClose(Symbol_,Slippage); //--- open new positions if(UpSignal[Number]) if(BuyPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit)) UpSignal[Number]=false; //This trade signal will be no more on this bar! //--- if(DnSignal[Number]) if(SellPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit)) DnSignal[Number]=false; //This trade signal will be no more on this bar! //--- return(true); }Doch das liegt nur daran, dass die tatsächlichen Befehle für die Durchführung von Handelstätigkeiten in vier zusätzlichen Funktionen verpackt sind:
BuyPositionClose(); SellPositionClose(); BuyPositionOpen(); SellPositionOpen();
Alle vier Funktionen arbeiten absolut analog, also können wir uns auf nur eine davon beschränken:
bool BuyPositionClose(const string symbol,ulong deviation) { //--- declare structures of trade request and result of trade request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); //--- check if there is BUY position if(PositionSelect(symbol)) { if(PositionGetInteger(POSITION_TYPE)!=POSITION_TYPE_BUY) return(false); } else return(false); //--- initializing structure of the MqlTradeRequest to close BUY position request.type = ORDER_TYPE_SELL; request.price = SymbolInfoDouble(symbol, SYMBOL_BID); request.action = TRADE_ACTION_DEAL; request.symbol = symbol; request.volume = PositionGetDouble(POSITION_VOLUME); request.sl = 0.0; request.tp = 0.0; request.deviation=(deviation==ULONG_MAX) ? deviation : deviation; request.type_filling=ORDER_FILLING_FOK; //--- string word=""; StringConcatenate(word, "<<< ============ BuyPositionClose(): Close Buy position at ", symbol," ============ >>>"); Print(word); //--- send order to close position to trade server if(!OrderSend(request,result)) { Print(ResultRetcodeDescription(result.retcode)); return(false); } //----+ return(true); }
Alles in allem ist das der komplette Expert Advisor für mehrere Währungen (Exp_TEMA.mq5)!
Abgesehen von den bereits betrachteten Funktionen beinhaltet er zwei weitere benutzerdefinierte Funktionen:
bool IsNewBar(int Number, string symbol, ENUM_TIMEFRAMES timeframe); string ResultRetcodeDescription(int retcode);
Die erste dieser Funktionen gibt zum Zeitpunkt der Balkenänderung den Wert true basierend auf dem ausgewählten Symbol und Timeframe aus, die zweite gibt die Zeile nach dem Code des Ergebnisses der Handelstransaktion aus, abgeleitet aus dem Feld retcode der Handelsanfragestruktur MqlTradeResult.
Der Expert Advisor ist fertig und es ist an der Zeit, ihn zu testen! Zwischen dem Testing des Expert Advisors für mehrere Währungen und des Expert Advisors für eine Währung bestehen keine ernst zu nehmenden Unterschiede.
Bestimmen Sie die Konfigurationen unter der Registerkarte "Settings" des Strategietesters:
Abbildung 1. Registerkarte "Settings" des Strategietesters
Passen Sie die Werte der Eingabeparameter unter der Registerkarte "Inputs" an, falls erforderlich:
Abbildung 2. Registerkarte "Inputs" des Strategietesters
und klicken Sie dann in der Registerkarte "Settings" des Strategietesters auf die Schaltfläche "Start":
Abbildung 3. Durchführen des Expert-Advisor-Tests
Da die Historie für alle zwölf Symbole geladen wird, kann der erste Test des Expert Advisors eine beträchtliche Durchlaufzeit haben. Öffnen Sie nach dem Abschluss des Tests im Strategietester die Registerkarte "Results":
Abbildung 4. Testergebnisse
und führen Sie mithilfe der Optionen unter der Registerkarte "Graph" eine Analyse der Daten durch:
Abbildung 5. Diagramm der Saldendynamik und des Eigenkapitals
und das "Journal" (Logbuch):
Abbildung 6. Logbuch des Strategietesters
Der eigentliche Sinn hinter dem Algorithmus der Marktein- und -austritte dieses Expert Advisors ist natürlich zu einfach und es wäre naiv, bedeutende Ergebnisse zu erwarten, wenn man die ersten, beliebig gewählten Parameter nutzt. Doch im Moment ist es unser Ziel, die grundlegende Idee hinter der möglichst einfachen Erstellung eines Expert Advisors für mehrere Währungen vorzuführen.
Mit der Optimierung dieses Expert Advisors können aufgrund von zu vielen Eingabeparametern Unannehmlichkeiten entstehen. Der erbliche Algorithmus der Optimierung benötigt eine viel kleinere Menge dieser Parameter, also sollte der Expert Advisor für jeden Vermögenswert individuell optimiert werden, wobei die verbleibenden Vermögenswerte durch die Eingabeparameter TradeN deaktiviert werden.
Nun, da der eigentliche Sinn dieser Herangehensweise erläutert wurde, können Sie anfangen, mit dem interessanteren Algorithmus der Entscheidungsfindung für den Roboter für den Handel mit mehreren Währungen zu arbeiten.
2. Resonanzen auf den Finanzmärkten und ihre Anwendung in Handelssystemen
Die Idee, die Zusammenhänge zwischen den verschiedenen Vermögenswerten zu berücksichtigen, ist im Allgemeinen nicht neu. Es wäre interessant, einen Algorithmus einzusetzen, der genau auf der Analyse solcher Trends basiert. In diesem Beitrag implementiere ich einen Mehrwährungsmechanismus auf Basis des Artikels "Resonanzen – eine neue Klasse von technischen Indikatoren" von Vasilij Jakimkin, veröffentlicht in der Fachzeitschrift "Valutny Spekulant" (Russisch) 04, 05, 2001.
Die Essenz dieser Herangehensweise sieht kurz gefasst wie folgt aus. Zum Beispiel nutzen wir für die Untersuchung des Marktes für EUR/USD nicht nur die Ergebnisse von einigen Indikatoren für diesen Vermögenswert, sondern auch die Ergebnisse des gleichen Indikators zu Vermögenswerten, die mit EUR/USD zusammenhängen, nämlich EUR/JPY und USD/JPY. Am besten sollte ein Indikator genutzt werden, dessen Werte im gleichen Änderungsbereich normalisiert sind, um die Messungen und Berechnungen zu vereinfachen.
Unter Berücksichtigung dieser Anforderungen ist der klassische stochastische Indikator gut für unsere Zwecke geeignet, auch wenn es in der Realität keinen Unterschied im Gebrauch anderer Indikatoren gibt. Als Trendrichtung betrachten wir das Vorzeichen der Differenz zwischen dem Wert des stochastischen Stoh und dessen Signallinie Sign.
Abbildung 7. Bestimmung der Trendrichtung
Für das Symbol der Variable dStoh existiert eine ganze Tabelle von möglichen Kombinationen und deren Interpretationsmöglichkeiten für die Richtung des aktuellen Trends:
Abbildung 8. Kombinationen des Symbols der Variable dStoh und Richtung des Trends
In Fällen, in denen zwei Signale der Vermögenswerte EUR/JPY und USD/JPY entgegengesetzte Werte haben, sollten wir deren Summe berechnen. Ist die Summe größer als Null, können beide Signale als positiv betrachtet werden, andernfalls als negativ.
Nutzen Sie also für das Eröffnen von Longs Situationen, in denen der Trend steigt und für den Austritt einen Abwärtstrend oder einen Trend, bei dem die Signale des Indikators der Hauptvermögenswerts EUR/USD negativ sind. Verlassen Sie die Long außerdem, wenn der Hauptvermögenswert keine Signale hat und wenn die Summe der Variable dStoh für die verbleibenden Vermögenswerte weniger als Null beträgt. Für Shorts ist alles analog, nur die Situation ist genau umgekehrt.
Es wäre am sinnvollsten, den gesamten analytischen Teil des Expert Advisors im Mehrwährungsindikator zu platzieren und für den Expert Advisor aus den Indikatorpuffern nur die fertigen Signale für die Steuerung des Handels zu nutzen. Eine Variante dieses Indikatortypen wird im Indikator MultiStochastic.mq5 bereitgestellt und bietet visuelle Analysen der Marktbedingungen.
Abbildung 9. Indikator MultiStochastic
Die grünen Balken signalisieren das Eröffnen und Halten von Longs und die roten das von Shorts. Rosafarbene und hellblaue Punkte am oberen Rand des Diagramms stehen für Signale für das Verlassen der Long und Short Positions.
Dieser Indikator kann direkt für das Abrufen von Signalen im Expert Advisor genutzt werden, doch es wäre trotzdem besser, seine Arbeit zu erleichtern und sämtliche unnötigen Puffer und Visualisierungselemente zu entfernen, sodass nur übrig bleibt, was direkt an der Versorgung mit Handelssignalen beteiligt ist. Genau das wurde im Indikator MultiStochastic_Exp.mq5 getan.
In diesem Expert Advisor handelte ich mit nur drei Vermögenswerten, sodass der Code der OnTick()-Funktion nahezu primitiv wurde:
void OnTick() { //--- declare variables arrays for trade signals static bool UpSignal[], DnSignal[], UpStop[], DnStop[]; //--- get trade signals TradeSignalCounter(0, Trade0, Kperiod0, Dperiod0, slowing0, ma_method0, price_0, SymbolA0, SymbolB0, SymbolC0, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(1, Trade1, Kperiod1, Dperiod1, slowing1, ma_method1, price_1, SymbolA1, SymbolB1, SymbolC1, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(2, Trade2, Kperiod2, Dperiod2, slowing2, ma_method2, price_2, SymbolA2, SymbolB2, SymbolC2, UpSignal, DnSignal, UpStop, DnStop); //--- perform trade operations TradePerformer( 0, SymbolA0, Trade0, StopLoss0, 0, Lots0, Slippage0, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 1, SymbolA1, Trade1, StopLoss1, 0, Lots1, Slippage1, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 2, SymbolA2, Trade2, StopLoss2, 0, Lots2, Slippage2, UpSignal, DnSignal, UpStop, DnStop); //--- }
Der Code der Funktion TradeSignalCounter() ist allerdings etwas komplexer: Das liegt daran, dass ein Mehrwährungsindikator direkt mit drei Zeitreihen von verschiedenen Vermögenswerten arbeitet. Deshalb nutzen wir eine subtilere Überprüfung der Balken auf die Hinlänglichkeit ihrer Mindestmenge in einer der drei Zeitreihen mithilfe der Funktion Rates_Total().
Zudem wird eine zusätzliche Überprüfung der Synchronisation der Zeitreihen mithilfe der Funktion SynchroCheck() durchgeführt, um die Genauigkeit der Bestimmung des Zeitpunkts, zu dem in allen Zeitreihen gleichzeitig eine Balkenänderung stattfindet, zu gewährleisten.
bool TradeSignalCounter(int Number, bool Trade, int Kperiod, int Dperiod, int slowing, ENUM_MA_METHOD ma_method, ENUM_STO_PRICE price_, string SymbolA, string SymbolB, string SymbolC, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- declare variable to store sizes of variables arrays static int Size_=0; //--- declare arrays to store handles of indicators as static variables static int Handle[]; static int Recount[],MinBars[]; //--- double dUpSignal_[1],dDnSignal_[1],dUpStop_[1],dDnStop_[1]; //--- change size of variables arrays if(Number+1>Size_) { uint size=Number+1; //---- if(ArrayResize(Handle,size)==-1 || ArrayResize(Recount,size)==-1 || ArrayResize(UpSignal, size) == -1 || ArrayResize(DnSignal, size) == -1 || ArrayResize(UpStop, size) == -1 || ArrayResize(DnStop, size) == -1 || ArrayResize(MinBars,size) == -1) { string word=""; StringConcatenate(word,"TradeSignalCounter( ",Number, " ): Error!!! Unable to change sizes of variables arrays!!!"); int error=GetLastError(); ResetLastError(); //--- if(error>4000) { StringConcatenate(word,"TradeSignalCounter( ",Number," ): Error code ",error); Print(word); } Size_=-2; return(false); } Size_=int(size); Recount[Number] = false; MinBars[Number] = Kperiod + Dperiod + slowing; //--- get indicator's handle Handle[Number]=iCustom(SymbolA,0,"MultiStochastic_Exp", Kperiod,Dperiod,slowing,ma_method,price_, SymbolA,SymbolB,SymbolC); } //--- check if number of bars is sufficient for calculation if(Rates_Total(SymbolA,SymbolB,SymbolC)<MinBars[Number])return(true); //--- check timeseries synchronization if(!SynchroCheck(SymbolA,SymbolB,SymbolC))return(true); //--- get trade signals if(IsNewBar(Number,SymbolA,0) || Recount[Number]) { DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- using indicators' handles, copy values of indicator's //--- buffers into static arrays, specially prepared for this purpose if(CopyBuffer(Handle[Number], 1, 1, 1, dDnSignal_) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 2, 1, 1, dUpSignal_) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 3, 1, 1, dDnStop_ ) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 4, 1, 1, dUpStop_ ) < 0){Recount[Number] = true; return(false);} //--- convert obtained values into values of logic variables of trade commands if(dDnSignal_[0] == 300)DnSignal[Number] = true; if(dUpSignal_[0] == 300)UpSignal[Number] = true; if(dDnStop_ [0] == 300)DnStop [Number] = true; if(dUpStop_ [0] == 300)UpStop [Number] = true; //--- all copy operations from indicator's buffers completed successfully //--- unnecessary to return into this block until next bar change Recount[Number]=false; } //----+ return(true); }
Es gibt keine weiteren bedeutenden ideologischen Unterschiede im Code dieses Expert Advisors (Exp_ResonanceHunter.mq5), da er auf Basis derselben funktionalen Komponenten kompiliert wird. Deshalb glaube ich nicht, dass es nötig ist, noch mehr Zeit mit seiner internen Struktur zu verbringen.
Fazit
Meiner Meinung nach entspricht der Code des Expert Advisors für mehrere Währungen in MQL5 vollständig dem Code eines herkömmlichen Expert Advisors.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/105
- 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.