Filtraggio dei segnali basati su dati statistici di correlazione dei prezzi
Com’è iniziato
L'idea che mi ha portato a scrivere questo articolo è apparsa dopo aver letto il libro di Larry Williams "Long-Term Secrets to Short-Term Trading", in cui il detentore del record mondiale in investimenti (durante il 1987 ha aumentato il suo capitale dell’ 11.000%) sta completamente dissipando i miti con "... professori universitari e altri accademici che sono ricchi di teoria e poveri di conoscenza del mercato ..." sull'assenza di qualsiasi correlazione tra il comportamento passato dei prezzi e le tendenze future.
Se lanci una moneta 100 volte, 50 volte cadrà sulle teste e 50 volte sulle code. Ad ogni lancio successivo, la probabilità delle teste è del 50%, la stessa delle code. La probabilità non cambia da lancio a lancio, perché questo gioco è casuale e non ha memoria. Supponiamo che i mercati si comportino come una moneta, in modo caotico.
Di conseguenza, quando appare una nuova barra, un prezzo ha pari opportunità di salire o scendere e le barre precedenti non influenzano nemmeno minimamente quella attuale. Idillio! Crea un sistema di trading, imposta il take profit più grande dello stop loss (cioè imposta l’aspettativa matematica sulla zona positiva) e il gioco è fatto. Semplicemente mozzafiato. Tuttavia, il problema è che la nostra ipotesi sul comportamento del mercato non è del tutto vera. Francamente parlando, è assurda! E lo dimostrerò.
Creiamo un modello di Expert Advisor utilizzando il Wizard MQL5 e, con semplici interventi alfanumerici, presentiamolo in una condizione adatta all'espletamento dell'attività. Codificheremo un Expert Advisor per simulare l'acquisto che segue una, due e tre barre chiuse. Simulazione significa che il programma ricorderà semplicemente i parametri delle barre analizzate. L'invio di ordini (un modo più usuale) in questo caso non funzionerà, perché gli spread e gli swap sono in grado di mettere in discussione l'affidabilità delle informazioni ricevute.
Ecco il codice:
//+------------------------------------------------------------------+ //| 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 } } //+------------------------------------------------------------------+
Il test sarà effettuato su EUR/USD, nell'intervallo dal 1 gennaio 2000 al 31 dicembre 2010:
Figura 1. La percentuale di chiusure con l'aumento
(La prima colonna mostra i dati per l'intero periodo, la seconda, la terza e la quarta dopo una chiusura singola, doppia e tripla)
Questo è ciò di cui parlavo! Le barre precedenti hanno un impatto abbastanza significativo su quella attuale perché il prezzo cerca sempre di riconquistare le perdite.
Un altro passo avanti
Benissimo! Una volta che siamo sicuri che il comportamento dei prezzi non sia casuale, dobbiamo usare questo fatto sorprendente il prima possibile. Certo, non è sufficiente per un sistema di trading indipendente, ma sarà un ottimo strumento in grado di liberarti dai segnali noiosi e spesso errati. Implementiamolo!
Quindi questo è ciò di cui abbiamo bisogno:
- Un sistema di self-trading che mostra risultati positivi almeno per l'ultimo anno.
- Qualche esempio divertente che conferma la presenza di correlazioni nel comportamento dei prezzi.
Ho trovato molte idee utili nel libro di L. Williams. Ne condividerò uno con voi.
La strategia TDW (Trade Day Of Week). Ci permetterà di vedere cosa accadrà se in alcuni giorni della settimana compreremo solamente e negli altri apriremo solo posizioni short. Dopotutto, possiamo supporre che il prezzo in un giorno cresca in una percentuale maggiore di casi rispetto a un altro. Qual è la ragione? La situazione geopolitica, le statistiche macroeconomiche o, come scritto nel libro di A. Elder, il lunedì e il martedì sono i giorni dei laici, mentre il giovedì e il venerdì rappresentano il momento dei professionisti? Proviamo a capire.
In primo luogo, venderemo solo per ogni giorno della settimana e poi venderemo e basta. Alla fine dello studio abbineremo i risultati migliori e questo sarà un filtro per il nostro sistema di trading. A proposito, ho un paio di parole a riguardo. È un classico puro!
Il sistema si basa su due MA e MACDake. Segnali:
- Se la media mobile veloce attraversa quella lenta dal basso verso l'alto e l'istogramma MACD è al di sotto della linea dello zero, allora ACQUISTA.
- Se la media mobile veloce attraversa quella lenta da capovolto e MACD è sopra lo zero, allora VENDI.
Esci da una posizione utilizzando un trailing stop da un punto. Il lotto è fisso - 0,1.
Per comodità, ho inserito la classe Expert Advisor in un file di intestazione separato:
//+------------------------------------------------------------------+ //| 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); } } } } } //+------------------------------------------------------------------
I miei umili omaggi all'autore dell'articolo "Writing an Expert Advisor using the MQL5 Object-Oriented Approach". Cosa farei senza di lui! Consiglio di leggere questo articolo a chiunque non sia molto esperto in questa programmazione orientata agli oggetti malvagia,ma estremamente funzionale.
Aggiungi il file con la classe al codice principale di Expert Advisor? Crea un oggetto e inizializza le funzioni:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Fatto! Vorrei far notare alcune caratteristiche speciali. Per identificare i giorni della settimana a livello di software, ho usato la struttura MqlDateTime. Innanzitutto, trasformiamo l'ora corrente del server in un formato strutturato. Otteniamo un indice del giorno corrente (1-lunedì, ..., 5-venerdì) e lo confrontiamo con il valore che abbiamo impostato.
Prova! Per non appesantirti con ricerche noiose e cifre extra, sto riportando tutti i risultati nella tabella.
Eccola:
Tabella 1. Riepilogo degli acquisti per tutti i giorni della settimana
Tabella 2. Riepilogo delle vendite per tutti i giorni della settimana
I migliori risultati sono evidenziati in verde, i peggiori in arancione.
Faccio una premessa, ovvero che dopo le azioni descritte sopra il sistema deve garantire il profitto in combinazione con un drawdown basso relativo, una buona percentuale di operazioni vincenti (qui, meno operazioni esegui e meglio è) e un profitto relativamente alto per operazione.
Ovviamente, il sistema più efficace consiste nell'acquistare il venerdì e vendere il lunedì. Combina entrambe queste condizioni:
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();
Ora l'Expert Advisor apre posizioni in entrambe le direzioni, ma in giorni rigorosamente definiti. Per chiarezza, disegnerò i diagrammi ottenuti senza e con il filtro:
Figura 2. I risultati dei test EA senza l'utilizzo di un filtro (EURUSD, H1, 01.01.2010-31.12.2010,)
Figura 3. I risultati dei test EA utilizzando il filtro (EURUSD, H1, 01.01.2010-31.12.2010)
Ti piace il risultato? Utilizzando il filtro, il sistema di trading è diventato più stabile. Prima delle modifiche, l'Expert Advisor aumentava principalmente il saldo nella prima metà del periodo di test, ma dopo l'"aggiornamento" è in aumento per tutto il periodo.
Confrontiamo i rapporti:
Tabella 3. Risultati dei test prima e dopo l'utilizzo del filtro
L'unico fattore angosciante, che non può essere ignorato, è il calo dell'utile netto di quasi 1000 USD (26%). Ma stiamo riducendo il numero di operazioni quasi in 3,5 volte, quindi stiamo riducendo significativamente, in primo luogo, il potenziale di fare un trading negativo e, in secondo luogo, le spese per lo spread (218 * 2-62 * 2 = 312 USD ed è solo per EUR / USD). La percentuale di vincita è aumentata al 57%, che è già significativa. Mentre il profitto per operazione aumenta del 14% a 113 USD. Come direbbe L. Williams: "Questo è l'importo con cui vale la pena fare trading!"
Conclusione
I prezzi non si comportano in modo casuale: è un dato di fatto. Questo fatto può e deve essere usato. Ho fatto solo un esempio che è una piccola frazione delle innumerevoli variazioni e tecniche che possono migliorare le prestazioni del tuo sistema di trading. Tuttavia, questa diversità nasconde un vizio. Non tutti i filtri possono essere integrati, quindi deve essere scelto con attenzione, pensando a tutti i possibili scenari.
Non dimenticare che non importa quanto sia perfetto il filtro, eliminerà anche le operazioni redditizie, cioè il tuo profitto ... Buona Fortuna!
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/269
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso