Construire un Analyseur de Spectre
Introduction
Cet article est destiné à familiariser ses lecteurs avec une variante possible d'utilisation des objets graphiques du langage MQL5. Il analyse un indicateur, qui implémente un panneau de contrôle d'un simple analyseur de spectre utilisant les objets graphiques.
Trois fichiers sont joints à l'article :
- SpecAnalyzer.mq5 – l'indicateur décrit dans cet article.
- SpecAnalyzer.mqh – le fichier d'inclusion pour SpecAnalyzer.mq5.
- SAInpData.mq5 – l'indicateur utilisé pour l'organisation de l'accès aux données externes.
Pour charger normalement l'indicateur SpecAnalyzer, vous devez placer les trois fichiers dans le dossier \MQL5\Indicators du terminal client. Ensuite, vous devez compiler les fichiers SpecAnalyzer.mq5 et SAInpData.mq5. L'indicateur est destiné à être chargé dans la fenêtre graphique principale. Une fois l'indicateur chargé, il modifie les paramètres d'affichage de cette fenêtre, et lorsqu'il est retiré tous les objets graphiques sont supprimés de la fenêtre. C'est pourquoi vous devez charger l'indicateur dans une fenêtre séparée spéciale créée pour lui, afin d'éviter un changement accidentel du mode d'affichage des fenêtres existantes du terminal.
Compte tenu du fait que l'article ne comporte pas le code source complet de l'indicateur, il est recommandé d'ouvrir le code dans les fichiers joints lors de la lecture de l'article.
L'indicateur décrit dans l'article ne prétend pas être une application toute faite. C'est juste un exemple d'utilisation d'objets graphiques du langage. Ceux qui sont intéressés peuvent mettre à jour et optimiser eux-mêmes le code présenté dans l'article.
Coordonnées
Deux méthodes peuvent être utilisées dans MQL5 pour indiquer les coordonnées lors du dessin d'objets graphiques. Pour certains objets, les coordonnées sont indiquées en nombre de pixels à partir d'un point indiqué de la fenêtre ; et pour d'autres, les coordonnées sont indiquées en tant que valeurs de prix et de temps du graphique affiché dans la fenêtre.
Par exemple, pour placer un objet tel que « Étiquette » ou « Bouton » sur un graphique, vous devez spécifier leurs coordonnées comme la distance en pixels de l'un des coins de la fenêtre graphique. Avec cette façon de traitement les objets conservent leur position quelles que soient les propriétés actuelles de la fenêtre et l'échelle du graphique qui y est affiché. Même si la taille de la fenêtre change, ces objets conservent leur position et se lient les uns aux autres. Si vous déplacez le graphique dans la fenêtre ,à l'aide du bouton gauche de la souris, ces objets conservent leur position par rapport au point d'ancrage choisi de la fenêtre.
L'autre groupe d'objets implique la liaison à un graphique dans la fenêtre au lieu des coordonnées de la fenêtre. Ces objets sont « Courbe de tendance », « Rectangle », etc. Lors de la création et du placement de tels objets, les coordonnées sont indiquées en tant que valeur de temps et de prix d'un graphique affiché dans la fenêtre. Avec ce mode d’indication des coordonnées, les objets changent de position par rapport à la fenêtre graphique et les uns par rapport aux autres lorsque l'échelle de la carte est modifiée ou lorsqu'elle défile. Puisqu’une nouvelle barre apparaît sur le graphique, les objets changent également de position, car l'axe du temps se déplace vers la gauche sur la taille du lapse de temps.
Dans l'indicateur SpecAnalyzer, les deux types d'objets graphiques sont utilisés simultanément pour créer le panneau de contrôle de l'analyseur de spectre. Pour que les objets tenus au graphique à ne pas bouger relativement à la fenêtre, nous fixons un mode d'affichage du graphique selon l'axe vertical et le mode correspondant à l'échelle d'affichage minimum possible au long de l'échelle horizontale du graphique. De plus, le minimum de l'échelle verticale est défini sur 0,0 ; et pour l'axe horizontal, nous définissons le mode sans décalage de la barre zéro du bord droit et sans défilement automatique vers le bord droit du graphique. Par conséquent, le point de coordonnée qui correspond à la barre zéro et à la valeur 0.0 du graphique semble être dans le coin inférieur droit, et avec l'échelle fixe, il peut être utilisé comme point d'ancrage pour les objets tels que « Courbe de tendance » et « Rectangle » . À cela, si nous définissons le même coin inférieur droit que le point d'ancrage des objets comme "Étiquette" ou "Bouton", alors les deux types d'objets seront définitivement liés l'un à l'autre.
Toutes les propriétés nécessaires du graphique sont définies dans la fonction SetOwnProperty() dans le fichier SpecAnalyzer.mqh.
void GRaphChart::SetOwnProperty(void) { ChartSetInteger(m_chart_id,CHART_FOREGROUND,1); ChartSetInteger(m_chart_id,CHART_SHIFT,0); ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,0); ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,1); ChartSetInteger(m_chart_id,CHART_SCALE,0); ChartSetInteger(m_chart_id,CHART_SCALEFIX_11,1); ChartSetInteger(m_chart_id,CHART_SHOW_OHLC,0); ChartSetInteger(m_chart_id,CHART_SHOW_BID_LINE,0); ChartSetInteger(m_chart_id,CHART_SHOW_ASK_LINE,0); ChartSetInteger(m_chart_id,CHART_SHOW_LAST_LINE,0); ChartSetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP,0); ChartSetInteger(m_chart_id,CHART_SHOW_GRID,0); ChartSetInteger(m_chart_id,CHART_SHOW_VOLUMES,CHART_VOLUME_HIDE); ChartSetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR,0); ChartSetInteger(m_chart_id,CHART_SHOW_TRADE_LEVELS,0); ChartSetInteger(m_chart_id,CHART_COLOR_BACKGROUND,Black); ChartSetInteger(m_chart_id,CHART_COLOR_FOREGROUND,Black); ChartSetDouble(m_chart_id,CHART_FIXED_MIN,0.0); }
En plus de définir les propriétés nécessaires du graphique pour assurer la liaison des objets graphiques, cette fonction définit les propriétés qui permettent d'attribuer une couleur et de restreindre l'affichage de certains éléments du graphique.
Étant donné que l'indicateur SpecAnalyzer modifie les propriétés du graphique pendant le travail, nous devons enregistrer les paramètres précédents du graphique lors du chargement de l'indicateur et restaurer les paramètres lors du déchargement. La bibliothèque standard de MQL5 comprend les fonctions virtuelles spéciales à cet effet - Save() et Load() de la classe CChart. Ces fonctions sont destinées à enregistrer les propriétés d'un objet de la classe CChart dans un fichier et à restaurer ces propriétés à partir du fichier créé. Pour modifier l'ensemble des propriétés enregistrées et éviter d'utiliser le fichier des opérations lors de l'enregistrement des propriétés d'un graphique, les fonctions virtuelles Save() et Load() de la classe CChart sont outrepassées lors de la création de la nouvelle classe GRaphChart (voir le fichier SpecAnalyzer.mqh).
class GRaphChart : public CChart { protected: struct ChartPropertyes { double shift_size; double fixed_max; double fixed_min; double points_per_bar; long mode; bool foreground; bool shift; bool autoscroll; long scale; bool scalefix; bool scalefix_11; bool scale_pt_per_bar; bool show_ohls; bool show_bid_line; bool show_ask_line; bool show_last_line; bool show_period_sep; bool show_grid; long show_volumes; bool show_object_descr; bool show_trade_levels; long color_background; long color_foreground; long color_grid; long color_volume; long color_chart_up; long color_chart_down; long color_chart_line; long color_candle_bull; long color_candle_bear; long color_bid; long color_ask; long color_last; long color_stop_level; string ch_comment; }; ChartPropertyes ChProp; public: GRaphChart(); ~GRaphChart(); void SetOwnProperty(); virtual void Save(); virtual void Load(); };
La classe de base de GRaphChart est CChart de la bibliothèque standard de MQL5. La classe GRaphChart comporte la description de la structure ChartPropertyes et la création de l'objet ChProp destiné à stocker les propriétés du graphique dans la mémoire au lieu d'un fichier comme cela est implémenté dans la classe de base. La fonction Save() remplit la structure ChProp avec des données en fonction des propriétés actuelles du graphique, et la fonction Load() restaure les propriétés précédemment enregistrées à partir de celle-ci.
La fonction Save() est appelée dans le constructeur de la classe GRaphChart, et la fonction Load() est appelée dans son destructeur. C'est pourquoi la sauvegarde et la restauration de l'état précédent du graphique sont effectuées automatiquement lors de la création et de la suppression de l'objet de la classe GRaphChart. Le SetOwnProperty() mentionné ci-dessus est également appelé dans le constructeur de classe.
//---------------------------------------------------- Constructor GRaphChart -- GRaphChart::GRaphChart() { m_chart_id=ChartID(); Save(); // Keep a original chart settings SetOwnProperty(); // Set own chart settings } //----------------------------------------------------- Destructor GRaphChart -- GRaphChart::~GRaphChart() { Load(); // Restore a previous chart settings m_chart_id=-1; }
Démontrons l'utilisation de la classe GRaphChart avec un exemple simple. Pour ce faire, créons un nouvel indicateur personnalisé dans MetaEditor et nommons-le Test. Incluez le fichier d'en-tête SpecAnalyzer.mqh dans le code de l'indicateur et créez un objet de la classe GRaphChart en ajoutant deux lignes.
//+------------------------------------------------------------------+ //| Test.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_chart_window #include "SpecAnalyzer.mqh" // <----- Including header file GRaphChart MainChart; // <----- Creating object of the class GRaphChart //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- 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[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Pour une compilation réussie du code ci-dessus, le fichier SpecAnalyzer.mqh doit être placé dans le dossier \MQL5\Indicators du terminal client.
Si vous créez un graphique dans le terminal client et essayez d'y charger notre exemple de test, les propriétés du graphique seront modifiées et vous ne verrez qu'une fenêtre vide prête à y afficher des objets graphiques. Lorsque vous supprimez notre indicateur de test du graphique, son apparence initiale sera restaurée avec l'arrivée d'un nouveau tick.
Revenons à l'indicateur SpecAnalyzer. Au début de l'indicateur (voir le fichier SpecAnalyzer.mq5), la création de l'objet MainChart de la classe GRaphChart est effectuée, ce qui conduit au chargement de l'indicateur et à la sauvegarde des propriétés du graphique.
button. button. button.
GRaphChart MainChart; // Create MainChart object
button. button. button.
Lors du déchargement de l'indicateur, l'objet MainChart est automatiquement terminé, les propriétés initiales du graphique sont alors restaurées dans le destructeur de classe.
Panneau de contrôle
L'apparence du panneau de contrôle dans l'indicateur SpecAnalyzer est déterminée par des objets graphiques placés dans la fenêtre. La classe AllGrObject réunit toutes les fonctions nécessaires pour créer et interagir avec elles ; voir le fichier SpecAnalyzer.mqh.
class AllGrObject : public CChart { protected: long m_chart_id; // chart identifier public: AllGrObject(); ~AllGrObject(); void AddLabel(string name,int fsize,string font, color col,int x,int y,string text=""); void AddButton(string name,int xsize,int ysize,color bgcol, int fsize,string font,color col,int x,int y, string text="",int state=0); void AddEdit(string name,int xsize,int ysize,color bgcol,int fsize, string font,color col,int x,int y,string text=""); void AddTrendLine(string name,color col,int style=0,int width=1); void AddArrowline(string name,color col,int style=0,int width=1); void AddRectangle(string name,color col,int style=0,int width=1); void MoveGrObject(string name,int x1,int y1,int x2,int y2); void SetButtonProp(string name,bool state,color col); long GetRowNumber(string name); void LabelTxt(string name,string str); };
Les fonctions de la classe, dont les noms commencent par Add, sont destinées à créer les objets graphiques. Par exemple, AddButton() crée l'objet « Button ».
Dans l'application, les coordonnées de tous les objets graphiques sont définies comme la distance en pixels du coin inférieur droit du graphique. Pour les objets « Courbe de tendance », « Ligne fléchée » et « Rectangle », nous devons transformer ces coordonnées en valeurs de temps et de prix. Une telle transformation est effectuée dans la fonction MoveGrObject() avant d'affecter des coordonnées à un objet. Un pixel horizontal correspond à une barre et un pixel vertical correspond à un point.
void AllGrObject::MoveGrObject(string name,int x1,int y1,int x2,int y2) { datetime t1[1],t2[1]; long typ; typ=ObjectGetInteger(m_chart_id,name,OBJPROP_TYPE); if(typ==OBJ_TREND||typ==OBJ_ARROWED_LINE||typ==OBJ_RECTANGLE) { CopyTime(_Symbol,_Period,x1,1,t1); CopyTime(_Symbol,_Period,x2,1,t2); ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,0,t1[0]); ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,0,_Point*y1); ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,1,t2[0]); ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,1,_Point*y2); } }
Tous les objets graphiques ne sont créés qu'une seule fois dans l'indicateur, cela se fait lors de l'appel de gr_object_create() depuis la fonction OnInit() de l'indicateur ; voir le fichier SpecAnalyzer.mq5. Pour tous les objets, à l'exception de « Courbe de tendance », « Ligne fléchée » et « Rectangle », les coordonnées sont immédiatement définies. Pour des objets comme « Courbe de tendance », « Ligne fléchée » et « Rectangle » les coordonnées sont définis en appelant la fonction gr_object_coordinate() qui utilise la fonction MoveGrObject() mentionnée ci-dessus, qui transforme le mode de traitement.
void gr_object_coordinate() { GObj.MoveGrObject("Trdline1",48,150,48,360); GObj.MoveGrObject("Trdline2",176,150,176,360); GObj.MoveGrObject("Trdline3",304,150,304,360); GObj.MoveGrObject("Trdline4",432,150,432,360); GObj.MoveGrObject("Trdline5",42,350,560,350); GObj.MoveGrObject("Trdline6",42,300,560,300); GObj.MoveGrObject("Trdline7",42,250,560,250); GObj.MoveGrObject("Trdline8",42,200,560,200); GObj.MoveGrObject("Arrline1",560,150,28,150); GObj.MoveGrObject("Arrline2",560,150,560,370); GObj.MoveGrObject("Rect1",0,1,208,110); GObj.MoveGrObject("Rect2",208,1,416,110); GObj.MoveGrObject("Rect3",416,1,624,110); GObj.MoveGrObject("Rect4",0,1,624,400); GObj.MoveGrObject("Rect5",20,10,195,80); }
L'appel de la fonction gr_object_coordinate() est inclus dans la fonction OnCalculate() de l'indicateur. Il fournit un recalcul correct des coordonnées lorsqu'une nouvelle barre est formée sur le graphique, puisque la fonction est appelée à chaque arrivée d'un nouveau tick.
Les boutons sur le panneau de l'indicateur sont divisés en trois groupes. Le premier groupe se compose de quatre boutons situés à gauche ; ils permettent de sélectionner un mode d'affichage du résultat de l'estimation du spectre de séquence d'entrée par l'indicateur. Quatre modes d'affichage sont pris en charge (selon le nombre de boutons) :
- Amplitude/Ligne - affichage du module de transformée de Fourier à une échelle linéaire le long de l'axe Y.
- Amplitude/dB - affichage du module de transformée de Fourier à une échelle logarithmique le long de l'axe Y.
- Power/Line - affichage du carré du module de la transformée de Fourier à une échelle linéaire le long de l'axe Y.
- Puissance/dB - affichage du carré du module de la transformée de Fourier à une échelle logarithmique le long de l'axe Y.
Pour traiter les clics sur les boutons de ce groupe, le code suivant est inclus dans la fonction OnChartEvent() de l'indicateur :
if(id==CHARTEVENT_OBJECT_CLICK) // Click on the gr. object { if(sparam=="Butt1") // Click on the button { GObj.SetButtonProp("Butt1",1,Chocolate); GObj.SetButtonProp("Butt2",0,SlateGray); GObj.SetButtonProp("Butt3",0,SlateGray); GObj.SetButtonProp("Butt4",0,SlateGray); YPowerFlag=0;YdBFlag=0; } if(sparam=="Butt2") // Click on the button { GObj.SetButtonProp("Butt1",0,SlateGray); GObj.SetButtonProp("Butt2",1,Chocolate); GObj.SetButtonProp("Butt3",0,SlateGray); GObj.SetButtonProp("Butt4",0,SlateGray); YPowerFlag=0;YdBFlag=1; } if(sparam=="Butt3") // Click on the button { GObj.SetButtonProp("Butt1",0,SlateGray); GObj.SetButtonProp("Butt2",0,SlateGray); GObj.SetButtonProp("Butt3",1,Chocolate); GObj.SetButtonProp("Butt4",0,SlateGray); YPowerFlag=1;YdBFlag=0; } if(sparam=="Butt4") // Click on the button { GObj.SetButtonProp("Butt1",0,SlateGray); GObj.SetButtonProp("Butt2",0,SlateGray); GObj.SetButtonProp("Butt3",0,SlateGray); GObj.SetButtonProp("Butt4",1,Chocolate); YPowerFlag=1;YdBFlag=1; }
Lorsqu'un clic sur l'un des boutons est détecté, l'état des autres boutons passe à non enfoncé, ce qui empêche d'appuyer simultanément sur plusieurs boutons d'un même groupe. Dans le même temps, les valeurs correspondantes sont définies pour les indicateurs YPowerFlag et YdBFlag qui déterminent le mode d'affichage actuel.
Le deuxième groupe composé de quatre boutons permet de sélectionner une source de données d'entrée. Il peut s'agir de données externes obtenues en appelant l'indicateur SAInpData.mq5 ou de trois séquences de test générées par l'application elle-même. Le dernier groupe de boutons comprend deux boutons utilisés pour faire défiler la liste dans le champ de saisie d'informations textuelles. La gestion du clic sur tous ces boutons est également effectuée dans la fonction OnChartEvent() de l'indicateur de la même manière que les boutons du premier groupe ; voir le fichier SpecAnalyzer.mq5.
Montrons un exemple d'utilisation de la classe AllGrObject à l’aide l'indicateur de test précédemment créé Test.mq5. Pour ce faire, ajoutez plusieurs lignes à son code source et incluez les fonctions gr_object_create() et gr_object_coordinate() mentionnées précédemment à partir du fichier SpecAnalyzer.mq5.
//+------------------------------------------------------------------+ //| Test.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_chart_window #include "SpecAnalyzer.mqh" GRaphChart MainChart; AllGrObject GObj; // <----- Creating object of the class AllGrObject //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- gr_object_create(); // <----- creating graphical objects 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[]) { //--- MainChart.SetOwnProperty(); // <----- restoring current properties of the chart gr_object_coordinate(); // <----- setting coordinates for the graphical objects //--- return value of prev_calculated for next call return(rates_total); } //----------------------------------------------- Create all graphical objects -- void gr_object_create() { GObj.AddLabel("Title",10,"Arial",DarkGray,256,367,"Spectrum Analyzer"); GObj.AddLabel("Label1",9,"Arial",LightSteelBlue,557,128,"0"); GObj.AddLabel("Label2",9,"Arial",LightSteelBlue,422,128,"128"); GObj.AddLabel("Label3",9,"Arial",LightSteelBlue,294,128,"256"); GObj.AddLabel("Label4",9,"Arial",LightSteelBlue,166,128,"384"); GObj.AddLabel("Label5",9,"Arial",LightSteelBlue,40,128,"512"); GObj.AddLabel("Label6",9,"Arial",LightSteelBlue,28,156,"N"); GObj.AddLabel("Label7",9,"Arial",LightSteelBlue,569,141,"0.00"); GObj.AddLabel("Label8",9,"Arial",LightSteelBlue,569,191,"0.25"); GObj.AddLabel("Label9",9,"Arial",LightSteelBlue,569,241,"0.50"); GObj.AddLabel("Label10",9,"Arial",LightSteelBlue,569,291,"0.75"); GObj.AddLabel("Label11",9,"Arial",LightSteelBlue,569,341,"1.00"); GObj.AddLabel("Label12",9,"Arial",LightSteelBlue,569,358,"U"); GObj.AddLabel("Label13",9,"Arial",DarkGray,490,86,"Y-axis Mode"); GObj.AddLabel("Label14",9,"Arial",DarkGray,285,86,"Input Data"); GObj.AddLabel("Label15",9,"Arial",DarkGray,75,86,"Level"); GObj.AddLabel("Label16",9,"Arial",DarkGray,185,86,"N"); GObj.AddLabel("Label17",8,"Courier",DarkOliveGreen,64,64); GObj.AddLabel("Label18",8,"Courier",DarkOliveGreen,64,51); GObj.AddLabel("Label19",8,"Courier",DarkOliveGreen,64,38); GObj.AddLabel("Label20",8,"Courier",DarkOliveGreen,64,25); GObj.AddLabel("Label21",8,"Courier",DarkOliveGreen,64,12); GObj.AddButton("Butt1",185,18,C'32,32,32',8,"Arial",SlateGray,612,79,"Amplitude (line)",0); GObj.AddButton("Butt2",185,18,C'32,32,32',8,"Arial",Chocolate,612,61,"Amplitude (log)",1); GObj.AddButton("Butt3",185,18,C'32,32,32',8,"Arial",SlateGray,612,43,"Power (line)",0); GObj.AddButton("Butt4",185,18,C'32,32,32',8,"Arial",SlateGray,612,25,"Power (log)",0); GObj.AddButton("Butt5",185,18,C'32,32,32',8,"Arial",SlateGray,403,79,"External Data",0); GObj.AddButton("Butt6",185,18,C'32,32,32',8,"Arial",SlateGray,403,61,"Test 1. SMA(3)",0); GObj.AddButton("Butt7",185,18,C'32,32,32',8,"Arial",Chocolate,403,43,"Test 2. SMA(32)",1); GObj.AddButton("Butt8",185,18,C'32,32,32',8,"Arial",SlateGray,403,25,"Test 3. LWMA(12)",0); GObj.AddButton("Butt9",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,78,"\x0431",0); GObj.AddButton("Butt10",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,44,"\x0432",0); GObj.AddEdit("Edit1",35,18,Black,9,"Arial",SlateGray,180,102); GObj.AddTrendLine("Trdline1",C'32,32,32'); GObj.AddTrendLine("Trdline2",C'32,32,32'); GObj.AddTrendLine("Trdline3",C'32,32,32'); GObj.AddTrendLine("Trdline4",C'32,32,32'); GObj.AddTrendLine("Trdline5",C'32,32,32'); GObj.AddTrendLine("Trdline6",C'32,32,32'); GObj.AddTrendLine("Trdline7",C'32,32,32'); GObj.AddTrendLine("Trdline8",C'32,32,32'); GObj.AddArrowline("Arrline1",LightSteelBlue); GObj.AddArrowline("Arrline2",LightSteelBlue); GObj.AddRectangle("Rect1",C'72,72,72'); GObj.AddRectangle("Rect2",C'72,72,72'); GObj.AddRectangle("Rect3",C'72,72,72'); GObj.AddRectangle("Rect4",DarkGray); GObj.AddRectangle("Rect5",C'72,72,72'); } //---------- Set object coordinate for Trend Line, Arroved Line and Rectangle -- void gr_object_coordinate() { GObj.MoveGrObject("Trdline1",48,150,48,360); GObj.MoveGrObject("Trdline2",176,150,176,360); GObj.MoveGrObject("Trdline3",304,150,304,360); GObj.MoveGrObject("Trdline4",432,150,432,360); GObj.MoveGrObject("Trdline5",42,350,560,350); GObj.MoveGrObject("Trdline6",42,300,560,300); GObj.MoveGrObject("Trdline7",42,250,560,250); GObj.MoveGrObject("Trdline8",42,200,560,200); GObj.MoveGrObject("Arrline1",560,150,28,150); GObj.MoveGrObject("Arrline2",560,150,560,370); GObj.MoveGrObject("Rect1",0,1,208,110); GObj.MoveGrObject("Rect2",208,1,416,110); GObj.MoveGrObject("Rect3",416,1,624,110); GObj.MoveGrObject("Rect4",0,1,624,400); GObj.MoveGrObject("Rect5",20,10,195,80); } //+------------------------------------------------------------------+
Pour donner accès aux fonctions de la classe AllGrObject, créez l'objet GObj de cette classe. Dans la fonction OnInit() de l'indicateur, incluez l'appel de la fonction gr_object_create(), qui crée tous les objets graphiques nécessaires qui déterminent l'apparence et la fonctionnalité du panneau de commande de l'indicateur.
Dans la fonction OnCalculate ajoutez des appels des fonctions MainChart.SetOwnProperty() et gr_object_coordinate(); ainsi, les propriétés du graphique et les coordonnées des objets dessinés dessus seront restaurés à chaque nouvelle tick. Une telle restauration est nécessaire lorsqu'une nouvelle barre est formée au niveau du graphique initial ou que le graphique est déplacé à l'aide du bouton gauche de la souris ou lorsqu'un utilisateur modifie les propriétés du graphique. Après avoir compilé et chargé cet exemple de test, nous verrons les interfaces du panneau de contrôle ; voir fig. 1.
Fig. 1. Interface du panneau de commande.
Afin de vous montrer visuellement l'emplacement du panneau de contrôle en rapport avec la carte, activez l'affichage de l'échelle de la carte dans l'exemple ci-dessus ; voir fig. 2.
Figure 2. L’ Échelle de la Carte
L'Analyseur de Spectre
L'analyse d'un spectre dans l'indicateur est réalisée par 1024 graduations de la séquence d'entrée. L’analyse du spectre est effectuée à l’aide de l’algorithme rapide de Fourier transformation La fonction qui implémente l'algorithme FFT est tirée des publications du site web www.mql4.com. Pour les calculs, nous utilisons la fonction FFT de séquence d’entrée en temps réel ; son code est placé dans le fichier SpecAnalyzer.mqh. Toutes les actions nécessaires à l'analyse spectrale sont implémentées dans la fonction fft_calc().
void fft_calc() { int i,k; realfastfouriertransform(InpData,ArraySize(InpData),false); // FFT for(i=1;i<ArraySize(Spectrum);i++) { k=i*2; Spectrum[i]=InpData[k]*InpData[k]+InpData[k+1]*InpData[k+1]; } Spectrum[0]=0.0; // Clear constant component level }
Lorsque la fonction fft_calc() est appelée, le tableau InpData[] doit comporter une séquence d'entrée à analyser. Après l'exécution de realfastfouriertransform(), le résultat de FFT sera placé dans ce tableau. De plus, le carré du modulo est calculé pour chaque harmonique à partir de la partie réelle et imaginaire des estimations du spectre ; le résultat est écrit dans le tableau Spectrum[]. L'indice d'élément dans le tableau Spectrum[] correspond au numéro d'harmonique. Comme la valeur calculée de la composante constante n'est pas utilisée dans l'indicateur, la valeur zéro est toujours attribuée à l'élément Spectrum[0] du tableau.
Selon la valeur de la variable InputSource, le tableau InpData[] peut être rempli soit avec des séquences de test, soit avec des données obtenues à partir d'un indicateur externe. Les données d'entrée sont formées dans la fonction get_input_data().
void get_input_data() { int i; ArrayInitialize(InpData,0.0); switch(InputSource) { case 0: // External Data if(ExtHandle!=INVALID_HANDLE) CopyBuffer(ExtHandle,0,0,ArraySize(InpData),InpData); break; case 1: // Test 1. SMA(3) for(i=0;i<3;i++)InpData[i]=1; break; case 2: // Test 2. SMA(32) for(i=0;i<32;i++)InpData[i]=1; break; case 3: // Test 3. LWMA(12) for(i=0;i<12;i++)InpData[i]=12-i; break; } }
Si la valeur de InputSource est égale à zéro, 1024 valeurs seront copiées dans le tableau d'entrée InpData[] à partir du tampon zéro de l'indicateur SAInpData.mq5. Les données pour l'analyse peuvent être formées soit dans l'indicateur lui-même, soit en appelant d'autres indicateurs à partir de celui-ci. Pour donner accès à l'indicateur SAInpData.mq5, la ligne suivante est ajoutée à la fonction OnInit(), elle détermine la valeur de la variable ExtHandle.
int OnInit() { button. button. button. ExtHandle=iCustom(NULL,0,"SAInpData"); // External indicator Handle return(0); }
L'indicateur SAInpData.mq5 doit être placé dans le répertoire \MQL5\Indicators du terminal client. L'indicateur SAInpData.mq5 joint à cet article à titre d'exemple transmet une séquence aléatoire à l'analyseur. Pour que SAInpData.mq5 passe une autre séquence à l'analyseur, modifiez son code source.
En tant que séquences de test pour les fonctions get_input_data(), les caractéristiques d'impulsion des moyennes mobiles SMA(3), SMA(32) et LWMA(12) sont générées. En tenant compte du fait que la transformation de Fourier d'une caractéristique impulsionnelle d'un filtre correspond aux caractéristiques amplitude-fréquence de ce filtre, nous pouvons constater les caractéristiques amplitude-fréquence des moyennes mobiles si nous les sélectionnons comme séquences de test.
Pour normaliser le résultat de l'estimation du spectre qui est stocké dans le tableau Spectrum[] et le préparer son affichage dans le mode spécifié, la fonction norm_and_draw() est utilisée ; voir le fichier SpecAnalyzer.mq5. Selon le mode d'affichage choisi, cette fonction remplace le marquage du texte de l'axe Y.
Le résultat de l'estimation du spectre de la séquence d'entrée est affiché non seulement sous forme graphique, mais également sous forme de texte. Pour la représentation des résultats sous forme de texte, cinq objets graphiques de type « Label » sont créés ; ils correspondent à cinq lignes de texte affichées. La fonction list_levels() assure le remplissage de ces lignes avec des informations.
void list_levels() { int m; string str; if(YdBFlag==0) str="%3u %.5f"; // If Y-axis mode = Line else str="%3u %6.1f dB"; // If Y-axis mode = dB m=ArraySize(ListArray)-5; if(ListPointer<1)ListPointer=1; if(ListPointer>m)ListPointer=m; GObj.LabelTxt("Label17",StringFormat(str,ListPointer,ListArray[ListPointer])); GObj.LabelTxt("Label18",StringFormat(str,ListPointer+1,ListArray[ListPointer+1])); GObj.LabelTxt("Label19",StringFormat(str,ListPointer+2,ListArray[ListPointer+2])); GObj.LabelTxt("Label20",StringFormat(str,ListPointer+3,ListArray[ListPointer+3])); GObj.LabelTxt("Label21",StringFormat(str,ListPointer+4,ListArray[ListPointer+4])); }
Les lignes affichent les valeurs des niveaux du tableau ListArray[] formaté à l'aide de la fonction StringFormat(). Selon le mode d'affichage actuel, ce tableau est rempli d'informations au sein de la fonction norm_and_draw(); voir le fichier SpecAnalyzer.mq5. Les informations du tableau ListArray[] sont affichées à partir de l'index du tableau égal à la valeur stockée dans la variable ListPointer. Vous pouvez modifier la valeur de la variable ListPointer, et donc, l'index de départ des lignes à afficher, en appuyant sur les boutons situés à droite du champ de sortie ou en spécifiant l'index nécessaire dans le champ de saisie. Les événements liés à l'appui sur ces boutons et à la finition des modifications sur le champ de saisie sont gérés dans la fonction OnChartEvent() de l'indicateur ; voir le fichier SpecAnalyzer.mq5.
L'apparence de l'indicateur SpecAnalyzer est illustrée dans la figure ci-dessous.
Fig. 3. L'apparition de l'indicateur SpecAnalyzer.
Conclusion
Comme indiqué précédemment, l'indicateur SpecAnalyzer.mq5 n'est qu'un prototype d'un analyseur de spectre complet ; dans l'article, il est utilisé comme exemple d'utilisation des objets graphiques. Pour créer un indicateur complet, vous devrez probablement améliorer son apparence, implémenter une interface plus pratique et améliorer l'algorithme d'estimation du spectre.
Vous pouvez considérablement améliorer l'interface de l'indicateur en utilisant l'objet graphique « Bitmap » pour son ornement, en créant une image pour son panneau de commande frontal dans un éditeur graphique et l’utiliser en tant que sous-couche sur laquelle les éléments de commande seront affichés. Une telle approche peut être utilisée pour créer des indicateurs avec des habillages modifiables( skins).
Littérature
- Yukio Sato. Introduction à la Gestion des Signaux.
- L. Rabiner, B. Gold. Théorie et Application du Traitement Numérique du Signal.
- S.L. Marple, Jr. Analyse Spectrale Numérique avec Applications.
Dossiers
- SpecAnalyzer.mq5 – l'indicateur décrit dans cet article.
- SpecAnalyzer.mqh – le fichier d'inclusion pour SpecAnalyzer.mq5.
- SAInpData.mq5 – l'indicateur utilisé pour l'organisation de l'accès aux données externes.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/185
- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation