English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Test rapidi delle idee di trading sul grafico

Test rapidi delle idee di trading sul grafico

MetaTrader 5Sistemi di trading | 11 gennaio 2022, 16:15
104 0
Vladimir Kustikov
Vladimir Kustikov

Introduzione

Il sesto Campionato di Trading Automatizzato è finalmente iniziato. Tutta l'eccitazione iniziale è finita e finalmente possiamo rilassarci un po 'ed esaminare i robot di trading presentati. Ho deciso di fare una piccola ricerca per scoprire le caratteristiche più evidenti dei moderni robot di trading e definire cosa possiamo aspettarci dalla loro attività di trading.

Ciò si è rivelato abbastanza difficile. Pertanto, i miei calcoli non si possono definire perfettamente accurati o completi, poiché le mie uniche fonti sono state le descrizioni di Expert Advisor e qualche commento degli sviluppatori. Tuttavia, possiamo ancora trarre alcune conclusioni e di seguito sono riportati i risultati dei miei calcoli: 451 Expert Advisor partecipano al campionato, ma solo 316 di loro contengono alcune descrizioni significative. Gli sviluppatori dei rimanenti hanno riempito le loro descrizioni con saluti ai loro amici e familiari, messaggi alle civiltà extraterrestri o lodi rivolte a loro stessi.

Le strategie più popolari su ATC 2012:

  • fare trading utilizzando varie costruzioni grafiche (importanti livelli di prezzo, livelli di supporto-resistenza, canali) – 55;
  • analisi del movimento dei prezzi (per vari intervalli di tempo) – 33;
  • sistemi di tracciamento delle tendenze (immagino che queste parolone nascondano una combinazione sovraottimizzata di medie mobili ma potrei sbagliarmi) :) ) – 31;
  • modelli statistici dei prezzi – 10:
  • arbitrato, analisi della correlazione dei simboli – 8;
  • analisi della volatilità – 8;
  • Rete neurale -7;
  • analisi delle candele giapponesi – 5;
  • media – 5;
  • pacchetti strategici – 5;
  • tempo della sessione di trading – 4;
  • generatore di numeri casuali – 4;
  • trading delle notizie – 3,
  • Onde di Elliott - 2

Le strategie di indicatore sono tradizionalmente le più popolari, ovviamente. È difficile definire il ruolo di ogni particolare indicatore in un particolare Expert Advisor ma è possibile stimare il numero assoluto del loro utilizzo:

  • Media Mobile - 75
  • MACD – 54;
  • Oscillatore Stocastico - 25
  • RSI – 23;
  • Bande di Bollinger - 19;
  • Frattali - 8
  • CCI, ATR – 7 indicatori ciascuno;
  • Zigzag, SAR parabolico – 6 indicatori ciascuno;
  • ADX – 5;
  • Impulso - 4;
  • indicatori personalizzati (che intrigante :)) – 4;
  • Ichimoku, AO – 3 indicatori ciascuno;
  • ROC, WPR, StdDev, Volumi – 2 indicatori ciascuno.

I dati suggeriscono le seguenti conclusioni: la maggior parte dei partecipanti utilizza strategie commerciali con indicatori. Forse, mi sono perso qualcosa durante la raccolta dei dati, e vedremo l'avvento di alcune personalità eccezionali nel campo del trading automatizzato ma sembra improbabile per ora. Penso che il problema principale sia che ai nuovi arrivati attratti dal mercato nella maggior parte vengono date regole invece di conoscenze.

Ad esempio, queste sono le regole per l'utilizzo di MACD, e questi sono i segnali: ora ottimizza i parametri e guadagna. Che ne dici di usare un po' il cervello? Sciocchezza! Gli standard sono già stati sviluppati! Perché reinventare la ruota? Tuttavia, spesso dimentichiamo che gli indicatori che sono così popolari ora sono stati inventati anche da trader proprio come me e te. Avevano anche i loro standard e le loro autorità. Forse, un nuovo indicatore che porta il tuo nome diventerà uno standard tra una decina d'anni.

Vorrei condividere il mio metodo di ricerca di idee di trading, così come il metodo che uso per testare rapidamente queste idee.


Descrizione del metodo

Tutta l'analisi tecnica si basa su un semplice assioma: i prezzi considerano tutto. Ma c'è un problema: questa affermazione manca di dinamiche. Guardiamo il grafico e vediamo un'immagine statica: il prezzo ha effettivamente considerato tutto. Tuttavia, vogliamo sapere quale sarà il prezzo considerato in un certo periodo di tempo in futuro e dove andrà, in modo da poter realizzare profitti. Gli indicatori derivati dal prezzo sono stati progettati esattamente per prevedere possibili movimenti futuri.

Come sappiamo dalla fisica, la derivata del primo ordine della grandezza è la velocità. Pertanto, gli indicatori calcolano l'attuale velocità di variazione dei prezzi. Sappiamo anche che le grandezze significative hanno inerzia che impedisce alla velocità di variazioni brusche del suo valore senza l'intervento di considerevoli forze esterne. È così che ci avviciniamo gradualmente al concetto di tendenza - lo stato del prezzo quando il suo derivato di primo ordine (velocità) mantiene il suo valore durante il periodo di tempo in cui le forze esterne (notizie, politiche delle banche centrali, ecc.) non influenzano il mercato.

Ma torniamo da dove siamo partiti: i prezzi considerano tutto. Per sviluppare nuove idee, dovremmo esaminare il comportamento del prezzo e dei suoi derivati allo stesso intervallo di tempo. Solo un attento esame dei grafici dei prezzi aumenterà il tuo trading dalla fede cieca fino al livello di comprensione genuina.

Ciò potrebbe non portare a cambiamenti immediati nei risultati di trading, ma la capacità di rispondere a numerose domande sul perché, prima o poi giocherà un ruolo positivo. Inoltre, l'analisi visiva di grafici e indicatori ti consentirà di trovare alcune correlazioni nuove di zecca tra prezzi e indicatori non previsti dai loro sviluppatori.

Supponiamo di aver trovato una nuova correlazione che apparentemente funziona a tuo favore. Che novità ci sono? Il modo più semplice è scrivere un Expert Advisor e testarlo su dati storici assicurandosi che la tua ipotesi sia corretta. In caso contrario, dobbiamo scegliere un modo comune di ottimizzare i parametri. La cosa peggiore è che non siamo stati in grado di rispondere alla domanda sul perché. Perché il nostro Expert Advisor si è rivelato redditizio e in perdita? Perché c'è stato un drawdown così enorme? Senza le risposte, non sarai in grado di implementare la tua idea in modo efficiente.

Eseguo le seguenti azioni per visualizzare i risultati di una correlazione ottenuta direttamente sul grafico:

  1. Creo o cambio l'indicatore necessario, in modo che generi un segnale: -1 per vendere e 1 per comprare.
  2. Collego l'indicatore di bilanciamento che visualizza i punti di entrata e di uscita al grafico. L'indicatore mostra anche le variazioni del saldo e dell'equità (in punti) durante l'elaborazione del segnale.
  3. Analizzo in quali casi e circostanze le mie ipotesi sono corrette.

Il metodo ha alcuni vantaggi.

  • Innanzitutto, l'indicatore di bilanciamento viene interamente calcolato utilizzando il metodo OnCalculate che fornisce la massima velocità di calcolo e la disponibilità automatica dei dati storici negli array di calcolo di input.
  • In secondo luogo, l'aggiunta del segnale all'indicatore esistente è un passaggio intermedio tra la creazione di un Expert Advisor tramite Wizard e lo sviluppo da soli.
  • In terzo luogo, un'idea e un risultato finale possono essere visti su un singolo grafico. Naturalmente, il metodo ha alcune limitazioni: un segnale è legato al prezzo di chiusura della barra, il saldo è calcolato per il lotto costante, non ci sono opzioni per il trading utilizzando ordini in sospeso. Tuttavia, tutte queste limitazioni possono essere facilmente risolte / migliorate.


Implementazione

Sviluppiamo un semplice indicatore di segnale per capire come funziona e valutare la praticità del metodo. Ho sentito parlare a lungo dei modelli di candele giapponesi. Quindi, perché non controllare il loro lavoro in pratica? Ho selezionato i modelli inversi "martello" e "stella cadente" come segnali di acquisto e vendita, rispettivamente. Le immagini qui sotto mostrano il loro aspetto schematico:

Figura 1. Modelli di candele giapponesi "Hammer" e "shooting star"

Figura 1. Modelli di candele giapponesi "Hammer" e "shooting star"

Ora, definiamo le regole di ingresso nel mercato quando appare il modello "martello".

  1. Il valore più basso della candela dovrebbe essere inferiore a quello delle cinque candele precedenti;
  2. Il corpo della candela non deve superare il 50% della sua altezza totale;
  3. L'ombra superiore della candela non deve superare lo 0% della sua altezza totale;
  4. L'altezza della candela non deve essere inferiore al 100% dell'altezza media delle cinque candele prima di essa;
  5. Il prezzo di chiusura del modello dovrebbe essere inferiore alla media mobile a 10periodi.

Se queste condizioni sono soddisfatte, dovremmo aprire una posizione lunga. Le regole sono le stesse per il modello "stella cadente". L'unica differenza è che dovremmo aprire una posizione corta:

  1. Il valore più alto della candela dovrebbe essere superiore a quello delle cinque candele precedenti;
  2. Il corpo della candela non deve superare il 50% della sua altezza totale;
  3. L'ombra inferiore della candela non deve superare lo 0% della sua altezza totale;
  4. L'altezza della candela non deve essere inferiore al 100% dell'altezza media delle cinque candele prima di essa;
  5. Il prezzo di chiusura del modello dovrebbe essere superiore alla media mobile a 10 periodi.

Ho usato lo stile grassetto per i parametri che ho usato basati su disegni che possono essere ottimizzati in futuro (se il modello mostra risultati accettabili). Le limitazioni che voglio implementare ci permettono di cancellare i modelli da quelli che hanno un aspetto inappropriato (pp. 1-3), così come da quelli consapevolmente deboli che non possono essere accettati come segnali.

Inoltre, dovremmo determinare i momenti di uscita. Poiché i modelli menzionati appaiono come segnali di inversione di tendenza, la tendenza esiste nel momento in cui appare la candela appropriata. Pertanto, sarà presente anche la media mobile che insegue il prezzo. Il segnale di uscita è formato dall'incrocio del prezzo e della sua media mobile a 10periodi.

Ora è il momento di fare un po 'di programmazione. Sviluppiamo un nuovo indicatore personalizzato in MQL5 Wizard, denominalo PivotCandles e descriviamone il comportamento. Definiamo i valori restituiti per collegare l'indicatore di bilanciamento:

  • -1 – aprire una posizione di vendita;
  • -2 – chiudere una posizione di acquisto;
  • 0 – nessun segnale;
  • 1 – posizione di acquisto aperta;
  • 2 – chiudi la posizione di vendita.

Come sapete, i veri programmatori non cercano modi semplici. Cercano i più semplici. :) Io non sono un'eccezione. Mentre ascoltavo musica in cuffia e bevevo caffè aromatico, ho creato il file con la classe da implementare in un indicatore e in un Expert Advisor (nel caso decidessi di svilupparlo in base all'indicatore). Forse, può anche essere modificato per altri modelli di candele giapponesi. Il codice non contiene nulla di nuovo. Credo che i commenti implementati al codice coprano tutte le possibili domande.

//+------------------------------------------------------------------+
//|                                            PivotCandlesClass.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input int      iMaxBodySize            = 50;  // Maximum candle body, %
input int      iMaxShadowSize          = 0;   // Maximum allowed candle shadow, %
input int      iVolatilityCandlesCount = 5;   // Number of previous bars for calculation of an average volatility
input int      iPrevCandlesCount       = 5;   // Number of previous bars, for which the current bar should be an extremum
input int      iVolatilityPercent      = 100; // Correlation of a signal candle with a previous volatility, %
input int      iMAPeriod               = 10;  // Period of a simple signal moving average
//+------------------------------------------------------------------+
//| Class definition                                                 |
//+------------------------------------------------------------------+
class CPivotCandlesClass
  {
private:
   MqlRates          m_candles[];              // Array for storing the history necessary for calculations
   int               m_history_depth;          // Array length for storing the history
   int               m_handled_candles_count;  // Number of the already processed candles
   
   double            m_ma_value;               // Current calculated moving average value
   double            m_prev_ma_value;          // Previous calculated moving average value
   bool              m_is_highest;             // Check if the current candle is the highest one
   bool              m_is_lowest;              // Check if the current candle is the lowest one
   double            m_volatility;             // Average volatility
   int               m_candle_pattern;         // Current recognized pattern
   
   void              PrepareArrayForNewCandle();        // Prepare the array for accepting the new candle
   int               CheckCandleSize(MqlRates &candle); // Check the candle for conformity with patterns
   void              PrepareCalculation();
protected:
   int               DoAnalizeNewCandle();              // Calculation function
public:
   void              CPivotCandlesClass(); 
   
   void              CleanupHistory();                  // Clean up all calculation variables  
   double            MAValue() {return m_ma_value;}     // Current value of the moving average
   int               AnalizeNewCandle(MqlRates& candle);
   int               AnalizeNewCandle( const datetime time,
                                       const double open,
                                       const double high,
                                       const double low,
                                       const double close,
                                       const long tick_volume,
                                       const long volume,
                                       const int spread );
  };
//+------------------------------------------------------------------+
//| CPivotCandlesClass                                               |
//+------------------------------------------------------------------+
//| Class initialization                                             |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::CPivotCandlesClass()
  {
   // History depth should be enough for all calculations
   m_history_depth = (int)MathMax(MathMax(
      iVolatilityCandlesCount + 1, iPrevCandlesCount + 1), iMAPeriod);
   m_handled_candles_count = 0;
   m_prev_ma_value = 0;
   m_ma_value = 0;
   
   ArrayResize(m_candles, m_history_depth);
  }  
//+------------------------------------------------------------------+
//| CleanupHistory                                                   |
//+------------------------------------------------------------------+
//| Clean up the candle buffer for recalculation                     |
//+------------------------------------------------------------------+
void CPivotCandlesClass::CleanupHistory()
  {
   // Clean up the array
   ArrayFree(m_candles);
   ArrayResize(m_candles, m_history_depth);
   
   // Null calculation variables
   m_handled_candles_count = 0;
   m_prev_ma_value = 0;
   m_ma_value = 0;   
  }
//+-------------------------------------------------------------------+
//| AnalizeNewCandle                                                  |
//+-------------------------------------------------------------------+
//| Preparations for analyzing the new candle and the analysis itself |
//| based on candle's separate parameter values                       |
//+-------------------------------------------------------------------+
int CPivotCandlesClass::AnalizeNewCandle( const datetime time,
                                          const double open,
                                          const double high,
                                          const double low,
                                          const double close,
                                          const long tick_volume,
                                          const long volume,
                                          const int spread )
  {
   // Prepare the array for the new candle
   PrepareArrayForNewCandle();

   // Fill out the current value of the candle
   m_candles[0].time          = time;
   m_candles[0].open          = open;
   m_candles[0].high          = high;
   m_candles[0].low           = low;
   m_candles[0].close         = close;
   m_candles[0].tick_volume   = tick_volume;
   m_candles[0].real_volume   = volume;
   m_candles[0].spread        = spread;

   // Check if there is enough data for calculation
   if (m_handled_candles_count < m_history_depth)
      return 0;
   else
      return DoAnalizeNewCandle();
  }  
//+-------------------------------------------------------------------+
//| AnalizeNewCandle                                                  |
//+-------------------------------------------------------------------+
//| Preparations for analyzing the new candle and the analysis itself |
//| based on the received candle                                      |
//+-------------------------------------------------------------------+
int CPivotCandlesClass::AnalizeNewCandle(MqlRates& candle)
  {
   // Prepare the array for the new candle   
   PrepareArrayForNewCandle();

   // Add the candle 
   m_candles[0] = candle;

   // Check if there is enough data for calculation
   if (m_handled_candles_count < m_history_depth)
      return 0;
   else
      return DoAnalizeNewCandle();
  }
//+------------------------------------------------------------------+
//| PrepareArrayForNewCandle                                         |
//+------------------------------------------------------------------+ 
//| Prepare the array for the new candle                             |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::PrepareArrayForNewCandle()
  {
   // Shift the array by one position to write the new value there
   ArrayCopy(m_candles, m_candles, 1, 0, m_history_depth-1);
   
   // Increase the counter of added candles
   m_handled_candles_count++;
  }
//+------------------------------------------------------------------+
//| CalcMAValue                                                      |
//+------------------------------------------------------------------+ 
//| Calculate the current values of the Moving Average, volatility   |
//|   and the value extremality                                      |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::PrepareCalculation()
  {
   // Store the previous value
   m_prev_ma_value = m_ma_value;
   m_ma_value = 0;
   
   m_is_highest = true; 	// check if the current candle is the highest one
   m_is_lowest = true;  	// check if the current candle is the lowest one
   m_volatility = 0;  	// average volatility
   
   double price_sum = 0; // Variable for storing the sum
   for (int i=0; i<m_history_depth; i++)
     {
      if (i<iMAPeriod)
         price_sum += m_candles[i].close;
      if (i>0 && i<=iVolatilityCandlesCount)
         m_volatility += m_candles[i].high - m_candles[i].low;
      if (i>0 && i<=iPrevCandlesCount)
        {
         m_is_highest = m_is_highest && (m_candles[0].high > m_candles[i].high);
         m_is_lowest = m_is_lowest && (m_candles[0].low < m_candles[i].low);
        }
     }
   m_ma_value = price_sum / iMAPeriod;
   m_volatility /= iVolatilityCandlesCount;
   
   m_candle_pattern = CheckCandleSize(m_candles[0]);
  }
//+------------------------------------------------------------------+
//| CheckCandleSize                                                  |
//+------------------------------------------------------------------+
//| Check if the candle sizes comply with the patterns               |
//| The function returns:                                            |
//|   0 - if the candle does not comply with the patterns            |
//|   1 - if "hammer" pattern is detected                            |
//|   -1 - if "shooting star" pattern is detected                    |
//+------------------------------------------------------------------+ 
int CPivotCandlesClass::CheckCandleSize(MqlRates &candle)
  {
   double candle_height=candle.high-candle.low;          // candle's full height
   double candle_body=MathAbs(candle.close-candle.open); // candle's body height

   // Check if the candle has a small body
   if(candle_body/candle_height*100.0>iMaxBodySize)
      return 0;

   double candle_top_shadow=candle.high-MathMax(candle.open,candle.close);   // candle upper shadow height
   double candle_bottom_shadow=MathMin(candle.open,candle.close)-candle.low; // candle bottom shadow height

   // If the upper shadow is very small, that indicates the "hammer" pattern
   if(candle_top_shadow/candle_height*100.0<=iMaxShadowSize)
      return 1;
   // If the bottom shadow is very small, that indicates the "shooting star" pattern
   else if(candle_bottom_shadow/candle_height*100.0<=iMaxShadowSize)
      return -1;
   else
      return 0;
  }
//+------------------------------------------------------------------+
//| DoAnalizeNewCandle                                               |
//+------------------------------------------------------------------+
//| Real analysis of compliance with the patterns                    |
//+------------------------------------------------------------------+ 
int CPivotCandlesClass::DoAnalizeNewCandle()
  {
   // Prepare data for analyzing the current situation
   PrepareCalculation();
   
   // Process prepared data and set the exit signal
   int signal = 0;
   
   ///////////////////////////////////////////////////////////////////
   // EXIT SIGNALS                                                  //
   ///////////////////////////////////////////////////////////////////
   // If price crosses the moving average downwards, short position is closed
   if(m_candles[1].close > m_prev_ma_value && m_candles[0].close < m_ma_value)
      signal = 2;
   // If price crosses the moving average upwards, long position is closed 
   else if (m_candles[1].close < m_prev_ma_value && m_candles[0].close > m_ma_value)
      signal = -2;
      
   ///////////////////////////////////////////////////////////////////
   // ENTRY SIGNALS                                                 //
   ///////////////////////////////////////////////////////////////////
   // Check if the minimum volatility condition is met
   if (m_candles[0].high - m_candles[0].low >= iVolatilityPercent / 100.0 * m_volatility)
     {
      // Checks for "shooting star" pattern
      if (m_candle_pattern < 0 && m_is_highest && m_candles[0].close > m_ma_value)
         signal = -1;
      // Checks for "hammer" pattern
      else if (m_candle_pattern > 0 && m_is_lowest && m_candles[0].close < m_ma_value)
         signal = 1;
     }
     
   return signal;
  }
//+------------------------------------------------------------------+

Possiamo vedere che l'intera parte di calcolo viene eseguita dalla classe CPivotCandlesClass. È considerata una buona programmazione separare la parte di calcolo da quella visiva e cerco di fare del mio meglio per seguire questa raccomandazione. I benefici non tardano ad arrivare - di seguito è riportato il codice dell'indicatore stesso:

//+------------------------------------------------------------------+
//|                                                 PivotCandles.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

// Use four buffers, while drawing two
#property indicator_buffers 4
#property indicator_plots   2
//--- plot SlowMA
#property indicator_label1  "SlowMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAliceBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot ChartSignal
#property indicator_label2  "ChartSignal"
#property indicator_type2   DRAW_COLOR_ARROW
#property indicator_color2  clrLightSalmon,clrOrangeRed,clrBlack,clrSteelBlue,clrLightBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  3

#include <PivotCandlesClass.mqh>
//+------------------------------------------------------------------+
//| Common arrays and structures                                     |
//+------------------------------------------------------------------+
//--- Indicator buffers                                                
double   SMA[];            // Values of the Moving Average
double   Signal[];         // Signal values
double   ChartSignal[];    // Location of signals on the chart
double   SignalColor[];    // Signal color array
//--- Calculation class
CPivotCandlesClass PivotCandlesClass;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,SMA,INDICATOR_DATA);
   SetIndexBuffer(1,ChartSignal,INDICATOR_DATA);
   SetIndexBuffer(2,SignalColor,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(3,Signal,INDICATOR_CALCULATIONS);

//--- set 0 as an empty value
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   // If there have not been calculations yet or (!) the new history is uploaded, clean up the calculation object
   if (prev_calculated == 0)
      PivotCandlesClass.CleanupHistory();
   
   int end_calc_edge = rates_total-1;   
   if (prev_calculated >= end_calc_edge)
      return end_calc_edge;
   
   for(int i=prev_calculated; i<end_calc_edge; i++)
     {
      int signal = PivotCandlesClass.AnalizeNewCandle(time[i],open[i],high[i],low[i],close[i],tick_volume[i],volume[i],spread[i]);
      Signal[i] = signal;
      SMA[i] = PivotCandlesClass.MAValue();
      
      // Signals are processed, display them on the chart
      // Set the location of our signals...
      if (signal < 0)
         ChartSignal[i]=high[i];
      else if (signal > 0)
         ChartSignal[i]=low[i];
      else
         ChartSignal[i]=0;
      // .. as well as their color
      // Signals have a range of [-2..2], while color indices - [0..4]. Align them 
      SignalColor[i]=signal+2;
     }
   
   // Set the Moving Average value similar to the previous one to prevent it from sharp fall
   SMA[end_calc_edge] = SMA[end_calc_edge-1];

//--- return value of prev_calculated for next call
   return(end_calc_edge);
  }
//+------------------------------------------------------------------+

L'indicatore è pronto. Ora, testiamolo su uno qualsiasi dei grafici. Per fare ciò, installare l'indicatore compilato sul grafico. Successivamente, vedremo qualcosa di simile a quello mostrato nell'immagine qui sotto.

Figura 2. Indicatore dei modelli di candele giapponesi "martello" e "stella cadente

Figura 2. Indicatore dei modelli di candele giapponesi "martello" e "stella cadente"

I punti colorati indicano possibili entrate e uscite dal mercato. I colori sono selezionati come segue:

  • rosso scuro – vendere;
  • blu scuro – comprare;
  • rosso chiaro – chiusura posizione lunga;
  • rosso chiaro – chiusura posizione corta.

I segnali di chiusura si formano ogni volta che il prezzo raggiunge la sua media mobile. Il segnale viene ignorato, se non c'erano posizioni in quel momento.

Ora, passiamo all'argomento principale dell'articolo. Abbiamo l'indicatore con il buffer del segnale che genera solo alcuni segnali determinati. Mostriamo in una finestra separata dello stesso grafico quanto possono essere redditizi / in perdita questi segnali se effettivamente seguiti. L'indicatore è stato sviluppato appositamente per quel caso. Può connettersi a un altro indicatore e aprire / chiudere posizioni virtuali a seconda dei segnali in arrivo.

Proprio come con l'indicatore precedente, dovremmo dividere il codice in due parti: calcolo e visiva. Di seguito è riportato il risultato di una notte insonne ma spero che ne valga la pena. :)

