
Filtern von Signalen auf Basis statistischer Daten von Preiskorrelationen
Wie alles begann
Die Idee zum Schreiben dieses Beitrags kam mir, nachdem ich Larry Williams' Buch "Die Erfolgsgeheimnisse des Kurzfrist-Tradings" gelesen hatte, in dem der Weltrekordhalter in Sachen Investitionen (über das Jahr 1987 konnte er sein Kapital um 11.000 % steigern) die durch "... Hochschulprofessoren und sonstige Akademiker, die reich an Theorie und arm an Marktkenntnis sind..." geschaffenen Mythen über das Fehlen jeglicher Korrelation zwischen dem Verhalten von Preisen in der Vergangenheit und ihren zukünftigen Trends vollständig widerlegt.
Wenn Sie eine Münze 100 Mal werfen, fällt sie 50 Mal mit dem Kopf und 50 Mal mit der Zahl nach oben. Mit jedem Wurf liegt die Wahrscheinlichkeit für Kopf bei 50 %, genauso wie für Zahl. Die Wahrscheinlichkeit ändert sich nicht von Wurf zu Wurf, weil dieses Spiel auf dem Zufall basiert und über kein Gedächtnis verfügt. Nehmen wir an, dass die Märkte sich ebenso chaotisch verhalten wie eine Münze.
Folglich hat ein Preis mit jedem neuen Balken eine gleich hohe Chance, nach oben oder nach unten zu gehen, und die vorhergehenden Balken beeinflussen den aktuellen nicht auf die geringste Weise. Welch ein Idyll! Erstellen Sie ein Handelssystem, legen Sie Take Profit höher als Stop Loss fest (d. h. legen Sie eine positive mathematische Erwartung fest) und das ist auch der ganze Trick. Einfach atemberaubend. Doch das Problem ist, dass unsere Annahme über das Verhalten des Marktes nicht ganz richtig ist. Genauer gesagt, ist sie vollkommen absurd! Und ich werde es beweisen.
Erstellen wir eine Vorlage für einen Expert Advisor mithilfe des MQL5 Wizard und bringen ihn durch einfache alphanumerische Eingriffe in einen Zustand, der geeignet ist, die Aufgabe auszuführen. Wir programmieren einen Expert Advisor, der einen Kauf nach einem, zwei und drei Balken, die mit Preissenkung schließen, simuliert. Simulieren bedeutet in diesem Fall, dass das Programm sich einfach an die Parameter analysierter Balken erinnern wird. Das Senden von Ordern (die gebräuchlichere Variante) wird in diesem Fall nicht funktionieren, da die Spreads und Swaps die Zuverlässigkeit der erhaltenen Informationen in Frage stellen können.
Und das ist der Code:
//+------------------------------------------------------------------+ //| explorer.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //---Variables--- double profit_percent,open_cur,close_cur; double profit_trades=0,loss_trades=0,day_cur,hour_cur,min_cur,count; double open[],close[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { /* Calculate percent of closures with increase from the total number */ profit_percent=NormalizeDouble(profit_trades*100/(profit_trades+loss_trades),2); Print("Percent of closures with increase ",profit_percent,"%"); // Enter data to the Journal } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //---find out the time--- MqlDateTime time; // Create a structure to store time TimeToStruct(TimeCurrent(),time); // Structuring the data day_cur=time.day_of_week; // Receive the value of the current day hour_cur=time.hour; // Receive the current hour min_cur=time.min; // Receive the current minute //---Find out the prices--- CopyOpen(NULL,0,0,4,open);ArraySetAsSeries(open,true); CopyClose(NULL,0,0,4,close);ArraySetAsSeries(close,true); if(close[1]<open[1]/*&&close[2]<open[2]&&close[3]<open[3]*/ && count==0) // If it closed with a loss { open_cur=open[0]; // Remember open price of the current bar count=1; } if(open_cur!=open[0] && count==1) // The current bar has closed { close_cur=close[1]; // Remember the close price of the formed bar count=0; if(close_cur>=open_cur)profit_trades+=1; // If the close price is higher than open, else loss_trades+=1; // +1 to closures with profit, otherwise +1 to closures with loss } } //+------------------------------------------------------------------+
Der Test wird auf EUR/USD im Intervall von 1. Januar 2000 bis 31. Dezember 2010 ausgeführt:
Abbildung 1. Anteil von Schließungen mit Erhöhung
(Die erste Säule zeigt die Daten für den gesamten Zeitraum, die zweite dritte und vierte die Daten nach einfacher, zweifacher und dreifacher Schließung)
Das ist es! Die vorherigen Balken wirken sich deutlich auf den aktuellen aus, weil der Preis immer versucht, Verluste zurückzugewinnen.
Ein weiterer Schritt nach vorne
Großartig! Da wir nun überzeugt sind, dass das Verhalten von Preisen nicht zufällig ist, sollten wir diese erstaunliche Tatsache so bald wie möglich einsetzen. Natürlich reicht das nicht für ein unabhängiges Handelssystem, doch es wird ein tolles Werkzeug sein, das Sie von anstrengenden und oftmals falschen Signalen befreien kann. Setzen wir es um!
Was wir brauchen:
- Ein eigenständiges Handelssystem, das mindestens für das letzte Jahr positive Ergebnisse aufweist.
- Irgendein lustiges Beispiel, das das Vorhandensein von Korrelationen im Verhalten von Preisen bestätigt.
Ich habe in L. Williams' Buch etliche nützliche Ideen gefunden. Eine davon werde ich mit Ihnen teilen.
Die Strategie des Handelstages der Woche (Trade Day Of Week, TDW). Diese Idee ermöglicht es uns, zu sehen, was passieren wird, wenn wir an einigen Tagen der Woche nur kaufen und an den anderen nur kurze Positionen öffnen. Schließlich können wir annehmen, dass der Preis innerhalb des einen Tages in mehr Fällen wächst als innerhalb des anderen. Woran liegt das? Der geopolitischen Situation? Makroökonomischen Statistiken? Oder, wie es in A. Elders Buch steht, weil Montage und Dienstage die Tage der Laien sind und Donnerstage und Freitage die Tage der Experten? Versuchen wir, dies nachzuvollziehen.
Wir werden erst an jedem Wochentag kaufen und dann nur verkaufen. Am Ende der Untersuchung vergleichen wir die besten Ergebnisse und erstellen daraus einen Filter für unser Handelssystem. Dazu möchte ich noch ein paar Worte sagen. Es ist ein absoluter Klassiker!
Das System basiert auf zwei MAs und MACDake. Signale:
- BUY, wenn der schnelle gleitende Mittelwert den langsamen von unten nach oben überkreuzt und das MACD-Histogramm unter der Nulllinie liegt.
- SELL, wenn der schnelle gleitende Mittelwert den langsamen von oben nach unten überkreuzt und MACD über Null liegt.
Verlassen Sie die Position mithilfe eines Trailing Stops von einem Punkt. Das Los ist fix: 0,1.
Aus Bequemlichkeitsgründen habe ich die Klasse des Expert Advisors in einer separaten Header-Datei platziert:
//+------------------------------------------------------------------+ //| moving.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Класс my_expert | //+------------------------------------------------------------------+ class my_expert { // Creating a class // Closed class members private: int ma_red_per,ma_yel_per; // Periods of MAs int ma_red_han,ma_yel_han,macd_han; // Handles double sl,ts; // Stop orders double lots; // Lot double MA_RED[],MA_YEL[],MACD[]; // Arrays for the indicator values MqlTradeRequest request; // Structure of a trade request MqlTradeResult result; // Structure of a server response // Open class members public: void ma_expert(); // Constructor void get_lot(double lot){lots=lot;} // Receiving a lot void get_periods(int red,int yel){ma_red_per=red;ma_yel_per=yel;} // Receiving the periods of MAs void get_stops(double SL,double TS){sl=SL;ts=TS;} // Receiving the values of stops void init(); // Receiving the indicator values bool check_for_buy(); // Checking for buy bool check_for_sell(); // Checking for sell void open_buy(); // Open buy void open_sell(); // Open sell void position_modify(); // Position modification }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* Function definition */ //---Constructor--- void my_expert::ma_expert(void) { //--- Reset the values of variables ZeroMemory(ma_red_han); ZeroMemory(ma_yel_han); ZeroMemory(macd_han); } //---The function for receiving the indicator values--- void my_expert::init(void) { ma_red_han=iMA(_Symbol,_Period,ma_red_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the slow MA ma_yel_han=iMA(_Symbol,_Period,ma_yel_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the fast MA macd_han=iMACD(_Symbol,_Period,12,26,9,PRICE_CLOSE); // Handle of MACDaka //---Copy data into arrays and set indexing like in a time-series--- CopyBuffer(ma_red_han,0,0,4,MA_RED); CopyBuffer(ma_yel_han,0,0,4,MA_YEL); CopyBuffer(macd_han,0,0,2,MACD); ArraySetAsSeries(MA_RED,true); ArraySetAsSeries(MA_YEL,true); ArraySetAsSeries(MACD,true); } //---Function to check conditions to open buy--- bool my_expert::check_for_buy(void) { init(); //Receive values of indicator buffers /* If the fast MA has crossed the slow MA from bottom up between 2nd and 3rd bars, and there was no crossing back. MACD-hist is below zero */ if(MA_RED[3]>MA_YEL[3] && MA_RED[1]<MA_YEL[1] && MA_RED[0]<MA_YEL[0] && MACD[1]<0) { return(true); } return(false); } //----Function to check conditions to open sell--- bool my_expert::check_for_sell(void) { init(); //Receive values of indicator buffers /* If the fast MA has crossed the slow MA from up downwards between 2nd and 3rd bars, and there was no crossing back. MACD-hist is above zero */ if(MA_RED[3]<MA_YEL[3] && MA_RED[1]>MA_YEL[1] && MA_RED[0]>MA_YEL[0] && MACD[1]>0) { return(true); } return(false); } //---Open buy--- /* Form a standard trade request to buy */ void my_expert::open_buy(void) { request.action=TRADE_ACTION_DEAL; request.symbol=_Symbol; request.volume=lots; request.price=SymbolInfoDouble(Symbol(),SYMBOL_ASK); request.sl=request.price-sl*_Point; request.tp=0; request.deviation=10; request.type=ORDER_TYPE_BUY; request.type_filling=ORDER_FILLING_FOK; OrderSend(request,result); return; } //---Open sell--- /* Form a standard trade request to sell */ void my_expert::open_sell(void) { request.action=TRADE_ACTION_DEAL; request.symbol=_Symbol; request.volume=lots; request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID); request.sl=request.price+sl*_Point; request.tp=0; request.deviation=10; request.type=ORDER_TYPE_SELL; request.type_filling=ORDER_FILLING_FOK; OrderSend(request,result); return; } //---Position modification--- void my_expert::position_modify(void) { if(PositionGetSymbol(0)==_Symbol) { //If a position is for our symbol request.action=TRADE_ACTION_SLTP; request.symbol=_Symbol; request.deviation=10; //---If a buy position--- if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { /* if distance from price to stop loss is more than trailing stop and the new stop loss is not less than the previous one */ if(SymbolInfoDouble(Symbol(),SYMBOL_BID)-PositionGetDouble(POSITION_SL)>_Point*ts) { if(PositionGetDouble(POSITION_SL)<SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts) { request.sl=SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts; request.tp=PositionGetDouble(POSITION_TP); OrderSend(request,result); } } } //---If it is a sell position--- else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { /* if distance from price to stop loss is more than the trailing stop value and the new stop loss is not above the previous one. Or the stop loss from the moment of opening is equal to zero */ if((PositionGetDouble(POSITION_SL)-SymbolInfoDouble(Symbol(),SYMBOL_ASK))>(_Point*ts)) { if((PositionGetDouble(POSITION_SL)>(SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts)) || (PositionGetDouble(POSITION_SL)==0)) { request.sl=SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts; request.tp=PositionGetDouble(POSITION_TP); OrderSend(request,result); } } } } } //+------------------------------------------------------------------
Ich verneige mich vor dem Verfasser des Beitrags "Einen Expert Advisor mit Hilfe des MQL5 Objekt-orientierten Programmieransatzes schreiben". Was würde ich nur ohne diesen Beitrag tun! Ich empfehle diesen Beitrag jedem, der in dieser bösartigen, doch äußerst funktionalen objektorientierten Programmierung nicht allzu versiert ist.
Fügen Sie die Datei mit der Klasse zum Hauptcode des Expert Advisors hinzu, erstellen Sie ein Objekt und initialisieren Sie die Funktionen:
//+------------------------------------------------------------------+ //| Moving.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //---Include a file with the class--- #include <moving.mqh> //---External Variables--- input int MA_RED_PERIOD=7; // The period of a slow MA input int MA_YEL_PERIOD=2; // The period of a fast MA input int STOP_LOSS=800; // Stop loss input int TRAL_STOP=800; // Trailing stop input double LOTS=0.1; // Lot //---Create an object--- my_expert expert; //---Initialize the MqlDataTime structure--- MqlDateTime time; int day_of_week; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //---Initialize the EA expert.get_periods(MA_RED_PERIOD,MA_YEL_PERIOD); // Set the MA periods expert.get_lot(LOTS); // Set the lot expert.get_stops(STOP_LOSS,TRAL_STOP); // Set stop orders return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { TimeToStruct(TimeCurrent(),time); day_of_week=time.day_of_week; if(PositionsTotal()<1) { if(day_of_week==5 && expert.check_for_buy()==true){expert.open_buy();} else if(day_of_week==1 && expert.check_for_sell()==true){expert.open_sell();} } else expert.position_modify(); } //+------------------------------------------------------------------+
Fertig! Ich möchte Sie auf einige Besonderheiten aufmerksam machen. Um die Wochentage auf Softwareebene zu identifizieren, nutze ich die Struktur MqlDateTime. Als Erstes wandeln wir die aktuelle Serverzeit in ein strukturiertes Format um. Wir erhalten einen Index des aktuellen Tages (1-Montag, ..., 5-Freitag) und vergleichen ihn mit dem von uns festgelegten Wert.
Probieren wir es aus! Um Sie nicht mit mühseligen Recherchen und unnötigen Zahlen zu belasten, führe ich alle Ergebnisse in einer Tabelle auf.
Hier ist sie:
Tabelle 1. Zusammenfassung der Käufe an jedem Wochentag
Tabelle 2. Zusammenfassung der Verkäufe an jedem Wochentag
Die besten Ergebnisse sind grün markiert, die schlechtesten orange.
Ich möchte anmerken, dass das System nach den oben beschriebenen Aktionen den Gewinn in Kombination mit einem geringen relativen Wertverlust, einem guten Anteil rentabler Abschlüsse (hier: je weniger Abschlüsse, desto besser) und einem vergleichsweise hohen Gewinn pro Abschluss gewährleisten muss.
Offensichtlich ist das effektivste System eins, das freitags kauft und montags verkauft. Kombinieren Sie diese beiden Bedingungen:
if(PositionsTotal()<1){ if(day_of_week==5&&expert.check_for_buy()==true){expert.open_buy();} else if(day_of_week==1&&expert.check_for_sell()==true){expert.open_sell();}} else expert.position_modify();
Nun öffnet der Expert Advisor Positionen in beiden Richtungen, allerdings nur an strikt festgelegten Tagen. Zur Erklärung zeichne ich die erhaltenen Diagramme mit und ohne Filter:
Abbildung 2. Testergebnisse des EAs ohne Filter (EURUSD, H1, 01.01.2010-31.12.2010,)
Abbildung 3. Testergebnisse des EAs mit Filter (EURUSD, H1, 01.01.2010-31.12.2010,)
Wie gefällt Ihnen das Ergebnis? Mithilfe des Filters ist das Handelssystem stabiler geworden. Vor den Modifikationen erhöhte der Expert Advisor die Bilanz hauptsächlich während der ersten Hälfte des Testzeitraums. Nach dem "Upgrade" erhöht er sie während des gesamten Zeitraums.
Wir vergleichen die Berichte:
Tabelle 3. Testergebnisse vor und nach dem Einsatz des Filters
Die einzige Enttäuschung, die sich nicht ignorieren lässt, ist die Abnahme des Nettogewinns um fast 1000 USD (26 %). Doch wir senken die Anzahl der Abschlüsse um knapp das 3,5-Fache, d. h. wir vermindern erstens das Risiko eines negativen Abschlusses und zweitens die Ausgaben für den Spread (218*2-62*2=312 USD, und das gilt nur für EUR/USD). Der Gewinnanteil steigt auf 57 %, was schon beträchtlich ist. Und der Gewinn pro Abschluss steigt um 14 % auf 113 USD. Wie L. Williams sagen würde: "Das ist eine Summe, die es wert ist, gehandelt zu werden!"
Fazit
Das Verhalten von Preisen ist nicht zufällig – das ist Tatsache. Diese Tatsache kann und soll genutzt werden. Ich habe nur ein Beispiel aufgeführt, das nur einen winzigen Anteil der unzähligen Varianten und Techniken darstellt, die die Performance Ihres Handelssystems steigern können. Doch hinter all dieser Vielfalt verbirgt sich ein Mangel. Nicht jeder Filter lässt sich integrieren, also müssen Sie sorgfältig auswählen und alle möglichen Szenarien durchdenken.
Vergessen Sie nicht, dass ein Filter, so perfekt er auch sein mag, auch rentable Abschlüsse – also Ihren Gewinn – herausfiltert... Viel Erfolg!
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/269
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.





- 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.
Wenn also ein neuer Balken erscheint, hat der Preis die gleiche Chance, nach oben oder unten zu gehen, und die vorherigen Balken beeinflussen den aktuellen nicht im Geringsten. Idylle! Erstellen Sie ein Handelssystem, setzen Sie den Take Profit höher als den Stop Loss (d. h. bringen Sie die Erwartungsmatrix in den positiven Bereich), und schon sind Sie fertig. Es ist einfach atemberaubend.
Zu dem Zitat: Ich habe überhaupt nichts verstanden!!! Was war das?!? Poesie? :) Und wo ist die Mathe?
Und zweitens: Es ist egal, wie wahrscheinlich die Schließungsrichtung ist, wenn wahrscheinliche kleine Gewinne weniger wahrscheinliche fette Verlierer haben. Deine Statistik hängt eher von den Details des Risikomanagements ab, die darin ausgelassen werden ;)
Ihr Artikel enthält zu wenige Beispiele, um sie zu analysieren. Man könnte sagen, dass das von Ihnen genannte Beispiel zur Geschichte passt.
Wenn also ein neuer Balken erscheint, hat der Preis die gleiche Chance, nach oben oder unten zu gehen, und die vorherigen Balken beeinflussen den aktuellen nicht im Geringsten. Erstellen Sie ein Handelssystem, setzen Sie den Take Profit höher als den Stop Loss (d. h. bringen Sie die Erwartungsmatrix in den positiven Bereich), und schon sind Sie fertig. Es ist atemberaubend.
Autor, haben Sie schon einmal darüber nachgedacht, dass ein Elch sogar in jedem Handel auslösen kann, obwohl die Anzahl der Aufwärtsschließungen gleich der Anzahl der Abwärtsschließungen sein wird. Selbst wenn also bekannt ist, dass es genauso viele Aufwärts- wie Abwärtsabschlüsse geben wird, ist die von Ihnen beschriebene Strategie nicht geeignet.
Hallo Тарачков,
vielen Dank für deinen tollen Artikel,
korrigieren Sie mich, wenn ich falsch liege....
Sie haben Daten aus der Vergangenheit extrahiert und den gefilterten Experten zur gleichen Zeit laufen lassen (beide im Jahr 2010)?
Wenn dies der Fall ist, denke ich bei allem Respekt, dass diese Filterung sinnlos ist. Diese Art der Filterung beweist nichts, denn sie macht die Ergebnisse offensichtlich besser....
Ich bin kein Fan von völlig zufälligen Marktbewegungen, aber ich denke, Sie sollten einen Zeitraum (z. B. 2005) verwendet haben, um die Daten für die Filterung zu extrahieren, und Ihren gefilterten Experten für das nächste Jahr (2006) ausführen und bis zum letzten Jahr fortfahren, um ihn dann mit dem ursprünglichen Experten zu vergleichen, um zu sehen, ob es irgendwelche Korrelationen zwischen dem vergangenen Preisverhalten und seinen zukünftigen Trends gibt.
Es ist wirklich ein interessanter Artikel, sehr gut.
Ich danke Ihnen für diesen Artikel.
Vielen Dank noch einmal !!!