English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Test rapide des idées de trading sur le graphique

Test rapide des idées de trading sur le graphique

MetaTrader 5Systèmes de trading | 13 janvier 2022, 09:28
224 0
Vladimir Kustikov
Vladimir Kustikov

Introduction

Le sixième championnat de trading automatisé a enfin commencé. Toute excitation initiale est terminée et nous pouvons enfin nous détendre un peu et examiner les robots de trading soumis. J'ai décidé de faire une petite recherche pour découvrir les caractéristiques les plus notables des robots de trading modernes et définir ce que nous pouvons attendre de leur activité de trading.

Cela s'est avéré assez difficile. Par conséquent, mes calculs ne peuvent pas être qualifiés de parfaitement précis ou complets, car les descriptions d'Expert Advisor et les rares commentaires des développeurs étaient les seules choses que j'avais. Cependant, nous pouvons encore tirer quelques conclusions et voici les résultats de mes calculs : 451 Expert Advisors participent au Championnat mais seulement 316 d'entre eux contiennent des descriptions significatives. Les développeurs des autres ont rempli leurs descriptions de salutations à leurs amis et à leur famille, de messages aux civilisations extraterrestres ou d'auto-applaudissements.

Les stratégies les plus populaires sur l'ATC 2012 :

  • le trading utilisant diverses constructions graphiques (niveaux de prix importants, niveaux de support-résistance, chaines) – 55 ;
  • l’analyse des mouvements de prix (pour différentes périodes de temps) – 33 ;
  • Les systèmes de suivi des tendances (je suppose que ces grands mots cachent une combinaison sur-optimisée de moyennes mobiles mais je peux me tromper) :) ) – 31 ;
  • les modèles de prix statistiques – 10 :
  • l’arbitrage, l’analyse de corrélation de symboles – 8;
  • l’analyse de la volatilité – 8 ;
  • les réseaux neuronaux -7 ;
  • l’analyse de chandelier – 5 ;
  • les moyenneurs – 5 ;
  • les ensembles de stratégies – 5 ;
  • le temps de séance de trading – 4 ;
  • le générateur de nombre aléatoire – 4 ;
  • le trading de l'actualité – 3,
  • les Vagues d'Elliott– 2.

Les stratégies d'indicateurs sont traditionnellement les plus populaires, bien sûr. Il est difficile de définir le rôle de chaque indicateur particulier dans un Expert Advisor particulier mais il est possible d'estimer le nombre absolu de leur utilisation :

  • Moyenne mobile - 75 ;
  • MACD – 54 ;
  • Oscillateur Stochastique – 25 ;
  • RSI – 23 ;
  • Bandes de Bollinger – 19 ;
  • Fractals – 8 ;
  • CCI, ATR – 7 indicateurs chacun ;
  • Zigzag, parabolique SAR – 6 indicateurs chacun ;
  • ADX – 5 ;
  • Momentum – 4 ;
  • indicateurs personnalisés (comment intriguer :) ) – 4 ;
  • Ichimoku, AO – 3 indicateurs chacun ;
  • ROC, WPR, StdDev, Volumes – 2 indicateurs chacun.

Les données permettent de tirer les conclusions suivantes : la plupart des participants utilisent des stratégies de suivi de trading avec des indicateurs. Peut-être que j'ai raté quelque chose lors de la collecte des données, et nous verrons l'avènement de quelques personnalités exceptionnelles dans le domaine du trading automatisé mais cela semble peu probable pour l'instant. Je pense que le problème principal est que les nouveaux arrivants attirés par le marché reçoivent dans la plupart des cas des règles au lieu de connaissances.

Par exemple, voici les règles d'utilisation du MACD, voici les signaux - optimisez maintenant les paramètres et gagnez de l'argent. Et si on utilisait un peu le cerveau ? Absurdité ! Les normes ont déjà été élaborées ! Pourquoi réinventer la roue ? Cependant, nous oublions souvent que les indicateurs qui sont si populaires maintenant ont également été inventés par des traders comme moi et vous. Ils avaient aussi leurs normes et leurs autorités. Peut-être qu'un nouvel indicateur portant votre nom deviendra un standard dans une dizaine d'années.