//+------------------------------------------------------------------+
//|                                                 BalanceClass.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Common structures                                                |
//+------------------------------------------------------------------+
// Structure for returning calculation results 
// using only return command;
struct BalanceResults
  {
   double balance;
   double equity;
  };
//+------------------------------------------------------------------+
//| Common function                                                  |
//+------------------------------------------------------------------+
//  Function for searching for the indicator handle by its name
int FindIndicatorHandle(string _name)
  {
   // Receive the number of open charts
   int windowsCount = (int)ChartGetInteger(0,CHART_WINDOWS_TOTAL);
   
   // Search all of them
   for(int w=windowsCount-1; w>=0; w--)
     {
      // How many indicators are attached to the current chart
      int indicatorsCount = ChartIndicatorsTotal(0,w);

      // Search by all chart indicators
      for(int i=0;i<indicatorsCount;i++)
        {
         string name = ChartIndicatorName(0,w,i);
         // If such an indicator is found, return its handle
         if (name == _name)
            return ChartIndicatorGet(0,w,name);
        }
     }  
     
   // If there is no such an indicator, return the incorrect handle 
   return -1;
  }
//+------------------------------------------------------------------+
//| Base calculation class                                           |
//+------------------------------------------------------------------+
class CBaseBalanceCalculator
  {
private:
   double            m_position_volume; // Current open position volume
   double            m_position_price;  // Position opening price
   double            m_symbol_points;   // Value of one point for the current symbol
   BalanceResults    m_results;         // Calculation results
public:
   void              CBaseBalanceCalculator(string symbol_name = "");
   void              Cleanup();
   BalanceResults    Calculate( const double _prev_balance, 
                                const int    _signal,
                                const double _next_open,
                                const double _next_spread );
  };
//+------------------------------------------------------------------+
//| CBaseBalanceCalculator                                           |
//+------------------------------------------------------------------+
void CBaseBalanceCalculator::CBaseBalanceCalculator(string symbol_name = "")
  {
   // Clean up state variables
   Cleanup();
   
   // Define point size (because we will calculate the profit in points)
   if (symbol_name == "")
      m_symbol_points = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
   else 
      m_symbol_points = SymbolInfoDouble(symbol_name, SYMBOL_POINT);
  }
//+------------------------------------------------------------------+
//| Cleanup                                                          |
//+------------------------------------------------------------------+
//| Clean up data on positions and prices                            |
//+------------------------------------------------------------------+
void CBaseBalanceCalculator::Cleanup()
  {
   m_position_volume = 0;
   m_position_price = 0;  
  }
//+------------------------------------------------------------------+
//| Calculate                                                        |
//+------------------------------------------------------------------+
//| Main calculation block                                           |
//+------------------------------------------------------------------+
BalanceResults CBaseBalanceCalculator::Calculate(
                                       const double _prev_balance,
                                       const int _signal,
                                       const double _next_open,
                                       const double _next_spread )
  {
   // Clean up the output structure from the previous values
   ZeroMemory(m_results);
   
   // Initialize additional variables
   double current_price = 0; // current price (bid or ask depending on position direction)
   double profit = 0;        // profit calculated value
   
   // If there was no signal, the balance remains the same 
   if (_signal == 0)
      m_results.balance = _prev_balance;
   // the signal coincides with the direction or no positions are opened yet
   else if (_signal * m_position_volume >= 0)
     {
      // Position already exists, the signal is ignored
      if (m_position_volume != 0)
         // Balance is not changed
         m_results.balance = _prev_balance;
      // No positions yet, buy signal

      else if (_signal == 1)
        {
         // Calculate current ASK price, recalculate price, volume and balance
         current_price = _next_open + _next_spread * m_symbol_points;
         m_position_price = (m_position_volume * m_position_price + current_price) / (m_position_volume + 1);
         m_position_volume = m_position_volume + 1;
         m_results.balance = _prev_balance;
        }
      // No positions yet, sell signal
      else if (_signal == -1) 
        {
         // Calculate current BID price, recalculate price, volume and balance
         current_price = _next_open;
         m_position_price = (-m_position_volume * m_position_price + current_price) / (-m_position_volume + 1);
         m_position_volume = m_position_volume - 1; 
         m_results.balance = _prev_balance;      
        }
      else
         m_results.balance = _prev_balance;
     }
   // Position is set already, the opposite direction signal is received
   else 
     {
      // buy signal/close sell position
      if (_signal > 0)
        {
         // Close position by ASK price, recalculate profit and balance
         current_price = _next_open + _next_spread * m_symbol_points;
         profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
         m_results.balance = _prev_balance + profit;
          
         // If there is a signal for opening a new position, open it at once
         if (_signal == 1)
           {
            m_position_price = current_price;
            m_position_volume = 1;
           }
         else
            m_position_volume = 0;
        }
      // sell signal/close buy position
      else 
        {
         // Close position by BID price, recalculate profit and balance
         current_price = _next_open;
         profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
         m_results.balance = _prev_balance + profit;
         
         // If there is a signal for opening a new position, open it at once
         if (_signal == -1)
           {
            m_position_price = current_price;
            m_position_volume = -1;
           }
         else 
           m_position_volume = 0;
        }
     }
    
   // Calculate the current equity
   if (m_position_volume > 0)
     {
      current_price = _next_open;
      profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
      m_results.equity = m_results.balance + profit;
     }
   else if (m_position_volume < 0)
     {
      current_price = _next_open + _next_spread * m_symbol_points;
      profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
      m_results.equity = m_results.balance + profit;
     }
   else
      m_results.equity = m_results.balance;    
   
   return m_results;
  }
