![Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 5): Die Bollinger Bänder mit dem Keltner-Kanal — Indikatoren Signal](https://c.mql5.com/2/61/rj-article-images_600x314.jpg)
Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 5): Die Bollinger Bänder mit dem Keltner-Kanal — Indikatoren Signal
Einführung
Die Definition eines Multiwährungs-Expert Advisors in diesem Artikel ist ein Expert Advisor oder Handelsroboter, der handeln kann (z.B. Aufträge eröffnen, schließen und verwalten, Trailing Stop Loss und Trailing Profit) für mehr als 1 Symbolpaar aus nur einem Symbolchart, wobei in diesem Artikel Expert Advisor für 30 Paare handeln wird.
In diesem Artikel werden wir Signale von zwei Indikatoren verwenden, in diesem Fall Bollinger Bänder® und dem Keltner Kanal.
Auf der alten Plattform (MetaTrader 4) war die Verwendung von Signalen dieser Art als iBandsOnArray-Funktion bekannt.
In der Erklärung der Funktion iBandsOnArray in der MQL4-Referenz heißt es, dass: Anmerkung: Im Gegensatz zu iBands(...) nimmt die Funktion iBandsOnArray() keine Daten nach Symbolname, Zeitrahmen oder angewandtem Preis entgegen. Die Preisdaten müssen vorher aufbereitet werden...
In der Diskussion im MQL5-Forum habe ich gelesen, dass es sogar Händler oder Nutzer gibt, die sagen, dass iBandOnArray() in MQL5 nicht existiert.
In der Tat ist iBandOnArray() nicht in der MQL5-Funktionsliste enthalten, aber durch die Verwendung des Indikator.Handles von iBands() können wir iBandOnArray() in MQL5 tatsächlich leicht erstellen. Meiner Meinung nach ist die Verwendung des Indikator-Handles in MQL5 sogar einfacher und bequemer als die Verwendung der Funktion iBandsOnArray() in MetaTrader 4.
Wie in den vorangegangenen Artikeln bewiesen, wissen wir alle, dass der Handel mit mehreren Währungen, sowohl auf dem Handelsterminal als auch auf dem Strategietester, mit der Leistung, den Fähigkeiten und den Möglichkeiten von MQL5 möglich ist.
Daher ist das Ziel, die wesentlichen Bedürfnisse der Händler, die effiziente und effektive Handelsroboter wollen zu befriedigen, sodass durch den Rückgriff auf die Stärken, Fähigkeiten und Einrichtungen von der sehr zuverlässigen MQL5 zur Verfügung gestellt, können wir eine einfache Multi-Currency Expert Advisor, die in diesem Artikel verwendet 2 Indikator-Signale für die Auftragserteilung erstellen: die Bollinger Bänder® mit dem Keltner-Kanal, wobei die Bollinger Bänder die Kursdaten des Keltner Kanal verwenden. In der Zwischenzeit werden wir für Trailing Stops weiterhin den Parabolic SAR (iSAR) Indikator verwenden.
Pläne und Eigenschaften
1. Handel mit Währungspaaren.
Dieser Multi-Currency Expert Advisor ist für den Handel mit einem Symbol oder Paar wie folgt konzipiert:
EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCAD,USDCHF,USDJPY,EURGBP,EURAUD, EURNZD, EURCAD, EURCHF, EURJPY, GBPAUD, GBPNZD, GBPCAD,GBPCHF,GBPJPY,AUDNZD,AUDCAD,AUDCHF,AUDJPY,NZDCAD,NZDCHF,NZDJPY, CADCHF, CADJPY, CHFJPY = 28 Paare.
Plus 2 Metallpaare: XAUUSD (Gold) und XAGUSD (Silber),
insgesamt sind es 30 Paare.
Wie im vorigen Artikel machen wir es uns auch in diesem Artikel einfach, indem wir spezielle Eingabeeigenschaften für das Präfix und das Suffix des Paarnamens hinzufügen. Dann werden wir mit einer einfachen Funktion das Präfix und/oder Suffix des Paarnamens in Kombination mit den 30 registrierten Paarnamen behandeln, sodass bei der Verwendung eines EA auf MetaTrader 5 von einem Broker mit solchen speziellen Symbolnamen alles reibungslos funktioniert.
Die Schwäche der Funktion zur Erkennung von Symbolnamen mit Präfixen und Suffixen ist, dass diese Funktion nur für Forex- und Metallsymbolpaare oder -namen im MT5 funktioniert, nicht aber für spezielle Symbole und Indizes. Eine weitere Schwäche dieser Methode besteht darin, dass sich der Händler beim Namen des Präfixes und/oder Suffixes des Paares vertippen kann (Groß- und Kleinschreibung muss beachtet werden). Daher erwarten wir von Händlern, die den Expert Advisor in diesem Artikel verwenden, Präzision und Genauigkeit bei der Eingabe des Präfixes und/oder Suffixes des Paarnamens.
Wie im vorherigen Artikel haben wir auch in diesem Expert Advisor 10 Optionen für die Paare hinzugefügt, die zu diesem Zeitpunkt gehandelt werden. Eines der 10 Optionspaare, die gehandelt werden, ist „Des Händlers Wunschpaare“, wobei die zu handelnden Paare manuell vom Händler in der Eigenschaft Expert Input eingegeben werden müssen. Sie müssen jedoch immer darauf achten, dass der Name des Paares, das Sie eingeben, bereits in der Liste der 30 Paare enthalten ist.
In dieser Version des Expert Advisors haben wir auch eine Option für die Handelssitzung (Zeitzone) hinzugefügt, sodass die Paare, die gehandelt werden, der Zeit der Handelssitzung entsprechen können.
2. Indikatoren.
In dieser Version des Expert Advisors werden wir die Signale von zwei Indikatoren verwenden, in diesem Fall die Bollinger Bänder® mit dem Keltner Kanal. Als Preisdaten für die Bollinger Bänder® verwenden wir den Keltner Kanal.
2.1. Keltner-Kanal-Indikator.
Der Keltner-Kanal wurde erstmals in den 1960er Jahren von Chester Keltner eingeführt. Die ursprüngliche Formel verwendete einfache gleitende Durchschnitte (SMAs) und die Höchst-/Tiefstkurs-Spanne zur Berechnung der Bandbreiten. In den 1980er Jahren wurde eine neue Formel eingeführt, die die Average True Range (ATR) verwendet. Die ATR-Methode ist heute weit verbreitet.
Der Keltner-Kanal-Indikator, der in diesem Artikel für Expert Advisor s verwendet wird, wurde von mir speziell mit einer Methode erstellt, die heute sehr beliebt ist, nämlich dem Exponential Moving Average (EMA) Periode 20 mit oberen und unteren Bändern unter Verwendung des ATR-Indikators Periode 20.
Die Eingangseigenschaften des Keltner-Kanalanzeigers sind wie folgt:
2.2. Der Indikator Bollinger Bänder®.
Die Bollinger Bänder® wurden von John Bollinger in den 80er Jahren entwickelt und wurden schnell zu einem der am häufigsten verwendeten Indikatoren im Bereich der technischen Analyse. Die Bollinger Bänder® bestehen aus drei Bändern - dem oberen, dem mittleren und dem unteren Band - die dazu dienen, kurzfristige Preisextreme auf dem Markt hervorzuheben. Das obere Band ist ein Zeichen für überkaufte Bedingungen, während das untere Band ein Zeichen für überverkaufte Bedingungen ist. Die meisten Finanzanalysten verwenden Bollinger Bänder® und kombinieren sie mit anderen Indikatoren, um ein besseres analytisches Bild der Marktlage zu erhalten.
Im Expert Advisor dieses Artikels verwenden wir die Bollinger Bänder® mit einer Periodenlänge 38, der die Preisdaten des Keltner Kanals nutzt.
Die Eingabeeigenschaften des Bollinger Bänder® sind wie folgt:
Eine Veranschaulichung des Keltner-Kanal als Preisdaten für die Bollinger Bänder® für KAUF- oder VERKAUFSSIGNALE ist in den Abbildungen 1 und 2 zu sehen.
Abbildung 1. Kaufsignal
Abbildung 2. Verkaufssignal
In der obigen Abbildung wird nur dann ein Signal gegeben, wenn die mittlere Linie des Keltner-Kanals die obere Linie der Bollinger Bänder® oder die untere Linie der Bollinger Bänder® über- oder unterschreitet. Aber für den Expert Advisor in diesem Artikel ist das Indikatorsignal tatsächlich eine Kreuzung zwischen der mittleren Linie des Keltner-Kanals und der oberen, mittleren und unteren Linie der Bollinger Bänder®.
- Bei Kaufsignalen:
- Erstes Signal: Wenn die mittlere Linie des Keltner Kanal-Indikators die untere Linie des Bollinger Bänder® nach oben kreuzt oder
- Zweites Signal: Wenn die Mittellinie des Keltner Kanal-Indikators die Mittellinie der Bollinger Bänder® nach oben kreuzt oder
- Drittes Signal: Wenn die mittlere Linie des Keltner-Kanals die obere Linie der Bollinger Bänder® nach oben kreuzt.
- Bei Verkaufssignalen:
- Erstes Signal: Wenn die mittlere Linie des Keltner Kanal Indikators die obere Linie des Bollinger Bänder® Indikators nach unten kreuzt oder
- Zweites Signal: Wenn die Mittellinie des Keltner Kanal-Indikators die Mittellinie der Bollinger Bänder® nach unten kreuzt oder
- Drittes Signal: Wenn die mittlere Linie des Keltner-Kanals die untere Linie der Bollinger Bänder® nach unten kreuzt.
3. Handels- & Auftragsmanagement
Dieser Multi-Currency Expert Advisor bietet Ihnen mehrere Optionen für die Verwaltung Ihrer Handels:
3.1. Stop-Loss-Aufträge
Optionen: Use Order Stop Loss (Yes) or (No) (Stop-Loss-Auftrag verwenden, Ja oder Nein)
- Wenn die Option (No) für Use Stop-Loss für Aufträge verwenden ausgewählt ist, werden alle Aufträge ohne Stop-Loss eröffnet.
- Wenn die Option Stop-Loss für Aufträge verwenden (Yes): Auch hier haben Sie die Wahl: Use Automatic Calculation Stop Loss (Yes) or (No) (Autom. Berechnung des Stop Loss verwenden, Ja oder Nein)
- Wenn die Option Automatische Berechnung Stop Loss (Yes), dann wird die Stop-Loss-Berechnung automatisch durch den Experten durchgeführt.
- Wenn die Option Automatische Berechnung des Stop Loss (No) gewählt wurde, muss der Händler den Stop-Loss-Wert in Pips eingeben.
- Wenn die Option Use Order Stop Loss (No): Dann prüft der Experte für jede eröffnete Order, ob die Signalbedingung noch gut ist und die Order mit Gewinn aufrechterhalten werden kann oder ob sich das Signal abgeschwächt hat und die Order geschlossen werden muss, um den Gewinn zu sichern oder ob sich die Signalbedingung umgekehrt hat und die Order mit Verlust geschlossen werden muss.
Anmerkung: Speziell um Positionen zu schließen (Close Trade) und den Gewinn zu sichern (Save Profit), wird aufgrund eines schwachen Signals eine Option angeboten, ob diese aktiviert werden soll oder nicht.
Wenn sie nicht aktiviert ist (No), wird der Auftrag trotz des schwächeren Signals aufrechterhalten oder nicht geschlossen, um den Gewinn zu sichern.
Wenn aktiviert (Yes), sind die Bedingungen für die Indikatoren Keltner Kanal und Bollinger Bänder®:
- Für das Schließen von Kaufpositionen: Wenn die untere Linie des Keltner-Kanals die obere Linie der Bollinger Bänder® nach unten kreuzt, wird die Kaufposition geschlossen.
- Für das Schließen von Verkaufspositionen: Wenn die obere Linie des Keltner Kanals die untere Linie der Bollinger Bänder® nach oben kreuzt, wird die Verkaufsposition geschlossen.
Der Code zum Setzen eines Stop-Loss-Auftrags lautet wie folgt:
double MCEA::OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice) { //--- slv=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); //-- switch(type) { case (ORDER_TYPE_BUY): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice-38*pip); else if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice-SLval*pip); else slv=0.0; //-- break; } case (ORDER_TYPE_SELL): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice+38*pip); else if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice+SLval*pip); else slv=0.0; } } //--- return(slv); //--- } //-end OrderSLSet() //---------//
Der Code für „Close Trade“ and „Save Profit“ auf der Signalbasis lautet wie folgt:
int MCEA::GetCloseInWeakSignal(const string symbol,int exis) // Signal Indicator Position Close in profit { //--- int ret=0; int rise=1, down=-1; //-- int br=3; Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); //-- double KCub[], KClb[]; double BBub[], BBlb[]; //-- ArrayResize(KCub,br,br); ArrayResize(KClb,br,br); ArrayResize(BBub,br,br); ArrayResize(BBlb,br,br); ArraySetAsSeries(KCub,true); ArraySetAsSeries(KClb,true); ArraySetAsSeries(BBub,true); ArraySetAsSeries(BBlb,true); //-- int xx=PairsIdxArray(symbol); //-- CopyBuffer(hKC[xx],1,0,br,KCub); CopyBuffer(hKC[xx],2,0,br,KClb); CopyBuffer(hBB[xx],1,0,br,BBub); CopyBuffer(hBB[xx],2,0,br,BBlb); //-- int dirmove=DirectionMove(symbol,TFt); bool closebuy=(KClb[1]>=BBub[1] && KClb[0]<BBub[0]-difud); bool closesel=(KCub[1]<=BBlb[1] && KCub[0]>BBlb[0]+difud); //-- if(exis==down && closesel && dirmove==rise) ret=rise; if(exis==rise && closebuy && dirmove==down) ret=down; //-- return(ret); //--- } //-end GetCloseInWeakSignal() //---------//
3.2. Take-Profit
Optionen: Take-Profit verwenden, Ja (Yes) oder Nein No)
- Wenn die Option Use Order Take Profit (No) ausgewählt ist, werden alle Aufträge ohne Take Profit eröffnet.
- Bei der Option Use Order Take Profit (Yes): Auch hier haben Sie die Wahl: Use Automatic Calculation Order Take Profit (Yes) oder (No) (Take-Profit automatisch berechnen, Ja oder Nein).
- Bei der Option Automatic Calculation Order Take Profit (Yes), dann wird die Berechnung der Take Profit Order automatisch vom Experten durchgeführt.
- Wenn die Option Automatic Calculation Order Take Profit (No), gewählt wurde, muss der Händler den Order-Take-Profit-Wert in Pips eingeben.
Der Code für das Setzen von Take-Profit lautet wie folgt:
double MCEA::OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice) { //--- tpv=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); //-- switch(type) { case (ORDER_TYPE_BUY): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice+50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice+TPval*pip); else tpv=0.0; //-- break; } case (ORDER_TYPE_SELL): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice-50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice-TPval*pip); else tpv=0.0; } } //--- return(tpv); //--- } //-end OrderTPSet() //---------//
3.3. Trailing Stop und Trailing Take-Profit
Optionen: Use Trailing SL/TP (Yes) or (No) (Nachlaufende SL/TP verwenden, Ja oder Nein)
- Wenn die Option Use Trailing SL/TP (No) gewählt wurde, wird der Experte keinen Trailing-Stop-Loss und keinen Trailing-Take-Profit durchführen.
- Bei der Option Use Trailing SL/TP (Yes): Auch hier haben Sie die Wahl: Use Automatic Trailing (Yes) oder (No) (Automatisches Nachziehen verwenden, Ja oder Nein).
- Wenn die Option Automatisches Trailing verwenden (Yes) aktiviert ist, wird der Trailing-Stop vom Experten unter Verwendung des Parabolic SAR (iSAR)-Wertes auf demselben Zeitrahmen wie die Signalberechnungen ausgeführt, wobei gleichzeitig ein Trailing-Profit auf der Grundlage des variablen Wertes TPmin (minimaler Trailing-Profit-Wert) erzielt wird.
- Wenn die Option Automatisches Trailing verwenden (No) aktiviert ist, wird der Trailing-Stop vom Experten anhand des Wertes in der Eingabeeigenschaft ausgeführt.
Anmerkung: Der Experte führt einen Trailing-Take-Profit gleichzeitig mit einem Trailing-Stop aus.
Funktion „Trailing Stop Price“:
double MCEA::TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type) { //--- int br=2; double pval=0.0; int x=PairsIdxArray(xsymb); Pips(xsymb); //-- switch(TS_type) { case 0: { RefreshTick(xsymb); if(ptype==POSITION_TYPE_BUY) pval=mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip); if(ptype==POSITION_TYPE_SELL) pval=mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip); break; } case 1: { double PSAR[]; ArrayResize(PSAR,br,br); ArraySetAsSeries(PSAR,true); CopyBuffer(hParIU[x],0,0,br,PSAR); RefreshPrice(xsymb,TFt,br); //-- if(ptype==POSITION_TYPE_BUY && (PSAR[0]<iLow(xsymb,TFt,0))) pval=PSAR[0]; if(ptype==POSITION_TYPE_SELL && (PSAR[0]>iHigh(xsymb,TFt,0))) pval=PSAR[0]; break; } } //-- return(pval); //--- } //-end TSPrice() //---------//
SL/TP-Funktion modifizieren:
bool MCEA::ModifySLTP(const string symbx,int TS_type) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int TRSP=TS_type; bool modist=false; int x=PairsIdxArray(symbx); Pips(symbx); //-- int total=PositionsTotal(); //-- for(int i=total-1; i>=0; i--) { string symbol=PositionGetSymbol(i); if(symbol==symbx && mc_position.Magic()==magicEA) { ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double vtrsb = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double netp=pos_profit+pos_swap+pos_comm; double modstart=mc_symbol.NormalizePrice(pos_open+TSmin*pip); double modminsl=mc_symbol.NormalizePrice(vtrsb+TSmin*pip); double modbuysl=vtrsb; double modbuytp=mc_symbol.NormalizePrice(price+TPmin*pip); bool modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0||modbuysl>pos_stop)); //-- if(modbuy && netp>0.05) { modist=mc_trade.PositionModify(symbol,modbuysl,modbuytp); } } if(opstype==POSITION_TYPE_SELL) { RefreshTick(symbol); double price = mc_position.PriceCurrent(); double vtrss = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double netp=pos_profit+pos_swap+pos_comm; double modstart=mc_symbol.NormalizePrice(pos_open-TSmin*pip); double modminsl=mc_symbol.NormalizePrice(vtrss-TSmin*pip); double modselsl=vtrss; double modseltp=mc_symbol.NormalizePrice(price-TPmin*pip); bool modsel = (price<modminsl && modselsl<modstart && (pos_stop==0.0||modselsl<pos_stop)); //-- if(modsel && netp>0.05) { modist=mc_trade.PositionModify(symbol,modselsl,modseltp); } } } } //-- return(modist); //--- } //-end ModifySLTP() //---------//
4. Manuelle Auftragsverwaltung.
Um die Effizienz in diesem Mehr-Währungs-EA zu unterstützen, werden mehrere manuelle Klick-Buttons hinzugefügt.
4.1. Set SL / TP All Orders (Setzen von SL / TP bei allen Aufträgen)
Wenn der Händler-Eingabeparameter Use Order Stop Loss (No) und/oder Use Order Take Profit (No) einstellt,
aber der Händler beabsichtigt, Stop Loss oder Take Profit für alle Aufträge zu verwenden, dann genügt ein einziger Klick auf die Schaltfläche
„Set SL / TP All Orders“ (SL/TP für alle Aufträge setzen) werden alle Aufträge geändert und ein Stop-Loss und/oder Take-Profits angewendet.
4.2. „Close All Orders“ (Alle Aufträge schließen). Wenn ein Händler alle Aufträge schließen möchte, dann werden mit einem einzigen Klick auf die Schaltfläche „Close All Orders“ alle offenen Aufträge geschlossen.
4.3. „Close All Orders Profit“ (Schließen aller Positionen im Gewinn). Wenn ein Händler alle Positionen, die bereits profitabel sind, mit einem einzigen Klick schließen möchte, genügt ein Klick auf die Schaltfläche „Close All Orders Profit“
5. Management-Befehle und Chartsymbole.
Für Multi-Currency Expert Advisors, die 30 Paare von nur einem Symbolchart aus handeln, ist es sehr effektiv und effizient, eine Schalttafel für alle Symbole zur Verfügung zu stellen, sodass Händler Charts oder Symbole mit nur einem Klick wechseln können, um die Genauigkeit des Indikatorsignals zu sehen, wenn der Experte eine Order öffnet oder schließt.
Umsetzung der Planung im Programm MQL5
1. Programmkopf und Eingabeeigenschaften
Header-Datei MQL5 einbinden:
//+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\AccountInfo.mqh> //-- CTrade mc_trade; CSymbolInfo mc_symbol; CPositionInfo mc_position; CAccountInfo mc_account; //---
Enumeration zur Verwendung der Zeitzone:
//-- enum tm_zone { Cus_Session, // Trading on Custom Session New_Zealand, // Trading on New Zealand Session Australia, // Trading on Australia Sydney Session Asia_Tokyo, // Trading on Asia Tokyo Session Europe_London, // Trading on Europe London Session US_New_York // Trading on US New York Session }; //--
Enumeration zur Auswahl der Zeitstunde:
//-- enum swhour { hr_00=0, // 00:00 hr_01=1, // 01:00 hr_02=2, // 02:00 hr_03=3, // 03:00 hr_04=4, // 04:00 hr_05=5, // 05:00 hr_06=6, // 06:00 hr_07=7, // 07:00 hr_08=8, // 08:00 hr_09=9, // 09:00 hr_10=10, // 10:00 hr_11=11, // 11:00 hr_12=12, // 12:00 hr_13=13, // 13:00 hr_14=14, // 14:00 hr_15=15, // 15:00 hr_16=16, // 16:00 hr_17=17, // 17:00 hr_18=18, // 18:00 hr_19=19, // 19:00 hr_20=20, // 20:00 hr_21=21, // 21:00 hr_22=22, // 22:00 hr_23=23 // 23:00 }; //--
Enumeration zur Auswahl von Zeitminuten:
//-- enum inmnt { mn_00=0, // Minute 0 mn_05=5, // Minute 5 mn_10=10, // Minute 10 mn_15=15, // Minute 15 mn_20=20, // Minute 20 mn_25=25, // Minute 25 mn_30=30, // Minute 30 mn_35=35, // Minute 35 mn_40=40, // Minute 40 mn_45=45, // Minute 45 mn_50=50, // Minute 50 mn_55=55 // Minute 55 }; //--
Enumeration zur Auswahl von Optionspaaren, die gehandelt werden sollen:
//-- enum PairsTrade { All30, // All Forex 30 Pairs TrdWi, // Trader Wishes Pairs Usds, // Forex USD Pairs Eurs, // Forex EUR Pairs Gbps, // Forex GBP Pairs Auds, // Forex AUD Pairs Nzds, // Forex NZD Pairs Cads, // Forex CDD Pairs Chfs, // Forex CHF Pairs Jpys // Forex JPY Pairs }; //--
Enumeration YN wird für die Optionen (Yes) oder (No) in der Experteneingabeeigenschaft verwendet.
Die Enumeration zur Bestimmung der Losgröße.
//-- enum YN { No, Yes }; //--
Die Enumeration zur Auswahl des Zeitrahmens, der für die Berechnung der Signalindikatoren verwendet werden soll:
//-- enum TFUSE { TFM5, // PERIOD_M5 TFM15, // PERIOD_M15 TFM30, // PERIOD_M30 TFH1, // PERIOD_H1 TFH2, // PERIOD_H2 TFH3, // PERIOD_H3 TFH4, // PERIOD_H4 TFH6, // PERIOD_H6 TFH8, // PERIOD_H8 TFH12, // PERIOD_H12 TFD1 // PERIOD_D1 }; //--
Anmerkung: Bei der TFUSE-Aufzählung beschränken wir die Verwendung von Zeitrahmenberechnungen für Experten nur auf die Bereiche TF-M5 bis TF-D1
Eingabeeigenschaften des Experten:
//--- input group "=== Global Strategy EA Parameter ==="; // Global Strategy EA Parameter input TFUSE tfinuse = TFH1; // Select Expert TimeFrame, default PERIOD_H1 input int KCPeriod = 20; // Input Keltner Channel Period, default 20 input ENUM_MA_METHOD KCMethod = MODE_EMA; // Select Keltner Channel MA Method, default EMA input ENUM_APPLIED_PRICE KCMAAP = PRICE_TYPICAL; // Select Keltner Channel MA Applied Price, default Price Typical input int KCATRPer = 20; // Input Keltner Channel ATR Period, default 20 input double KCATRBandsMulti = 1.0; // Input Keltner Channel ATR bands multiplier input int BBPeriod = 38; // Input Bollinger Bands® Indicator period, default 38 input double BBDevi = 1.0; // Input Bollinger Bands® Indicator Deviations, default 1.00 //--- input group "=== Select Pairs to Trade ==="; // Selected Pairs to trading input PairsTrade usepairs = All30; // Select Pairs to Use input string traderwishes = "eg. eurusd,usdchf"; // If Use Trader Wishes Pairs, input pair name here, separate by comma input string sym_prefix = ""; // Input the symbol prefix in case sensitive (if any) input string sym_suffix = ""; // Input the symbol suffix in case sensitive (if any) //-- input group "=== Money Management Lot Size Parameter ==="; // Money Management Lot Size Parameter input mmt mmlot = DynamLot; // Money Management Type input double Risk = 10.0; // Percent Equity Risk per Trade (Min=1.0% / Max=10.0%) input double Lots = 0.01; // Input Manual Lot Size FixedLot //--Trade on Specific Time input group "=== Trade on Specific Time ==="; // Trade on Specific Time input YN trd_time_zone = Yes; // Select If You Like to Trade on Specific Time Zone input tm_zone session = Cus_Session; // Select Trading Time Zone input swhour stsescuh = hr_00; // Time Hour to Start Trading Custom Session (0-23) input inmnt stsescum = mn_15; // Time Minute to Start Trading Custom Session (0-55) input swhour clsescuh = hr_23; // Time Hour to Stop Trading Custom Session (0-23) input inmnt clsescum = mn_55; // Time Minute to Stop Trading Custom Session (0-55) //--Day Trading On/Off input group "=== Day Trading On/Off ==="; // Day Trading On/Off input YN ttd0 = No; // Select Trading on Sunday (Yes) or (No) input YN ttd1 = Yes; // Select Trading on Monday (Yes) or (No) input YN ttd2 = Yes; // Select Trading on Tuesday (Yes) or (No) input YN ttd3 = Yes; // Select Trading on Wednesday (Yes) or (No) input YN ttd4 = Yes; // Select Trading on Thursday (Yes) or (No) input YN ttd5 = Yes; // Select Trading on Friday (Yes) or (No) input YN ttd6 = No; // Select Trading on Saturday (Yes) or (No) //--Trade & Order management Parameter input group "=== Trade & Order management Parameter ==="; // Trade & Order management Parameter input YN use_sl = No; // Use Order Stop Loss (Yes) or (No) input YN autosl = Yes; // Use Automatic Calculation Stop Loss (Yes) or (No) input double SLval = 30; // If Not Use Automatic SL - Input SL value in Pips input YN use_tp = Yes; // Use Order Take Profit (Yes) or (No) input YN autotp = Yes; // Use Automatic Calculation Take Profit (Yes) or (No) input double TPval = 100; // If Not Use Automatic TP - Input TP value in Pips input YN TrailingSLTP = Yes; // Use Trailing SL/TP (Yes) or (No) input YN autotrl = Yes; // Use Automatic Trailing (Yes) or (No) input double TSval = 5; // If Not Use Automatic Trailing Input Trailing value in Pips input double TSmin = 5; // Minimum Pips to start Trailing Stop input double TPmin = 25; // Input Trailing Profit Value in Pips input YN Close_by_Opps = Yes; // Close Trade By Opposite Signal (Yes) or (No) input YN SaveOnRev = Yes; // Close Trade and Save profit due to weak signal (Yes) or (No) //--Others Expert Advisor Parameter input group "=== Others Expert Advisor Parameter ==="; // Others EA Parameter input YN alerts = Yes; // Display Alerts / Messages (Yes) or (No) input YN UseEmailAlert = No; // Email Alert (Yes) or (No) input YN UseSendnotify = No; // Send Notification (Yes) or (No) input YN trade_info_display = Yes; // Select Display Trading Info on Chart (Yes) or (No) input ulong magicEA = 20231204; // Expert ID (Magic Number) //---
Anmerkung: Wenn die Eingabeeigenschaft der Experten-ID (Magic Number) leer gelassen wird, kann der Expert Advisor manuell eröffnete Aufträge verwalten.
In der Experteneigenschaftsgruppe Globale Strategie-EA-Parameter werden Händler angewiesen, den Expertenzeitrahmen für Indikatorsignalberechnungen auszuwählen und Parameter für die Indikatoren Keltner Kanal und Bollinger Bänder® einzugeben.
Bei der Erstellung des Expert Advisors in diesem Artikel werden die Eingabeeigenschaften für den Keltner-Kanal-Indikator im Expert Advisor genau die gleichen Eingabeeigenschaften wie im Indikator verwenden.
//-- input int period_kc = 20; // Input Keltner Channel Period input ENUM_MA_METHOD ma_method = MODE_EMA; // Select MA Type of smoothing input ENUM_APPLIED_PRICE ma_price = PRICE_TYPICAL; // Select MA Applied Price input int atr_period = 20; // Input ATR Period (typically over 10 or 20) input double band_multi = 1.00; // Input the Band Multiplier ATR Desired //--
In der Eigenschaftsgruppe Select Pairs to Trade (Zu handelnde Paare auswählen) müssen die Händler das zu handelnde Paar aus den 10 zur Verfügung stehenden Optionen auswählen; standardmäßig ist All Forex 30 Pairs festgelegt.
Um das zu handelnde Paar zu konfigurieren, rufen wir die Funktion HandlingSymbolArrays() auf. Mit der Funktion HandlingSymbolArrays() werden wir alle Paare behandeln, die gehandelt werden sollen.
void MCEA::HandlingSymbolArrays(void) { //--- string All30[]={"EURUSD","GBPUSD","AUDUSD","NZDUSD","USDCAD","USDCHF","USDJPY","EURGBP", "EURAUD","EURNZD","EURCAD","EURCHF","EURJPY","GBPAUD","GBPNZD","GBPCAD", "GBPCHF","GBPJPY","AUDNZD","AUDCAD","AUDCHF","AUDJPY","NZDCAD","NZDCHF", "NZDJPY","CADCHF","CADJPY","CHFJPY","XAUUSD","XAGUSD"}; // 30 pairs string USDs[]={"USDCAD","USDCHF","USDJPY","AUDUSD","EURUSD","GBPUSD","NZDUSD","XAUUSD","XAGUSD"}; // USD pairs string EURs[]={"EURAUD","EURCAD","EURCHF","EURGBP","EURJPY","EURNZD","EURUSD"}; // EUR pairs string GBPs[]={"GBPAUD","GBPCAD","GBPCHF","EURGBP","GBPJPY","GBPNZD","GBPUSD"}; // GBP pairs string AUDs[]={"AUDCAD","AUDCHF","EURAUD","GBPAUD","AUDJPY","AUDNZD","AUDUSD"}; // AUD pairs string NZDs[]={"AUDNZD","NZDCAD","NZDCHF","EURNZD","GBPNZD","NZDJPY","NZDUSD"}; // NZD pairs string CADs[]={"AUDCAD","CADCHF","EURCAD","GBPCAD","CADJPY","NZDCAD","USDCAD"}; // CAD pairs string CHFs[]={"AUDCHF","CADCHF","EURCHF","GBPCHF","NZDCHF","CHFJPY","USDCHF"}; // CHF pairs string JPYs[]={"AUDJPY","CADJPY","CHFJPY","EURJPY","GBPJPY","NZDJPY","USDJPY"}; // JPY pairs //-- sall=ArraySize(All30); arusd=ArraySize(USDs); aretc=ArraySize(EURs); ArrayResize(VSym,sall,sall); ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY); //-- if(usepairs==TrdWi && StringFind(traderwishes,"eg.",0)<0) { string to_split=traderwishes; // A string to split into substrings pairs name string sep=","; // A separator as a character ushort u_sep; // The code of the separator character //--- Get the separator code u_sep=StringGetCharacter(sep,0); //--- Split the string to substrings int p=StringSplit(to_split,u_sep,SPC); if(p>0) { for(int i=0; i<p; i++) StringToUpper(SPC[i]); //-- for(int i=0; i<p; i++) { if(ValidatePairs(SPC[i])<0) ArrayRemove(SPC,i,1); } } arspc=ArraySize(SPC); } //-- SetSymbolNamePS(); // With this function we will detect whether the Symbol Name has a prefix and/or suffix //-- if(inpre>0 || insuf>0) { if(usepairs==TrdWi && arspc>0) { for(int t=0; t<arspc; t++) { SPC[t]=pre+SPC[t]+suf; } } //-- for(int t=0; t<sall; t++) { All30[t]=pre+All30[t]+suf; } for(int t=0; t<arusd; t++) { USDs[t]=pre+USDs[t]+suf; } for(int t=0; t<aretc; t++) { EURs[t]=pre+EURs[t]+suf; } for(int t=0; t<aretc; t++) { GBPs[t]=pre+GBPs[t]+suf; } for(int t=0; t<aretc; t++) { AUDs[t]=pre+AUDs[t]+suf; } for(int t=0; t<aretc; t++) { NZDs[t]=pre+NZDs[t]+suf; } for(int t=0; t<aretc; t++) { CADs[t]=pre+CADs[t]+suf; } for(int t=0; t<aretc; t++) { CHFs[t]=pre+CHFs[t]+suf; } for(int t=0; t<aretc; t++) { JPYs[t]=pre+JPYs[t]+suf; } } //-- ArrayCopy(VSym,All30,0,0,WHOLE_ARRAY); ArrayResize(AS30,sall,sall); ArrayCopy(AS30,All30,0,0,WHOLE_ARRAY); for(int x=0; x<sall; x++) {SymbolSelect(AS30[x],true);} if(ValidatePairs(Symbol())>=0) symbfix=true; if(!symbfix) { Alert("Expert Advisors will not trade on pairs "+Symbol()); Alert("-- "+expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart."); ExpertRemove(); } //-- switch(usepairs) { case 0: // All Forex 30 Pairs { ArrayResize(DIRI,sall,sall); arrsymbx=sall; ArraySymbolResize(); ArrayCopy(DIRI,All30,0,0,WHOLE_ARRAY); pairs="Multi Currency 30 Pairs"; //-- break; } case 1: // Trader wishes pairs { ArrayResize(DIRI,arspc,arspc); arrsymbx=arspc; ArraySymbolResize(); ArrayCopy(DIRI,SPC,0,0,WHOLE_ARRAY); pairs="("+string(arspc)+") Trader Wishes Pairs"; //-- break; } case 2: // USD pairs { ArrayResize(DIRI,arusd,arusd); arrsymbx=arusd; ArraySymbolResize(); ArrayCopy(DIRI,USDs,0,0,WHOLE_ARRAY); pairs="("+string(arusd)+") Multi Currency USD Pairs"; //-- break; } case 3: // EUR pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,EURs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex EUR Pairs"; //-- break; } case 4: // GBP pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,GBPs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex GBP Pairs"; //-- break; } case 5: // AUD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,AUDs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex AUD Pairs"; //-- break; } case 6: // NZD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,NZDs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex NZD Pairs"; //-- break; } case 7: // CAD pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,CADs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex CAD Pairs"; //-- break; } case 8: // CHF pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,CHFs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex CHF Pairs"; //-- break; } case 9: // JPY pairs { ArrayResize(DIRI,aretc,aretc); arrsymbx=aretc; ArraySymbolResize(); ArrayCopy(DIRI,JPYs,0,0,WHOLE_ARRAY); pairs="("+string(aretc)+") Forex JPY Pairs"; //-- break; } } //-- return; //--- } //-end HandlingSymbolArrays() //---------//
Innerhalb der Funktion HandlingSymbolArrays() rufen wir die Funktion SetSymbolNamePS() auf. Mit SetSymbolNamePS() können wir Symbolnamen mit Präfixen und/oder Suffixen behandeln.
void MCEA::SetSymbolNamePS(void) { //--- symbfix=false; int ptriml; int ptrimr; string insymbol=Symbol(); int sym_Lenpre=StringLen(prefix); int sym_Lensuf=StringLen(suffix); if(sym_Lenpre>0) { ptriml=StringTrimLeft(suffix); ptriml=StringTrimRight(suffix); } if(sym_Lensuf>0) { ptrimr=StringTrimLeft(suffix); ptrimr=StringTrimRight(suffix); } string sym_pre=prefix; string sym_suf=suffix; //-- pre=sym_pre; suf=sym_suf; inpre=StringLen(pre); insuf=StringLen(suf); posCur1=inpre; posCur2=posCur1+3; //-- return; //--- } //-end SetSymbolNamePS() //---------//
Anmerkung: Der Experte wird die Paare validieren. Wenn der Händler einen Fehler bei der Eingabe des Paar-Namens oder des Paar-Präfix-Namens und/oder des Paar-Suffix-Namens (Tippfehler) macht, oder wenn die Paar-Validierung fehlschlägt, erhält der Experte eine Warnung und der Expert Advisor wird aus dem Chart entfernt.
In der Experten-Eingabeeigenschaftsgruppe Trade on Specific Time (Handel zu einer bestimmten Zeit) wählt der Händler aus, ob er in einer bestimmten Zeitzone handeln möchte (Ja) oder (Nein), und wenn Ja, wählen Sie die Aufzählungsoptionen:
- Handel während einer nutzerdefinierten Sitzung (Custom Session)
- Handel während der neuseeländischen Sitzung
- Handel während der Australien Sydney Session
- Handel während der Asien Tokyo Session
- Handel während der Europa London Session
- Handel während der Amerika New York Session
Handel während der Custom Session: In dieser Sitzung müssen die Händler die Zeit oder die Stunden und Minuten für den Handelsbeginn und die Stunden und Minuten für den Handelsschluss festlegen. Das bedeutet, dass die EA nur während des angegebenen Zeitraums vom Beginn bis zum Ende Aktivitäten durchführen wird.
Beim Handel zwischen der New Zealand Session und der New York US Session wird die Zeit vom Beginn des Handels bis zum Abschluss des Handels vom EA berechnet.
Klasse für funktionierenden Expert Advisor
Um den Aufbau und die Konfiguration des Expert Advisor-Workflows zu bestimmen, erstellen wir eine Klasse, die alle Variablen, Objekte und Funktionen deklariert, die für diesen Multi-Currency Expert Advisor erforderlich sind.
//+------------------------------------------------------------------+ //| Class for working Expert Advisor | //+------------------------------------------------------------------+ class MCEA { //--- private: //---- int x_year; // Year int x_mon; // Month int x_day; // Day of the month int x_hour; // Hour in a day int x_min; // Minutes int x_sec; // Seconds //-- int oBm, oSm, ldig; //--- Variables used in prefix and suffix symbols int posCur1, posCur2; int inpre, insuf; bool symbfix; string pre,suf; string prefix,suffix; //--- Variables are used in Trading Time Zone int ishour, onhour; int tftrlst, tfcinws; datetime rem, znop, zncl, zntm; datetime SesCuOp, SesCuCl, Ses01Op, Ses01Cl, Ses02Op, Ses02Cl, Ses03Op, Ses03Cl, Ses04Op, Ses04Cl, Ses05Op, Ses05Cl, SesNoOp, SesNoCl; //-- string tz_ses, tz_opn, tz_cls; //-- string tmopcu, tmclcu, tmop01, tmcl01, tmop02, tmcl02, tmop03, tmcl03, tmop04, tmcl04, tmop05, tmcl05, tmopno, tmclno; //---------------------- //-- double LotPS; double slv, tpv, pip, xpip; double SARstep, SARmaxi; double floatprofit, fixclprofit; //-- string pairs, hariini, daytrade, trade_mode; //-- double OPEN[], HIGH[], LOW[], CLOSE[]; datetime TIME[]; datetime closetime; //-- //------------ //------------ void SetSymbolNamePS(void); void HandlingSymbolArrays(void); void Set_Time_Zone(void); void Time_Zone(void); bool Trade_session(void); string PosTimeZone(void); int ThisTime(const int reqmode); int ReqTime(datetime reqtime,const int reqmode); //-- int DirectionMove(const string symbol,const ENUM_TIMEFRAMES stf); int BBOnKeltnerChannel(const string symbol); int PARSAR05(const string symbol); int LotDig(const string symbol); //-- double MLots(const string symbx); double NonZeroDiv(double val1,double val2); double OrderSLSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice); double OrderTPSet(const string xsymb,ENUM_ORDER_TYPE type,double atprice); double SetOrderSL(const string xsymb,ENUM_POSITION_TYPE type,double atprice); double SetOrderTP(const string xsymb,ENUM_POSITION_TYPE type,double atprice); double TSPrice(const string xsymb,ENUM_POSITION_TYPE ptype,int TS_type); //-- string ReqDate(int d,int h,int m); string TF2Str(ENUM_TIMEFRAMES period); string timehr(int hr,int mn); string TradingDay(void); string AccountMode(); string GetCommentForOrder(void) { return(expname); } //------------ public: //--- //-- BBOnKeltnerChannel_MCEA Config -- string DIRI[], AS30[], VSym[]; string SPC[]; string USD[]; string EUR[]; string GBP[]; string AUD[]; string NZD[]; string CAD[]; string CHF[]; string JPY[]; //-- string expname; string indiname; //-- int hKC[]; int hBB[]; int hParIU[], hPar05[]; int ALO, dgts, arrsar, arrsymbx; int sall, arusd, aretc, arspc, arper; ulong slip; //-- double profitb[], profits[]; //-- int Buy, Sell; int ccur, psec, xtto, checktml; int OpOr[],xob[],xos[]; //-- int year, // Year mon, // Month day, // Day hour, // Hour min, // Minutes sec, // Seconds dow, // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) doy; // Day number of the year (January 1st is assigned the number value of zero) //-- ENUM_TIMEFRAMES TFt, TFT05; //-- bool PanelExtra; //------------ MCEA(void); ~MCEA(void); //------------ //-- virtual void BBOnKeltnerChannel_MCEA_Config(void); virtual void ExpertActionTrade(void); //-- void ArraySymbolResize(void); void CurrentSymbolSet(const string symbol); void Pips(const string symbol); void TradeInfo(void); void Do_Alerts(const string symbx,string msgText); void CheckOpenPMx(const string symbx); void SetSLTPOrders(void); void CloseBuyPositions(const string symbol); void CloseSellPositions(const string symbol); void CloseAllOrders(void); void CheckClose(const string symbx); void TodayOrders(void); void UpdatePrice(const string symbol,ENUM_TIMEFRAMES xtf); void RefreshPrice(const string symbx,ENUM_TIMEFRAMES xtf,int bars); //-- bool RefreshTick(const string symbx); bool TradingToday(void); bool OpenBuy(const string symbol); bool OpenSell(const string symbol); bool ModifyOrderSLTP(double mStop,double ordtp); bool ModifySLTP(const string symbx,int TS_type); bool CloseAllProfit(void); bool ManualCloseAllProfit(void); //-- int PairsIdxArray(const string symbol); int ValidatePairs(const string symbol); int GetOpenPosition(const string symbol); int GetCloseInWeakSignal(const string symbol,int exis); //-- string getUninitReasonText(int reasonCode); //-- //------------ //--- }; //-end class MCEA //---------//
Die allererste und wichtigste Funktion im Arbeitsprozess des Multi-Currency Expert Advisor, die von OnInit() aufgerufen wird, ist BBOnKeltnerChannel_MCEA_Config().
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- mc.BBOnKeltnerChannel_MCEA_Config(); //-- return(INIT_SUCCEEDED); //--- } //-end OnInit() //---------//
Die Funktion BBOnKeltnerChannel_MCEA_Config() konfiguriert alle zu verwendenden Symbole, alle Handles der verwendeten Indikatoren und einige wichtige Funktionen des Headers der Include-Datei für den Expert Advisor Workflow.
In den Funktionszeilen 468 bis 484 wird erklärt, wie der Zeitrahmen zu handhaben ist und wie man Indikator-Handles für alle verwendeten Indikatoren erstellt.
//+------------------------------------------------------------------+ //| Expert Configuration | //+------------------------------------------------------------------+ void MCEA::BBOnKeltnerChannel_MCEA_Config(void) { //--- //-- HandlingSymbolArrays(); // With this function we will handle all pairs that will be traded //-- TFT05=PERIOD_M5; ENUM_TIMEFRAMES TFs[]={PERIOD_M5,PERIOD_M15,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,PERIOD_D1}; int arTFs=ArraySize(TFs); //-- for(int x=0; x<arTFs; x++) if(tfinuse==x) TFt=TFs[x]; // TF for calculation signal //-- //-- Keltner Channel and Bollinger Bands® Indicators handle for all symbol for(int x=0; x<arrsymbx; x++) { hKC[x]=iCustom(DIRI[x],TFt,indiname,KCPeriod,KCMethod,KCMAAP,KCATRPer,KCATRBandsMulti); //-- Handle for the Keltner Channel indicator hParIU[x]=iSAR(DIRI[x],TFt,SARstep,SARmaxi); //-- Handle for the iSAR indicator according to the selected Timeframe hPar05[x]=iSAR(DIRI[x],TFT05,SARstep,SARmaxi); //-- Handle for the iSAR indicator for M5 Timeframe //-- } //-- for(int x=0; x<arrsymbx; x++) hBB[x]=iBands(DIRI[x],TFt,BBPeriod,0,BBDevi,hKC[x]); //-- Handle for the iBands On Keltner Channel Indicator handle //-- ALO=(int)mc_account.LimitOrders()>sall ? sall : (int)mc_account.LimitOrders(); //-- LotPS=(double)ALO; //-- mc_trade.SetExpertMagicNumber(magicEA); mc_trade.SetDeviationInPoints(slip); mc_trade.SetMarginMode(); Set_Time_Zone(); //-- return; //--- } //-end BBOnKeltnerChannel_MCEA_Config() //---------//
2. Experten-Tick-Funktion
Innerhalb der Expert Tick Funktion OnTick() werden wir eine der wichtigsten Funktionen in einem Multi-Currency Expert Advisor aufrufen, nämlich die ExpertActionTrade() Funktion.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(void) { //--- mc.ExpertActionTrade(); //-- return; //--- } //-end OnTick() //---------//
Dies ist der Ablauf des EA-Arbeitsprozesses innerhalb dieser Funktion.
Die Funktion ExpertActionTrade() führt alle Aktivitäten durch und verwaltet den automatischen Handel, angefangen von Eröffnungs- und Schließaufträgen, den Trailing-Stops oder -Profits und anderen zusätzlichen Aktivitäten.
void MCEA::ExpertActionTrade(void) { //--- //--Check Trading Terminal ResetLastError(); //-- if(!MQLInfoInteger(MQL_TRADE_ALLOWED) && mc.checktml==0) //-- Check whether MT5 Algorithmic trading is Allow or Prohibit { mc.Do_Alerts(Symbol(),"Trading Expert at "+Symbol()+" are NOT Allowed by Setting."); mc.checktml=1; //-- Variable checktml is given a value of 1, so that the alert is only done once. return; } //-- if(!DisplayManualButton("M","C","R")) DisplayManualButton(); //-- Show the expert manual button panel //-- if(trade_info_display==Yes) mc.TradeInfo(); //-- Displayed Trading Info on Chart //--- //-- int mcsec=mc.ThisTime(mc.sec); //-- if(fmod((double)mcsec,5.0)==0) mc.ccur=mcsec; //-- if(mc.ccur!=mc.psec) { string symbol; //-- Here we start with the rotation of the name of all symbol or pairs to be traded for(int x=0; x<mc.arrsymbx && !IsStopped(); x++) { //-- if(mc.DIRI[x]==Symbol()) symbol=Symbol(); else symbol=mc.DIRI[x]; //-- mc.CurrentSymbolSet(symbol); //-- if(mc.TradingToday() && mc.Trade_session()) { //-- mc.OpOr[x]=mc.GetOpenPosition(symbol); //-- Get trading signals to open positions //-- //-- and store in the variable OpOr[x] if(mc.OpOr[x]==mc.Buy) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Buy" (value=1) { //-- mc.CheckOpenPMx(symbol); //-- if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol); //-- if(mc.xob[x]==0 && mc.xtto<mc.ALO) mc.OpenBuy(symbol); else if(mc.xtto>=mc.ALO) { //-- mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+ "\n the limit = "+string(mc.ALO)+" Orders "); //-- mc.CheckOpenPMx(symbol); //-- if(mc.xos[x]>0 && mc.profits[x]<-1.02 && mc.xob[x]==0) {mc.CloseSellPositions(symbol); mc.OpenBuy(symbol);} else if(SaveOnRev==Yes) mc.CloseAllProfit(); } } if(mc.OpOr[x]==mc.Sell) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Sell" (value=-1) { //-- mc.CheckOpenPMx(symbol); //-- if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol); //-- if(mc.xos[x]==0 && mc.xtto<mc.ALO) mc.OpenSell(symbol); else if(mc.xtto>=mc.ALO) { //-- mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+ "\n the limit = "+string(mc.ALO)+" Orders "); //-- mc.CheckOpenPMx(symbol); //-- if(mc.xob[x]>0 && mc.profitb[x]<-1.02 && mc.xos[x]==0) {mc.CloseBuyPositions(symbol); mc.OpenSell(symbol);} else if(SaveOnRev==Yes) mc.CloseAllProfit(); } } } //-- mc.CheckOpenPMx(symbol); //-- if(mc.xtto>0) { //-- if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes) { mc.CheckOpenPMx(symbol); if(mc.profitb[x]>0.02 && mc.xob[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) { mc.CloseBuyPositions(symbol); mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal."); } if(mc.profits[x]>0.02 && mc.xos[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) { mc.CloseSellPositions(symbol); mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal."); } } //-- if(TrailingSLTP==Yes) //-- Use Trailing SL/TP (Yes) { if(autotrl==Yes) mc.ModifySLTP(symbol,1); //-- If Use Automatic Trailing (Yes) if(autotrl==No) mc.ModifySLTP(symbol,0); //-- Use Automatic Trailing (No) } } //-- mc.CheckClose(symbol); } //-- mc.psec=mc.ccur; } //-- return; //--- } //-end ExpertActionTrade() //---------//
Die Eigenschaftsgruppe „Day Trading On/Off“ (Taghandel ein/aus) gibt Händlern die Möglichkeit, an den angegebenen Tagen von Sonntag bis Samstag zu handeln. Auf diese Weise können die Händler die Experten für den Handel an dem angegebenen Tag aktivieren oder deaktivieren, indem sie die Option (Ja) oder (Nein) verwenden.
//--Day Trading On/Off input group "=== Day Trading On/Off ==="; // Day Trading On/Off input YN ttd0 = No; // Select Trading on Sunday (Yes) or (No) input YN ttd1 = Yes; // Select Trading on Monday (Yes) or (No) input YN ttd2 = Yes; // Select Trading on Tuesday (Yes) or (No) input YN ttd3 = Yes; // Select Trading on Wednesday (Yes) or (No) input YN ttd4 = Yes; // Select Trading on Thursday (Yes) or (No) input YN ttd5 = Yes; // Select Trading on Friday (Yes) or (No) input YN ttd6 = No; // Select Trading on Saturday (Yes) or (No)
Die Ausführung für Day Trading On/Off ist wie folgt:
bool MCEA::TradingToday(void) { //--- bool tradetoday=false; int trdday=ThisTime(dow); hariini="No"; //-- int ttd[]; ArrayResize(ttd,7); ttd[0]=ttd0; ttd[1]=ttd1; ttd[2]=ttd2; ttd[3]=ttd3; ttd[4]=ttd4; ttd[5]=ttd5; ttd[6]=ttd6; //-- if(ttd[trdday]==Yes) {tradetoday=true; hariini="Yes";} //-- return(tradetoday); //--- } //-end TradingToday() //---------//
Anmerkungen: Die Bedingungen für den Tageshandel (Day Trading On/Off) werden in der Handelsinfo im Chart angezeigt.
In der Eigenschaftsgruppe „Trade on Specific Time“ (Handel zu bestimmten Zeiten) des Expert Advisors haben Händler die Möglichkeit, nach Zeitzonen zu handeln.
input group "=== Trade on Specific Time ==="; // Trade on Specific Time input YN trd_time_zone = Yes; // Select If You Like to Trade on Specific Time Zone input tm_zone session = Cus_Session; // Select Trading Time Zone input swhour stsescuh = hr_00; // Time Hour to Start Trading Custom Session (0-23) input inmnt stsescum = mn_15; // Time Minute to Start Trading Custom Session (0-55) input swhour clsescuh = hr_23; // Time Hour to Stop Trading Custom Session (0-23) input inmnt clsescum = mn_55; // Time Minute to Stop Trading Custom Session (0-55)
Anmerkungen: Wie oben erläutert, wird im Falle des Handels an der New Zealand Session zum Handel an der US New York Session die Zeit vom Beginn des Handels bis zum Handelsschluss vom EA berechnet. In den Eigenschaften des Experteneintrags müssen Händler also nur die Uhrzeit für die Stunde und Minute des Handelsbeginns und die Uhrzeit für die Stunde und Minute des Handelsendes für die nutzerdefinierte Sitzung festlegen.
Speziell für den Zeitzonenhandel wird ein Aufruf der booleschen Funktion Trade_session() zur Funktion ExpertActionTrade() hinzugefügt.
Wenn Trade_session() true ist, dann wird der EA-Arbeitsprozess fortgesetzt, bis er beendet ist, aber wenn er false ist, dann wird der EA nur die Aufgaben „Close Trade and Save Profit due to Weak Signal if it is (Yes)“ (Schließen der Position und Sichern des Gewinns aufgrund schwachen Signals, wenn es (Ja) ist) und „Trailing Stop if it is (Yes)“ (Trailing Stop, wenn ja (Ja)) ausführen.
bool MCEA::Trade_session(void) { //--- bool trd_ses=false; ishour=ThisTime(hour); if(ishour!=onhour) Set_Time_Zone(); datetime tcurr=TimeCurrent(); // Server Time //-- switch(session) { case Cus_Session: { if(tcurr>=SesCuOp && tcurr<=SesCuCl) trd_ses=true; break; } case New_Zealand: { if(tcurr>=Ses01Op && tcurr<=Ses01Cl) trd_ses=true; break; } case Australia: { if(tcurr>=Ses02Op && tcurr<=Ses02Cl) trd_ses=true; break; } case Asia_Tokyo: { if(tcurr>=Ses03Op && tcurr<=Ses03Cl) trd_ses=true; break; } case Europe_London: { if(tcurr>=Ses04Op && tcurr<=Ses04Cl) trd_ses=true; break; } case US_New_York: { if(tcurr>=Ses05Op && tcurr<=Ses05Cl) trd_ses=true; break; } } //-- if(trd_time_zone==No) { if(tcurr>=SesNoOp && tcurr<=SesNoCl) trd_ses=true; } //-- onhour=ishour; //-- return(trd_ses); //--- } //-end Trade_session() //---------//
3. Wie kann ich Handelssignale für offene Positionen erhalten?
Um ein Signal zu erhalten, ruft die Funktion ExpertActionTrade() die Funktion GetOpenPosition() auf.
int MCEA::GetOpenPosition(const string symbol) // Signal Open Position { //--- int ret=0; int rise=1, down=-1; //-- int BBOnKC=BBOnKeltnerChannel(symbol); //-- if(BBOnKC==rise) ret=rise; if(BBOnKC==down) ret=down; //-- return(ret); //--- } //-end GetOpenPosition() //---------//
Die Funktion GetOpenPosition() ruft BBOnKeltnerChannel() Funktionen auf, die die Signalberechnungen durchführt.
int MCEA::BBOnKeltnerChannel(const string symbol) // Fungction of Bollinger Bands® On Keltner Channel Indicator { //--- int ret=0; int rise=1, down=-1; int br=3; Pips(symbol); double difud=mc_symbol.NormalizePrice(1.5*pip); //-- double KCmb[], // Destination array for Keltner Channel Middle Line buffer KCub[], // Destination array for Keltner Channel Upper Band buffer KClb[]; // Destination array for Keltner Channel Lower Band buffer double BBmb[], // Destination array for Bollinger Bands® BASE_LINE buffer BBub[], // Destination array for Bollinger Bands® UPPER_BAND buffer BBlb[]; // Destination array for Bollinger Bands® LOWER_BAND buffer //-- ArrayResize(KCmb,br,br); ArrayResize(KCub,br,br); ArrayResize(KClb,br,br); ArrayResize(BBmb,br,br); ArrayResize(BBub,br,br); ArrayResize(BBlb,br,br); ArraySetAsSeries(KCmb,true); ArraySetAsSeries(KCub,true); ArraySetAsSeries(KClb,true); ArraySetAsSeries(BBmb,true); ArraySetAsSeries(BBub,true); ArraySetAsSeries(BBlb,true); //-- int xx=PairsIdxArray(symbol); //-- CopyBuffer(hKC[xx],0,0,br,KCmb); CopyBuffer(hKC[xx],1,0,br,KCub); CopyBuffer(hKC[xx],2,0,br,KClb); CopyBuffer(hBB[xx],0,0,br,BBmb); CopyBuffer(hBB[xx],1,0,br,BBub); CopyBuffer(hBB[xx],2,0,br,BBlb); //-- bool BBKCrise1=(KCmb[1]<=BBlb[1] && KCmb[0]>BBlb[0]+difud); bool BBKCrise2=(KCmb[1]<=BBmb[1] && KCmb[0]>BBmb[0]+difud); bool BBKCrise3=(KCmb[1]<=BBub[1] && KCmb[0]>BBub[0]+difud); //-- bool BBKCdown1=(KCmb[1]>=BBub[1] && KCmb[0]<BBub[0]-difud); bool BBKCdown2=(KCmb[1]>=BBmb[1] && KCmb[0]<BBmb[0]-difud); bool BBKCdown3=(KCmb[1]>=BBlb[1] && KCmb[0]<BBlb[0]-difud); //-- if(BBKCrise1 || BBKCrise2 || BBKCrise3) ret=rise; if(BBKCdown1 || BBKCdown2 || BBKCdown3) ret=down; //-- return(ret); //--- } //-end BBOnKeltnerChannel() //---------//
Wie Sie sehen können, wird innerhalb der Funktion BBOnKeltnerChannel() eine Funktion verwendet und aufgerufen, nämlich die Funktion PairsIdxArray().
int xx=PairsIdxArray(symbol);
int MCEA::PairsIdxArray(const string symbol) { //--- int pidx=-1; //-- for(int x=0; x<arrsymbx; x++) { if(DIRI[x]==symbol) { pidx=x; break; } } //-- return(pidx); //--- } //-end PairsIdxArray() //---------//
Die Funktion PairsIdxArray() wird verwendet, um den Namen des gewünschten Symbols und die Handles seiner Indikatoren zu erhalten. Dann wird das entsprechende Indikator-Handle aufgerufen, um den Pufferwerte der Signale vom Keltner Kanal und den Bollinger Bänder® aus diesem Zeitrahmen abzurufen.
Als Nächstes müssen wir die Puffernummer des Keltner-Kanals kennen. Beim Keltner-Kanal wissen wir in der Funktion OnInit(), dass die Puffernummern wie folgt sind: 0 - Mittlere Linie, 1 - Oberes Band, 2 - Unteres Band
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //-- assignment of array to indicator buffer SetIndexBuffer(0,KC_Middle,INDICATOR_DATA); SetIndexBuffer(1,KC_UpperB,INDICATOR_DATA); SetIndexBuffer(2,KC_LowerB,INDICATOR_DATA); SetIndexBuffer(3,KC_ATRtemp,INDICATOR_CALCULATIONS); //-- handleMA=iMA(Symbol(),Period(),period_kc,0,MODE_EMA,ma_price); //-- if(handleMA==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the Moving Average indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //-- handleATR=iATR(Symbol(),Period(),atr_period); //-- if(handleATR==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the ATR indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //-- short_name=StringFormat("Keltner Channel(%d, %s, %.2f)",period_kc,EnumToString(ma_method),band_multi); IndicatorSetString(INDICATOR_SHORTNAME,short_name); IndicatorSetInteger(INDICATOR_DIGITS,Digits()); //-- return(INIT_SUCCEEDED); //--- } //---------//
In diesem Fall müssen wir auch die Anzahl der Puffer des Bollinger Bänder® Indikators kennen.
Die Erklärung dazu ist in der Funktion iBands® angegeben:
„Hinweis Puffernummern: 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND“
applied_price
[in] Der verwendete Preis. Kann eine der Preiskonstanten ENUM_APPLIED_PRICE oder ein Handle eines anderen Indikators sein.
In diesem Artikel werden Expert Advisors den angewandten Preis für Bollinger Bänder® aus dem Keltner Kanal Indikator Handle verwenden.
Um also den Pufferwert für jede Zeile der Indikatoren Keltner Kanal und Bollinger Bänder® zu erhalten, kopieren wir jeden Puffer aus dem Indikatoren-Handle.
Kopieren des Puffers der Mittellinie (Puffer 0) vom Handle des Keltner-Kanals in das Ziel-Array:
CopyBuffer(hKC[xx],0,0,br,KCmb);
Kopieren des Puffers des oberen Bandes (Puffer 1) vom Handle des Keltner-Kanals in das Ziel-Array:
CopyBuffer(hKC[xx],1,0,br,KCub);
Kopieren des Puffers des unteren Bandes (Puffer 2) vom Handle des Keltner-Kanals in das Ziel-Array:
CopyBuffer(hKC[xx],2,0,br,KClb);
Kopieren des BASE_LINE Puffers (Puffer 0) der Bollinger Bänder® in das Ziel-Array:
CopyBuffer(hBB[xx],0,0,br,BBmb);
Kopieren des UPPER_BAND Puffers (Puffer 1) der Bollinger Bänder® in das Ziel-Array:
CopyBuffer(hBB[xx],1,0,br,BBub);
Kopieren des LOWER_BAND Puffers (Puffer 2) der Bollinger Bänder® in das Ziel-Array:
CopyBuffer(hBB[xx],2,0,br,BBlb);
Als Nächstes werden die folgenden Ergebnisse von der Funktion GetOpenPosition() zurückgegeben:
- Wert 0, Signal unbekannt.
- Der Wert 1 ist ein Signal für einen Kaufauftrag.
- Der Wert -1 ist ein Signal für einen Verkaufsauftrag.
Der Expert Advisor ruft die Funktion OpenBuy() auf, wenn die Funktion GetOpenPosition() den Wert 1 zurückgibt.
bool MCEA::OpenBuy(const string symbol) { //--- ResetLastError(); //-- bool buyopen = false; string ldComm = GetCommentForOrder()+"_Buy"; double ldLot = MLots(symbol); ENUM_ORDER_TYPE type_req = ORDER_TYPE_BUY; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- structure is set to zero ZeroMemory(req); ZeroMemory(res); ZeroMemory(check); //-- CurrentSymbolSet(symbol); double SL=OrderSLSet(symbol,type_req,mc_symbol.Bid()); double TP=OrderTPSet(symbol,type_req,mc_symbol.Ask()); //-- if(RefreshTick(symbol)) buyopen=mc_trade.Buy(ldLot,symbol,mc_symbol.Ask(),SL,TP,ldComm); //-- int error=GetLastError(); if(buyopen||error==0) { string bsopen="Open BUY Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!"; Do_Alerts(symbol,bsopen); } else { mc_trade.CheckResult(check); Do_Alerts(Symbol(),"Open BUY order for "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); return(false); } //-- return(buyopen); //-- //--- } //-end OpenBuy //---------//
In der Zwischenzeit ruft der Expert Advisor die Funktion OpenSell() auf, wenn die Funktion GetOpenPosition() einen Wert von -1 zurückgibt.
bool MCEA::OpenSell(const string symbol) { //--- ResetLastError(); //-- bool selopen = false; string sdComm = GetCommentForOrder()+"_Sell"; double sdLot = MLots(symbol); ENUM_ORDER_TYPE type_req = ORDER_TYPE_SELL; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- structure is set to zero ZeroMemory(req); ZeroMemory(res); ZeroMemory(check); //-- CurrentSymbolSet(symbol); double SL=OrderSLSet(symbol,type_req,mc_symbol.Ask()); double TP=OrderTPSet(symbol,type_req,mc_symbol.Bid()); //-- if(RefreshTick(symbol)) selopen=mc_trade.Sell(sdLot,symbol,mc_symbol.Bid(),SL,TP,sdComm); //-- int error=GetLastError(); if(selopen||error==0) { string bsopen="Open SELL Order for "+symbol+" ~ Ticket= ["+(string)mc_trade.ResultOrder()+"] successfully..!"; Do_Alerts(symbol,bsopen); } else { mc_trade.CheckResult(check); Do_Alerts(Symbol(),"Open SELL order for "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); return(false); } //-- return(selopen); //-- //--- } //-end OpenSell //---------//
4. ChartEvent Funktion.
Um die Effektivität und Effizienz bei der Verwendung von Multi-Currency Expert Advisors zu unterstützen, wird es als notwendig erachtet, eine oder mehrere manuelle Schaltflächen für die Verwaltung von Aufträgen und die Änderung von Charts oder Symbolen zu erstellen.
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- //--- handling CHARTEVENT_CLICK event ("Clicking the chart") ResetLastError(); //-- ENUM_TIMEFRAMES CCS=mc.TFt; //-- if(id==CHARTEVENT_OBJECT_CLICK) { int lensymbol=StringLen(Symbol()); int lensparam=StringLen(sparam); //-- //--- if "Set SL All Orders" button is click if(sparam=="Set SL/TP All Orders") { mc.SetSLTPOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Set SL/TP All Orders"); //--- unpress the button ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_STATE,false); ObjectSetInteger(0,"Set SL/TP All Orders",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "Close All Order" button is click if(sparam=="Close All Order") { mc.CloseAllOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Orders"); //--- unpress the button ObjectSetInteger(0,"Close All Order",OBJPROP_STATE,false); ObjectSetInteger(0,"Close All Order",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "Close All Profit" button is click if(sparam=="Close All Profit") { mc.ManualCloseAllProfit(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Profit"); //--- unpress the button ObjectSetInteger(0,"Close All Profit",OBJPROP_STATE,false); ObjectSetInteger(0,"Close All Profit",OBJPROP_ZORDER,0); CreateManualPanel(); } //--- if "X" button is click if(sparam=="X") { ObjectsDeleteAll(0,0,OBJ_BUTTON); ObjectsDeleteAll(0,0,OBJ_LABEL); ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL); //--- unpress the button ObjectSetInteger(0,"X",OBJPROP_STATE,false); ObjectSetInteger(0,"X",OBJPROP_ZORDER,0); //-- DeleteButtonX(); mc.PanelExtra=false; DisplayManualButton(); } //--- if "M" button is click if(sparam=="M") { //--- unpress the button ObjectSetInteger(0,"M",OBJPROP_STATE,false); ObjectSetInteger(0,"M",OBJPROP_ZORDER,0); mc.PanelExtra=true; CreateManualPanel(); } //--- if "C" button is click if(sparam=="C") { //--- unpress the button ObjectSetInteger(0,"C",OBJPROP_STATE,false); ObjectSetInteger(0,"C",OBJPROP_ZORDER,0); mc.PanelExtra=true; CreateSymbolPanel(); } //--- if "R" button is click if(sparam=="R") { Alert("-- "+mc.expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart."); ExpertRemove(); //--- unpress the button ObjectSetInteger(0,"R",OBJPROP_STATE,false); ObjectSetInteger(0,"R",OBJPROP_ZORDER,0); if(!ChartSetSymbolPeriod(0,Symbol(),Period())) ChartSetSymbolPeriod(0,Symbol(),Period()); DeletePanelButton(); ChartRedraw(0); } //--- if Symbol button is click if(lensparam==lensymbol) { int sx=mc.ValidatePairs(sparam); ChangeChartSymbol(mc.AS30[sx],CCS); mc.PanelExtra=false; } //-- } //-- return; //--- } //-end OnChartEvent() //---------//
In der anderen Parametergruppe der Eingabeeigenschaften des Expert Advisors kann der Händler auswählen, ob Handelsinformationen auf dem Chart angezeigt werden sollen (Yes) oder (No).
Wenn diese Option ausgewählt ist (Yes), werden die Handelsinformationen auf dem Chart angezeigt, an das der Expert Advisor durch Aufruf der Funktion TradeInfo() angehängt ist.
Als Teil der Funktion TradeInfo() haben wir auch eine Funktion hinzugefügt, die die Zeit entsprechend den Bedingungen der Handelszeitzone beschreibt.
string MCEA::PosTimeZone(void) { //--- string tzpos=""; //-- if(ReqTime(zntm,day)>ThisTime(day)) { tzpos=tz_opn+ " Next day to " +tz_cls + " Next day"; } else if(TimeCurrent()<znop) { if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)==ReqTime(zncl,day)) tzpos=tz_opn+" to " +tz_cls+ " Today"; //else if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day)) tzpos=tz_opn+ " Today to " +tz_cls+ " Next day"; } else if(TimeCurrent()>=znop && TimeCurrent()<zncl) { if(ThisTime(day)<ReqTime(zncl,day)) tzpos=tz_opn+ " Today to " +tz_cls+ " Next day"; else if(ThisTime(day)==ReqTime(zncl,day)) tzpos=tz_opn+" to " +tz_cls+ " Today"; } else if(ThisTime(day)==ReqTime(znop,day) && ThisTime(day)<ReqTime(zncl,day)) { tzpos=tz_opn+" Today to " +tz_cls+ " Next day"; } //-- return(tzpos); //---- } //-end PosTimeZone() //---------//
void MCEA::TradeInfo(void) // function: write comments on the chart { //---- Pips(Symbol()); double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/xpip; rem=zntm-TimeCurrent(); string postime=PosTimeZone(); string eawait=" - Waiting for active time..!"; //-- string comm=""; TodayOrders(); //-- comm="\n :: Server Date Time : "+string(ThisTime(year))+"."+string(ThisTime(mon))+"."+string(ThisTime(day))+ " "+TimeToString(TimeCurrent(),TIME_SECONDS)+ "\n ------------------------------------------------------------"+ "\n :: Broker : "+ TerminalInfoString(TERMINAL_COMPANY)+ "\n :: Expert Name : "+ expname+ "\n :: Acc. Name : "+ mc_account.Name()+ "\n :: Acc. Number : "+ (string)mc_account.Login()+ "\n :: Acc. TradeMode : "+ AccountMode()+ "\n :: Acc. Leverage : 1 : "+ (string)mc_account.Leverage()+ "\n :: Acc. Equity : "+ DoubleToString(mc_account.Equity(),2)+ "\n :: Margin Mode : "+ (string)mc_account.MarginModeDescription()+ "\n :: Magic Number : "+ string(magicEA)+ "\n :: Trade on TF : "+ EnumToString(TFt)+ "\n :: Today Trading : "+ TradingDay()+" : "+hariini+ "\n :: Trading Session : "+ tz_ses+ "\n :: Trading Time : "+ postime; if(TimeCurrent()<zntm) { comm=comm+ "\n :: Time Remaining : "+(string)ReqTime(rem,hour)+":"+(string)ReqTime(rem,min)+":"+(string)ReqTime(rem,sec) + eawait; } comm=comm+ "\n ------------------------------------------------------------"+ "\n :: Trading Pairs : "+pairs+ "\n :: BUY Market : "+string(oBm)+ "\n :: SELL Market : "+string(oSm)+ "\n :: Total Order : "+string(oBm+oSm)+ "\n :: Order Profit : "+DoubleToString(floatprofit,2)+ "\n :: Fixed Profit : "+DoubleToString(fixclprofit,2)+ "\n :: Float Money : "+DoubleToString(floatprofit,2)+ "\n :: Nett Profit : "+DoubleToString(floatprofit+fixclprofit,2); //-- Comment(comm); ChartRedraw(0); return; //---- } //-end TradeInfo() //---------//
Die Schnittstelle des Multi-Currency Expert Advisor BBOnKeltnerChannel_MCEA sieht ähnlich aus wie in der folgenden Abbildung.
Unter dem Namen des Expert Advisors BBOnKeltnerChannel_MCEA finden Sie die Schaltflächen „M“, „C“ und „R“.
Wenn Sie auf die Schaltfläche M klicken, wird ein Feld mit Schaltflächen für manuelle Klicks angezeigt, wie in der folgenden Abbildung dargestellt.
Der Händler kann Aufträge manuell verwalten, wenn die Schaltfläche für manuelle Klicks angezeigt wird:
1. Wie oben erläutert, kann der Händler, wenn er die Parameter „Use Order Stop Loss (No) and/or Use Order Take Profit (No)“ (Stop Loss verwenden (Nein) und/oder Take Profit verwenden (Nein)) eingibt, dann aber beabsichtigt, Stop Loss oder Take Profit für alle Aufträge zu verwenden, mit einem einzigen Klick auf die Schaltfläche „Set SL / TP All Orders“ (SL/TP für alle Aufträge setzen) alle Aufträge ändern und Stop Loss und/oder Take Profit anwenden.
void MCEA::SetSLTPOrders(void) { //--- ResetLastError(); MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- double modbuysl=0; double modselsl=0; double modbuytp=0; double modseltp=0; string position_symbol; int totalorder=PositionsTotal(); //-- for(int i=totalorder-1; i>=0; i--) { string symbol=PositionGetSymbol(i); position_symbol=symbol; if(mc_position.Magic()==magicEA) { ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { Pips(symbol); RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_take = mc_position.TakeProfit(); modbuysl=SetOrderSL(symbol,opstype,pos_open); if(price<modbuysl) modbuysl=mc_symbol.NormalizePrice(price-slip*pip); modbuytp=SetOrderTP(symbol,opstype,pos_open); if(price>modbuytp) modbuytp=mc_symbol.NormalizePrice(price+slip*pip); //-- if(pos_stop==0.0 || pos_take==0.0) { if(!mc_trade.PositionModify(position_symbol,modbuysl,modbuytp)) { mc_trade.CheckResult(check); Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); } } } if(opstype==POSITION_TYPE_SELL) { Pips(symbol); RefreshTick(symbol); double price = mc_position.PriceCurrent(); double pos_open = mc_position.PriceOpen(); double pos_stop = mc_position.StopLoss(); double pos_take = mc_position.TakeProfit(); modselsl=SetOrderSL(symbol,opstype,pos_open); if(price>modselsl) modselsl=mc_symbol.NormalizePrice(price+slip*pip); modseltp=SetOrderTP(symbol,opstype,pos_open); if(price<modseltp) modseltp=mc_symbol.NormalizePrice(price-slip*pip); //-- if(pos_stop==0.0 || pos_take==0.0) { if(!mc_trade.PositionModify(position_symbol,modselsl,modseltp)) { mc_trade.CheckResult(check); Do_Alerts(symbol,"Set SL and TP for "+EnumToString(opstype)+" on "+symbol+" FAILED!!. Return code= "+ (string)mc_trade.ResultRetcode()+". Code description: ["+mc_trade.ResultRetcodeDescription()+"]"); } } } } } //-- return; //--- } //-end SetSLTPOrders //---------//
2. Alle Aufträge schließen: Wenn ein Händler alle Aufträge schließen möchte, reicht ein einziger Klick auf die Schaltfläche „Close All Orders“ (Alle Aufträge schließen), um alle offenen Aufträge zu schließen.
void MCEA::CloseAllOrders(void) //-- function: close all order { //---- ResetLastError(); //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int total=PositionsTotal(); // number of open positions //--- iterate over all open positions for(int i=total-1; i>=0; i--) { //--- if the MagicNumber matches if(mc_position.Magic()==magicEA) { //-- string position_Symbol = PositionGetSymbol(i); // symbol of the position ulong position_ticket = PositionGetTicket(i); // ticket of the the opposite position ENUM_POSITION_TYPE type = mc_position.PositionType(); RefreshTick(position_Symbol); bool closepos = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); //--- } } //--- return; //---- } //-end CloseAllOrders() //---------//
3. Schließen der Positionen im Gewinn: Wenn ein Händler alle bereits profitablen Positionen schließen möchte, genügt ein einziger Klick auf die Schaltfläche „Close All Profitable Orders“, um alle offenen, bereits profitablen Positionen zu schließen.
bool MCEA::ManualCloseAllProfit(void) { //---- ResetLastError(); //-- bool orclose=false; //-- MqlTradeRequest req={}; MqlTradeResult res={}; MqlTradeCheckResult check={}; //-- int ttlorder=PositionsTotal(); // number of open positions //-- for(int x=0; x<arrsymbx; x++) { string symbol=DIRI[x]; orclose=false; //-- for(int i=ttlorder-1; i>=0; i--) { string position_Symbol = PositionGetSymbol(i); ENUM_POSITION_TYPE type = mc_position.PositionType(); if((position_Symbol==symbol) && (mc_position.Magic()==magicEA)) { double pos_profit = mc_position.Profit(); double pos_swap = mc_position.Swap(); double pos_comm = mc_position.Commission(); double cur_profit = NormalizeDouble(pos_profit+pos_swap+pos_comm,2); ulong position_ticket = PositionGetTicket(i); //--- if(type==POSITION_TYPE_BUY && cur_profit>0.02) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } if(type==POSITION_TYPE_SELL && cur_profit>0.02) { RefreshTick(position_Symbol); orclose = mc_trade.PositionClose(position_Symbol,slip); //--- output information about the closure PrintFormat("Close #%I64d %s %s",position_ticket,position_Symbol,EnumToString(type)); } } } } //-- return(orclose); //---- } //-end ManualCloseAllProfit() //---------//
Wenn die Schaltfläche C angeklickt wird, wird eine Schaltfläche mit 30 Symbolnamen oder -paaren angezeigt, und die Händler können auf einen der Paar- oder Symbolnamen klicken. Wenn Sie auf einen der Paarnamen oder ein Symbol klicken, wird das Chartsymbol sofort durch das Symbol ersetzt, auf dessen Namen Sie geklickt haben.
In diesem Fall wird in der Funktion OnChartEvent() der Befehl ChangeChartSymbol() aufgerufen, wenn einer der Symbolnamen angeklickt wird.
//--- if Symbol button is click if(lensparam==lensymbol) { int sx=mc.ValidatePairs(sparam); ChangeChartSymbol(mc.AS30[sx],CCS); mc.PanelExtra=false; } //--
void ChangeChartSymbol(string c_symbol,ENUM_TIMEFRAMES cstf) { //--- //--- unpress the button ObjectSetInteger(0,c_symbol,OBJPROP_STATE,false); ObjectSetInteger(0,c_symbol,OBJPROP_ZORDER,0); ObjectsDeleteAll(0,0,OBJ_BUTTON); ObjectsDeleteAll(0,0,OBJ_LABEL); ObjectsDeleteAll(0,0,OBJ_RECTANGLE_LABEL); //-- ChartSetSymbolPeriod(0,c_symbol,cstf); //-- ChartRedraw(0); //-- return; //--- } //-end ChangeChartSymbol() //---------//
Wenn Sie auf die Schaltfläche R klicken, wird der Multi-Currency Expert Advisor BBOnKeltnerChannel_MCEA aus dem Chart entfernt, sodass der Händler die Experten nicht manuell trennen muss.
if(id==CHARTEVENT_OBJECT_CLICK) { //-- //--- if "R" button is click if(sparam=="R") { Alert("-- "+mc.expname+" -- ",Symbol()," -- Expert Advisor will be Remove from the chart."); ExpertRemove(); //--- unpress the button ObjectSetInteger(0,"R",OBJPROP_STATE,false); ObjectSetInteger(0,"R",OBJPROP_ZORDER,0); if(!ChartSetSymbolPeriod(0,Symbol(),Period())) ChartSetSymbolPeriod(0,Symbol(),Period()); DeletePanelButton(); ChartRedraw(0); } //--- }
Strategietester
Wie Sie wissen, unterstützt das MetaTrader 5-Terminal Strategy Tester und ermöglicht es uns, Strategien zu testen, auf mehreren Symbolen zu handeln oder den automatischen Handel für alle verfügbaren Symbole und auf allen verfügbaren Zeitrahmen zu testen. Daher werden wir auf der MetaTrader 5 Strategy Tester Plattform einen BBOnKeltnerChannel_MCEA als Multi-Currency Expert Advisor testen.
In diesem Test haben wir den BBOnKeltnerChannel_MCEA auf das Paar XAGUSD und den Zeitrahmen H1 gelegt, mit einer nutzerdefinierten Zeitspanne von 2023.09.04 bis 2023.12.02.
Der Test wurde mit zwei verschiedenen Eingabeeigenschaften durchgeführt, und zwar in der Parametergruppe Trade & Order Management.
1. Standard-Eingabeeigenschaften.
2. Nutzerdefinierte Eingabeeigenschaften.
Schlussfolgerung
Die Schlussfolgerung bei der Erstellung eines Multi-Currency Expert Advisors mit Signalen der Bollinger Bänder® und dem Keltner Kanal unter Verwendung von MQL5 ist wie folgt:
- Es stellt sich heraus, dass die Erstellung eines Multiwährungs-Expert Advisors in MQL5 sehr einfach ist und sich nicht wesentlich von der Erstellung eines Expert Advisors für eine einzelne Währung unterscheidet.
- Im Vergleich zur alten Plattform (MetaTrader 4) ist die Verwendung von Indikator-Handles in MQL5 zum Abrufen von Indikatorwerten und Signalen einfacher und bequemer.
- Die Erstellung eines Multi-Currency Expert Advisors erhöht die Effizienz und Effektivität von Händlern, da sie nicht mehr viele Chart-Symbole für den Handel öffnen müssen.
- Die Anwendung der richtigen Handelsstrategie erhöht die Gewinnwahrscheinlichkeit im Vergleich zur Verwendung eines einzelnen Währungsexperten. Das liegt daran, dass Verluste in einem Paar durch Gewinne in anderen Paaren ausgeglichen werden.
- Dieser BBOnKeltnerChannel_MCEA Multi-Currency Expert Advisor ist nur ein Beispiel zum Lernen und zur Ideenfindung. Die Testergebnisse des Strategietesters sind immer noch nicht gut. Daher ist es möglich, bessere und profitablere Ergebnisse zu erzielen, indem man mit verschiedenen Zeitrahmen oder Berechnungen für verschiedene Indikatorperioden experimentiert und testet.
- Die Testergebnisse des Strategy Testers von BBOnKeltnerChannel_MCEA zeigen, dass die Ergebnisse der nutzerdefinierten Eingabeparameter-Eigenschaften besser sind als die der Standard-Eingabeparameter-Eigenschaften.
Wir hoffen, dass dieser Artikel und das MQL5 Multi-Currency Expert Advisor Programm für Händler nützlich sein werden, um zu lernen und Ideen zu entwickeln.
Danke fürs Lesen.
Anmerkung: Wenn Sie eine Idee für die Erstellung eines einfachen Mehr-Währungs-EA auf der Grundlage der eingebauten MQL5-Standardindikatoren haben, schlagen Sie sie bitte in den Kommentaren vor.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/13861
![Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 3): Verhaltensmuster 1](https://c.mql5.com/2/61/Design_Patterns_yPart_39_Behavioral_Patterns_1__LOGO.png)
![Modifizierter Grid-Hedge EA in MQL5 (Teil I): Erstellung eines einfachen Hedge EA](https://c.mql5.com/2/62/Modified_Grid-Hedge_EA_in_MQL5_4Part_Ip_Making_a_Simple_Hedge_EA__LOGO.png)
![Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 4): Triangulärer gleitender Durchschnitt — Indikatorensignale](https://c.mql5.com/2/60/rj-article-image-60x60.png)
![Kombinatorisch symmetrische Kreuzvalidierung in MQL5](https://c.mql5.com/2/60/aticleicon.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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.
Hallo Roberto, ich habe die beiden vom EA benötigten Indikatoren in den Chart geladen, aber der EA meldet immer noch, dass diese beiden Indikatoren nicht geöffnet werden können.
siehe Anhang
Danke
automatische Übersetzung durch einen Moderator
Wie auf dem von Ihnen zur Verfügung gestellten Bild zu sehen ist, ist der von Ihnen verwendete Dateiname des Keltner-Kanal-Indikators falsch.
Bitte beachten Sie den Unterschied in den Dateinamen der Indikatoren.
Sie verwenden andere Indikatoren als die, die von den Expertenberatern benötigt werden.
Wie auf dem von Ihnen zur Verfügung gestellten Bild zu sehen ist, ist der von Ihnen verwendete Dateiname des Keltner-Kanal-Indikators falsch.
Bitte sehen Sie sich den Unterschied zwischen den Dateinamen der Indikatoren an.
Sie verwenden andere Indikatoren als die, die von den Expertenberatern benötigt werden.
hallo roberto
habe den Indikatornamen geändert und der EA läuft gut
aber viele Aufträge für ein Symbol öffnen
hallo roberto
habe den Indikatornamen geändert und EA läuft OK
aber viele Aufträge für ein Symbol öffnen
Vielleicht verstehen Sie nicht oder wissen nicht, dass es im Internet viele Versionen des Keltner Channel Indikators gibt.
Haben Sie meine Erklärung im Artikel gelesen, oder haben Sie einfach das EA-Programm heruntergeladen.
In Teil 2. Signalindikatoren, habe ich erklärt:
"Den Keltner-Kanal-Indikator, der in diesem Artikel für Expert Advisors verwendet wird, habe ich speziell mit einer Methode erstellt, die heute sehr beliebt ist, nämlich dem Exponential Moving Average (EMA) Periode 20 mit oberen und unteren Bändern unter Verwendung des ATR-Indikators Periode 20."
Hoffen Sie also nicht, dass der EA durch die Umbenennung des Indikators korrekt funktionieren wird, denn verschiedene Indikatoren haben unterschiedliche Eingabeparameter und Berechnungen.
Wenn Sie den Keltner-Kanal-Indikator nicht verwenden, den ich speziell für den Bollinger Bands® On Keltner Channel EA erstellt habe, sollten Sie nicht erwarten, dass Sie den Bollinger Bands® On Keltner Channel EA verwenden können.
Hallo Roberto,
Du hast eine tolle Arbeit geleistet! Ich hoffe, ich kann es auf meinem PC zum Laufen bringen.
Ich habe die folgende Meldung: Keltner Channel.ex5 open error und andere folgen.
Natürlich kein Ergebnis auf dem Testgerät.
Ich danke Ihnen für Ihre Hilfe.
Alain
Hallo Roberto,
Du hast eine tolle Arbeit geleistet! Ich hoffe, ich kann es auf meinem PC zum Laufen bringen.
Ich habe die folgende Meldung: Keltner Channel.ex5 open error und weitere werden folgen.
Natürlich, kein Ergebnis auf dem Tester.
Ich danke Ihnen für Ihre Hilfe.
Alain
Hallo,
Mein Expert Advisor heißt BBOnKeltnerChannel_MCEA, NICHT Multi-Currency BB-Keltner (auf dem Bild, das Sie mir per E-Mail geschickt haben, heißt der EA Multi-Currency BB-Keltner)
. Der Name des Indikators, den ich speziell für BBOnKeltnerChannel_MCEA erstellt habe, ist Keltner Channel.mq5 (siehe und laden Sie den Anhang zu diesem Artikel herunter)
Es gibt viele Versionen des Keltner Channel Indikators im Internet.
Achten Sie bitte genau auf den Dateinamen und die Eingabe der Eigenschaften in Indikatoren und Experten, da hier zwischen Groß- und Kleinschreibung unterschieden wird.
Wenn die eingegebenen Eigenschaften nicht übereinstimmen, wird das Programm nicht ausgeführt.
Wenn Sie das EA-Programm ändern und ein Fehler auftritt, sollten Sie nicht mich fragen.
Alle Programme in MQL5 wurden von dem dafür zuständigen Moderator validiert. Wenn sie die Validierung nicht bestehen, ist es unmöglich, dass ein Programm von MQL5 veröffentlicht wird.