J'aimerais partager ma méthode de recherche d'idées de trading, ainsi que la méthode que j'utilise pour tester rapidement ces idées.


Description de la méthode

Toute analyse technique est basée sur un axiome simple - les prix tiennent compte de tout. Mais il y a un problème - cette déclaration manque de dynamique. Nous regardons le graphique et voyons une image statique : le prix a en fait tout considéré. Cependant, nous voulons savoir ce que le prix prendra en compte dans un certain laps de temps à l'avenir et où il ira, afin que nous puissions réaliser des bénéfices. Les indicateurs dérivés du prix ont été conçus exactement pour prédire les mouvements futurs possibles.

Comme nous le savons de la physique, la dérivée du premier ordre de la grandeur est la vitesse. Par conséquent, les indicateurs calculent la vitesse de changement de prix actuelle. Nous savons également que les grandeurs importantes ont une inertie empêchant la vitesse de changer brusquement de sa valeur sans l'intervention de forces externes considérables. C'est ainsi que nous abordons progressivement le concept de tendance - l'état des prix lorsque sa dérivée du premier ordre (la vitesse) garde sa valeur pendant la période où les forces externes (actualités, politiques des banques centrales, etc.) n'affectent pas le marché.

Mais revenons à notre point de départ - les prix tiennent compte de tout. Pour développer de nouvelles idées, nous devons examiner le comportement du prix et de ses dérivés au même intervalle de temps. Seul un examen attentif des graphiques des prix élèvera votre trading d'une foi aveugle jusqu'au niveau de compréhension authentique.

Cela peut ne pas conduire à des changements immédiats dans les résultats de trading, mais la capacité de répondre à de nombreuses questions sur le pourquoi jouera un rôle positif tôt ou tard. En outre, l'analyse visuelle des graphiques et des indicateurs vous permettra de trouver de toutes nouvelles corrélations entre les prix et les indicateurs totalement imprévues par leurs développeurs.

Supposons que vous ayez trouvé une nouvelle corrélation qui semble jouer en votre faveur. Quelle est la prochaine étape ? Le moyen le plus simple est d'écrire un Expert Advisor et de le tester sur des données historiques en s'assurant que votre hypothèse est correcte. Si ce n'est pas le cas, nous devons choisir une manière commune d'optimiser les paramètres. Le pire, c'est que nous n'avons pas été en mesure de répondre à la question pourquoi. Pourquoi notre Expert Advisor s'est-il avéré déficitaire/rentable ? Pourquoi y a-t-il eu un si gros retrait ? Sans les réponses, vous ne pourrez pas mettre en œuvre votre idée efficacement.

J'effectue les actions suivantes pour visualiser les résultats d'une corrélation obtenue directement sur le graphique :

  1. Je crée ou modifie l'indicateur nécessaire, pour qu'il génère un signal : -1 pour la vente et 1 pour l'achat.
  2. Je connecte l'indicateur de solde affichant les points d'entrée et de sortie au graphique. L'indicateur montre également les changements de l'équilibre et de l'équité (en points) lors du traitement du signal.
  3. J'analyse dans quels cas et circonstances mes hypothèses sont correctes.

La méthode présente certains avantages.

  • Tout d'abord, l'indicateur de solde est entièrement calculé à l'aide de la méthode OnCalculate offrant une vitesse de calcul maximale et une disponibilité automatique des données historiques dans les tableaux de calcul d'entrée.
  • Deuxièmement, l'ajout du signal à l'indicateur existant est une étape intermédiaire entre la création d'un Expert Advisor via Wizard et son développement par vous-même.
  • Troisièmement, une idée et un résultat final peuvent être vus sur un seul graphique. Bien sûr, la méthode a quelques limites : un signal est lié au cours de la fermeture de la barre, le solde est calculé pour le lot constant, il n'y a pas d'options pour trader avec des ordres en attente. Cependant, toutes ces limitations peuvent être facilement corrigées/améliorées.