//+------------------------------------------------------------------+

La classe di calcolo è pronta. Ora, dovremmo implementare la visualizzazione degli indicatori per vedere come funziona.

//+------------------------------------------------------------------+
//|                                                      Balance.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window

#property indicator_buffers 4
#property indicator_plots   3
#property indicator_level1  0.0 
#property indicator_levelcolor Silver 
#property indicator_levelstyle STYLE_DOT
#property indicator_levelwidth 1  
//--- plot Balance
#property indicator_label1  "Balance"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrBlue,clrRed
#property indicator_style1  STYLE_DOT
#property indicator_width1  1
//--- plot Equity
#property indicator_label2  "Equity"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLime
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Zero
#property indicator_label3  "Zero"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGray
#property indicator_style3  STYLE_DOT
#property indicator_width3  1

#include <BalanceClass.mqh>
//+------------------------------------------------------------------+
//| Input and global variables                                       |
//+------------------------------------------------------------------+
input string   iParentName        = "";             // Indicator name for balance calculation
input int      iSignalBufferIndex = -1;            // Signal buffer's index number
input datetime iStartTime         = D'01.01.2012';  // Calculation start date
input datetime iEndTime           = 0;             // Calculation end date
//--- Indicator buffers 
double   Balance[];       // Balance values
double   BalanceColor[];  // Color index for drawing the balance
double   Equity[];        // Equity values
double   Zero[];          // Zero value for histogram's correct display
//--- Global variables
double   Signal[1];       // Array for receiving the current signal
int      parent_handle;   // Indicator handle, the signals of which are to be used 