Mise en œuvre

Développons un indicateur de signal simple pour comprendre son fonctionnement et évaluer la commodité de la méthode. J'ai longtemps entendu parler des modèles de chandeliers. Alors, pourquoi ne pas découvrir leur travail en pratique ? J'ai sélectionné les modèles inversés "marteau" et "étoile filante" comme signaux d'achat et de vente, respectivement. Les images ci-dessous montrent leur aspect schématique :

Figure 1. Modèles de chandeliers « Marteau » et « Étoile filante »

Figure 1. Modèles de chandeliers "marteau" et "étoile filante"

Maintenant, définissons les règles d'entrée sur le marché lorsque le motif "marteau" apparaît.

  1. La valeur la plus basse de la bougie doit être inférieure à celle des cinq bougies précédentes ;
  2. Le corps de la bougie ne doit pas dépasser 50 % de sa hauteur totale ;
  3. L'ombre supérieure de la bougie ne doit pas dépasser 0% de sa hauteur totale ;
  4. La hauteur de la bougie ne doit pas être inférieure à 100 % de la hauteur moyenne des cinq bougies qui la précèdent ;
  5. Le prix de clôture du modèle devrait être inférieur à la moyenne mobile sur 10 périodes.

Si ces conditions sont remplies, nous devrions ouvrir une position longue. Les règles sont les mêmes pour le motif "étoile filante". La seule différence est que nous devrions ouvrir une position courte :

  1. La valeur la plus élevée de la bougie doit être supérieure à celle des cinq bougies précédentes ;
  2. Le corps de la bougie ne doit pas dépasser 50 % de sa hauteur totale ;
  3. L'ombre inférieure de la bougie ne doit pas dépasser 0% de sa hauteur totale ;
  4. La hauteur de la bougie ne doit pas être inférieure à 100 % de la hauteur moyenne des cinq bougies qui la précèdent ;
  5. Le prix de fermeture du modèle devrait être supérieur à la moyenne mobile sur 10-périodes.

J'ai utilisé un style gras pour les paramètres que j'ai utilisés en fonction de dessins qui peuvent être optimisés à l'avenir (si le motif montre des résultats acceptables). Les limitations que je veux mettre en œuvre nous permettent d'effacer les schémas de ceux qui ont une apparence inappropriée (pp. 1-3), ainsi que de ceux qui sont sciemment faibles et qui ne peuvent pas être acceptés comme signaux.

De plus, nous devons déterminer les moments de sortie. Étant donné que les modèles mentionnés apparaissent comme des signaux d'inversion de tendance, la tendance existe au moment où la bougie appropriée apparaît. Par conséquent, la moyenne mobile chassant le prix sera également présente. Le signal de sortie est formé par le croisement du prix et de sa moyenne mobile sur 10-périodes.

Maintenant, il est temps de faire un peu de programmation. Développons un nouvel indicateur personnalisé dans l'assistant MQL5, nommons-le PivotCandles et décrivons son comportement. Définissons les valeurs renvoyées pour connecter l'indicateur de solde :

  • -1 – ouvrir une position de vente ;
  • -2 – fermer une position d'achat ;
  • 0 – aucun signal ;
  • 1 – ouvrir une position d'achat ;
  • 2 – fermer une position de vente.

Comme vous le savez, les vrais programmeurs ne recherchent pas la facilité. Ils recherchent les plus faciles. :) Je ne suis pas une exception. En écoutant de la musique dans mes écouteurs et en buvant du café aromatique, j'ai créé le fichier avec la classe à mettre en œuvre dans un indicateur et dans un Expert Advisor (au cas où je déciderais de le développer en fonction de l'indicateur). Peut-être qu'il peut même être modifié pour d'autres modèles de chandeliers. Le code ne contient rien de nouveau. Je pense que les commentaires implémentés sur le code couvrent toutes les questions possibles.

//+------------------------------------------------------------------+
//|                                            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;
  }
//+------------------------------------------------------------------+

On peut voir que toute la partie calcul est effectuée par la classe CPivotCandlesClass. Il est considéré comme une bonne programmation de séparer la partie calcul à partir de la partie visuelle et j'essaie de faire de mon mieux pour suivre cette recommandation. Les avantages ne tardent pas à venir - ci-dessous le code de l'indicateur lui-même:

//+------------------------------------------------------------------+
//|                                                 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'indicateur est prêt. Maintenant, testons-le sur l'un des graphiques. Pour ce faire, installez l'indicateur compilé sur le graphique. Après cela, nous verrons quelque chose de similaire à ce qui est montré dans l'image ci-dessous.

Figure 2. Indicateur de modèles de chandeliers "marteau" et "étoile filante"

Figure 2. Indicateur de motifs de chandeliers « marteau » et « étoile filante »

Les points de couleur indiquent les entrées et sorties possibles du marché. Les couleurs sont sélectionnées comme suit :

  • rouge foncé – vendre ;
  • bleu foncé – acheter ;
  • rouge clair – fermeture de position longue ;
  • rouge clair – fermeture de position courte.

Des signaux de fermeture se forment à chaque fois que le prix atteint sa moyenne mobile. Le signal est ignoré, s'il n'y avait pas de positions à ce moment-là.

Passons maintenant au sujet principal de l'article. Nous avons l'indicateur avec le tampon de signal générant seulement certains signaux. Montrons dans une fenêtre séparée du même graphique à quel point ces signaux peuvent être rentables/pertes s'ils sont réellement suivis. L'indicateur a été développé spécialement pour ce cas. Il peut se connecter à un autre indicateur et ouvrir/fermer des positions virtuelles en fonction des signaux entrants.

Tout comme avec l'indicateur précédent, nous devons diviser le code en deux parties - calcul et visuel. Ci-dessous, le résultat d'une nuit blanche mais j'espère que cela en vaut la peine. :)

//+------------------------------------------------------------------+
//|                                                 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 de calcul est prête. Maintenant, nous devons implémenter l'affichage de l'indicateur pour voir comment cela fonctionne.

//+------------------------------------------------------------------+
//|                                                      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;
  }
//+------------------------------------------------------------------+

C'est enfin fini ! Compilons-le et examinons les résultats.


Mode d'emploi

Pour évaluer le fonctionnement de notre indicateur nouvellement développé, il doit être joint au graphique contenant au moins un indicateur de signal. Si vous avez suivi toutes les étapes, nous avons déjà un tel indicateur – PivotCandles. Nous devons donc configurer les paramètres d'entrée. Voyons ce qu'il faut préciser :

  • Nom de l'indicateur pour le calcul du solde (chaîne) - nous devons garder à l'esprit que la liaison de l'indicateur de solde est effectuée par nom. Ce champ est donc obligatoire.
  • Numéro d'index du tampon de signal (entier) - un autre paramètre critique. L'indicateur de signal peut générer plusieurs signaux selon un algorithme préalablement défini. Par conséquent, l'indicateur d'équilibre doit avoir les données concernant le signal du tampon qu'il doit calculer.
  • Date de début du calcul (date/heure) – date initiale du calcul du solde.
  • Date de fin du calcul (date/heure) – date de fin du calcul du solde. Si la date n'est pas sélectionnée (égale à zéro), le calcul se fera jusqu'à la dernière barre.

La figure 3 montre la configuration des deux premiers paramètres pour attacher l'indicateur de solde au troisième tampon de l'indicateur PivotCandles. Les deux paramètres restants peuvent être réglés à votre guise.

Figure 3. Paramètres de l'indicateur de solde

Figure 3. Paramètres de l'indicateur d'équilibre

Si toutes les étapes précédentes ont été effectuées correctement, vous devriez voir une image très similaire à celle illustrée ci-dessous.