CBaseBalanceCalculator calculator; // Object for calculating balance and equity
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {     
   // Binding indicator buffers
   SetIndexBuffer(0,Balance,INDICATOR_DATA);
   SetIndexBuffer(1,BalanceColor,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2,Equity,INDICATOR_DATA);
   SetIndexBuffer(3,Zero,INDICATOR_DATA);
  
   // Search for indicator handle by its name
   parent_handle = FindIndicatorHandle(iParentName);
   if (parent_handle < 0)
     {
      Print("Error! Parent indicator not found");
      return -1;
     } 
   
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {   
   // Set the borders for calculating the indicator
   int start_index = prev_calculated;
   int end_index = rates_total-1;
   
   // Calculate balance and equity values
   for(int i=start_index; i<end_index; i++)
     {
      // Check if the balance calculation corresponds the interval
      if (time[i] < iStartTime)
        {
         Balance[i] = 0;
         Equity[i] = 0; 
         continue;
        }
      if (time[i] > iEndTime && iEndTime != 0)
        {
         Equity[i] = (i==0) ? 0 : Equity[i-1];
         Balance[i] = Equity[i]; 
         continue;
        }
      
      // Request a signal from the parent indicator
      if(CopyBuffer(parent_handle,iSignalBufferIndex,time[i],1,Signal)==-1) // Copy the indicator main line data
        {
         Print("Data copy error: " + IntegerToString(GetLastError()));
         return(0);  // Finish the function operation and send indicator for the full recalculation
        }
      
      // Initialize balance and equity calculation
      // Since the signal is formed when the candle is closing, we will be able 
      //   to perform any operation only at the next candle's opening price
      BalanceResults results = calculator.Calculate(i==0?0:Balance[i-1], (int)Signal[0], open[i+1], spread[1+1]);
      
      // Fill out all indicator buffers
      Balance[i] = results.balance;
      Equity[i] = results.equity; 
      Zero[i] = 0;
      if (Balance[i] >= 0)
         BalanceColor[i] = 0;
      else
         BalanceColor[i] = 1;
     }
     
   // Fill out buffers for the last candle 
   Balance[end_index] = Balance[end_index-1];
   Equity[end_index] = Equity[end_index-1]; 
   BalanceColor[end_index] = BalanceColor[end_index-1];
   Zero[end_index] = 0;
     
   return rates_total;
  }
//+------------------------------------------------------------------+

Finalmente è finita! Compiliamolo ed esaminiamo i risultati.


Istruzioni per l'uso

Per valutare il funzionamento del nostro indicatore di nuova concezione, dovrebbe essere allegato al grafico contenente almeno un indicatore di segnale. Se hai seguito tutti i passaggi, allora abbiamo già un tale indicatore: PivotCandles. Quindi, dobbiamo configurare i parametri di input. Vediamo cosa dovremmo specificare:

  • Nome dell'indicatore per il calcolo del saldo (stringa) - dovremmo tenere presente che l'associazione dell'indicatore di saldo viene eseguita per nome. Pertanto, questo campo è obbligatorio.
  • Numero di indice del buffer di segnale (intero) – un altro parametro critico. L'indicatore di segnale può generare diversi segnali secondo l'algoritmo precedentemente definito. Pertanto, l'indicatore di saldo dovrebbe avere i dati relativi al segnale del buffer che dovrebbe calcolare.
  • Data di inizio del calcolo (data/ora) – data iniziale del calcolo del saldo.
  • Data di fine del calcolo (data/ora) – data di fine del calcolo del saldo. Se la data non è selezionata (uguale a zero), il calcolo verrà effettuato fino all'ultima barra.

La Figura 3 mostra la configurazione dei primi due parametri per il collegamento dell'indicatore di bilanciamento al terzo buffer dell'indicatore PivotCandles. I restanti due parametri possono essere impostati a proprio piacimento.

Figura 3. Parametri dell'indicatore di bilanciamento

Figura 3. Parametri dell'indicatore di bilanciamento

Se tutti i passaggi precedenti sono stati eseguiti correttamente, dovresti vedere un'immagine molto simile a quella mostrata di seguito.

Figura 4. Curve di equilibrio e di equità generate utilizzando i segnali dell'indicatore PivotCandles

Figura 4. Curve di equilibrio e di equità generate utilizzando i segnali dell'indicatore PivotCandles

Ora possiamo provare diversi intervalli di tempo e simboli e scoprire le voci di mercato più redditizie e in perdita. Va aggiunto che questo approccio aiuta a trovare le correlazioni di mercato che influenzano i risultati di trading.

Originariamente, volevo confrontare il tempo trascorso a testare l'Expert Advisor in base agli stessi segnali con il tempo trascorso utilizzando il metodo sopra descritto. Ma poi ho abbandonato l'idea, poiché il ricalcolo dell'indicatore richiede circa un secondo. Un tempo così breve non può certamente essere raggiunto dall'Expert Advisor con il suo caricamento della cronologia e gli algoritmi che generano ticks.


Conclusione

Il metodo sopra descritto è molto veloce. Inoltre, fornisce la chiarezza nel testare gli indicatori che generano segnali di apertura / chiusura della posizione. Consente ai trader di analizzare i segnali e le risposte del deposito in un'unica finestra del grafico. Ma ha ancora i suoi limiti di cui dovremmo essere consapevoli:

  • il buffer di segnale dell'indicatore analizzato deve essere preparato preliminarmente;
  • i segnali sono legati all'orario di apertura della nuova barra;
  • nessun ММ nel calcolo del saldo;

Tuttavia, nonostante queste carenze, spero che i benefici saranno più significativi e questo metodo di test prenderà il suo posto tra gli altri strumenti progettati per analizzare il comportamento del mercato ed elaborare i segnali generati dal mercato.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/505

File allegati |
balanceclass.mqh (8.07 KB)
balance.mq5 (5.57 KB)
pivotcandles.mq5 (4.14 KB)
Segnali di trading per MetaTrader 5 Una migliore alternativa agli account PAMM! Segnali di trading per MetaTrader 5 Una migliore alternativa agli account PAMM!
Siamo lieti di annunciare che MetaTrader 5 ora dispone di segnali di trading, offrendo così un potente strumento a investitori e manager. Mentre segui le operazioni di un trader di successo, il terminale le riprodurrà automaticamente nel tuo account!
Come copiare il trading da MetaTrader 5 a MetaTrader 4 Come copiare il trading da MetaTrader 5 a MetaTrader 4
È possibile fare trading su un vero conto MetaTrader 5 oggi? Come organizzare tale trading? L'articolo contiene la teoria di queste domande e i codici di lavoro utilizzati per copiare le operazioni dal terminale MetaTrader 5 a MetaTrader 4. L'articolo sarà utile sia per gli sviluppatori di Expert Advisor sia per i trader praticanti.
Fondamenti di programmazione MQL5: Arrays Fondamenti di programmazione MQL5: Arrays
Gli array sono parte integrante di quasi tutti i linguaggi di programmazione insieme a variabili e funzioni. L'articolo dovrebbe interessare principalmente i programmatori MQL5 alle prime armi, mentre i programmatori esperti avranno una buona opportunità per riassumere e sistematizzare le loro conoscenze.
L'algoritmo di generazione dei ticks all'interno del tester di strategia del terminale MetaTrader 5 L'algoritmo di generazione dei ticks all'interno del tester di strategia del terminale MetaTrader 5
MetaTrader 5 ci consente di simulare il trading automatico, all'interno di un tester di strategia incorporato, utilizzando gli Expert Advisor e il linguaggio MQL5. Questo tipo di simulazione è chiamato testing di Expert Advisors e può essere implementato utilizzando l'ottimizzazione multithreaded, così come quella simultanea su un numero di strumenti. Per fornire un test approfondito, è necessario eseguire una generazione di tick basata sullo storico dei minuti, disponibile. Questo articolo fornisce una descrizione dettagliata dell'algoritmo, mediante il quale i tick vengono generati per il test dello storico nel terminale client MetaTrader 5.