Figure 4. Courbes de solde et de capitaux propres générées à l'aide des signaux de l'indicateur PivotCandles

Figure 4. Courbes de solde et de capitaux propres générées à l'aide des signaux de l'indicateur PivotCandles

Maintenant, nous pouvons essayer différents délais et symboles et découvrir les entrées de marché les plus rentables et les plus déficitaires. Il faut ajouter que cette approche permet de trouver les corrélations de marché affectant vos résultats de trading.

A l'origine, je voulais comparer le temps passé à tester l'Expert Advisor sur la base des mêmes signaux avec le temps passé à utiliser la méthode décrite ci-dessus. Mais ensuite, j'ai abandonné l'idée, car le recalcul de l'indicateur prend environ une seconde. Un délai aussi court n'est certainement pas encore atteint par l'Expert Advisor avec son historique de téléchargement et ses algorithmes de génération de ticks.


Conclusion

La méthode décrite ci-dessus est très rapide. En outre, il fournit la clarté dans le test des indicateurs qui génèrent des signaux d'ouverture/fermeture de position. Il permet aux traders d'analyser les signaux et les réponses du dépôt à ceux-ci dans une seule fenêtre graphique. Mais il a encore ses limites dont nous devons être conscients :

  • le tampon de signal de l'indicateur analysé doit être préalablement préparé ;
  • les signaux sont liés à l'heure d'ouverture du nouveau bar ;
  • non ММ lors du calcul du solde ;

Cependant, malgré ces lacunes, j'espère que les bénéfices seront plus importants et que cette méthode de test prendra sa place parmi les autres outils conçus pour analyser le comportement du marché et traiter les signaux générés par le marché.

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/505

Fichiers joints |
balanceclass.mqh (8.07 KB)
balance.mq5 (5.57 KB)
pivotcandles.mq5 (4.14 KB)
Signaux de Trading pour MetaTrader 5: Une meilleure alternative aux comptes PAMM ! Signaux de Trading pour MetaTrader 5: Une meilleure alternative aux comptes PAMM !
Nous sommes heureux d'annoncer que MetaTrader 5 propose désormais des signaux de trading, offrant ainsi un outil puissant aux investisseurs et aux gestionnaires. Pendant que vous suivez les trades d'un trader performant, le terminal les reproduira automatiquement dans votre compte !
Comment copier le trading depuis MetaTrader 5 vers MetaTrader 4 Comment copier le trading depuis MetaTrader 5 vers MetaTrader 4
Est-il possible de trader sur un vrai compte MetaTrader 5 aujourd'hui ? Comment organiser un tel trading ? L'article contient la théorie de ces questions et les codes de travail utilisés pour copier les trades depuis le terminal MetaTrader 5 vers MetaTrader 4. L'article sera utile à la fois pour les développeurs d'Expert Advisors et pour les traders pratiquants.
Les bases de la programmation MQL5 : Tableaux Les bases de la programmation MQL5 : Tableaux
Les tableaux font partie intégrante de presque tous les langages de programmation, avec les variables et les fonctions. L'article devrait intéresser principalement les programmeurs novices en MQL5, tandis que les programmeurs expérimentés auront une bonne occasion de résumer et de systématiser leurs connaissances.
L’algorithme de génération de ticks dans le testeur de stratégie du terminal MetaTrader 5 L’algorithme de génération de ticks dans le testeur de stratégie du terminal MetaTrader 5
MetaTrader 5 nous permet de simuler le trading automatique, au sein d’un testeur de stratégie intégré, en utilisant l’Expert Advisors et le MQL5 language. Ce type de simulation est appelé test d’Expert Advisors, et peut être mis en œuvre en utilisant l’optimisation multithread, ainsi que simultanément sur un certain nombre d’instruments. Afin de fournir un test approfondi, une génération de ticks basée sur l’historique des minutes disponibles doit être effectuée. Cet article fournit une description détaillée de l’algorithme, par lequel les ticks sont générés pour les tests historiques dans le terminal client MetaTrader 5.