
Approche orientée objet pour créer des panneaux multi-délais et multi-devises
Introduction
Cet article décrit comment la programmation orientée objet peut être utilisée pour créer des panneaux multi-délais et multi-devises pour MetaTrader 5. L'objectif principal est de créer un panneau universel, qui peut être utilisé pour afficher de nombreux types de données, tels que les prix, les changements de prix, les valeurs des indicateurs ou les conditions d'achat/vente personnalisées sans avoir besoin de modifier le code du panneau lui-même. De cette façon, très peu de codage sera nécessaire pour personnaliser le panneau de la manière dont nous avons besoin.
La solution que je vais décrire fonctionne selon deux modes :
- Mode multi-délai - permet de voir le contenu du tableau calculé sur un symbole courant, mais sur des délais différents ;
- Mode multi-devises - permet de voir le contenu du tableau calculé sur un délai courant, mais sur des symboles différents.
Les images suivantes montrent le panneau dans ces deux modes.
Le premier fonctionne en mode multi-temps et affiche les données suivantes :
- Prix courant ;
- Changement de prix de la barre courante ;
- Changement de prix de la barre courante en pourcentage ;
- Changement de prix de la barre courante sous forme de flèche (haut/bas) ;
- Valeur de l'indicateur RSI(14) ;
- Valeur de l'indicateur RSI(10) ;
- État personnalisé : SMA(20) > prix courant.
Figure 1. Mode multi-délai
Le second fonctionne en mode multi-devises et affiche :
- Prix courant ;
- Changement de prix de la barre courante ;
- Changement de prix de la barre courante en pourcentage ;
- Le changement de prix de la barre courante sous forme de flèche ;
- Valeur de l'indicateur RSI(10) ;
- Valeur de l'indicateur RSI(14) ;
- État personnalisé : SMA(20) > prix courant.
Figure 2. Mode multi-devises
1. Mise en œuvre
Le diagramme de classes suivant décrit la conception de mise en œuvre du panneau.
Figure 3. Diagramme de classe du panneau
Permettez-moi de décrire les éléments du diagramme :
- CTable. Classe de base du panneau. Elle est chargée de dessiner le panneau et de gérer ses composants.
- SpyAgent. C'est un indicateur responsable de l’« espionnage » d'autres symboles (instruments). Chaque agent est créé et envoyé à un symbole différent. L'agent réagit à l'événement OnCalculate lorsqu'un nouveau trait arrive sur le graphique des symboles et envoie l'événement CHARTEVENT_CUSTOM pour informer l'objet CTable qu'il doit procéder à la mise à jour. Toute l'idée derrière cette approche est basée sur l'article « La mise en œuvre d'un mode multi-devises dans MetaTrader 5 ». Vous pouvez y trouver tous les détails techniques.
- CRow. Classe de base pour tous les indicateurs et conditions utilisés pour créer le panneau. En étendant cette classe, il est possible de créer tous les composants nécessaires du panneau.
- CPriceRow. Extension de la classe simple CRow, qui est utilisé pour afficher le prix de l'offre courante.
- CPriceChangeRow. Extension de la classe CRow, utilisée pour afficher le changement de prix de la barre courante. Elle peut afficher les changements de prix, les changements de pourcentage ou les flèches.
- CRSIRow. Extension de la classe CRow, utilisée pour afficher la valeur courante de l'indicateur RSI.
- CPriceMARow. Extension de la classe CRow, affichant une condition personnalisée : SMA > prix courant.
Les classes CTable et CRow ainsi que l'indicateur SpyAgent constituent les parties principales du panneau. CPriceRow, CPriceChangeRow, CRSIRow et CPriceMARow sont les contenus réels du panneau. La classe CRow est conçue pour être étendue par de nombreuses nouvelles classes afin d'obtenir le résultat souhaité. Les quatre classes dérivées présentées ne sont que de simples exemples de ce qui peut être fait ainsi que la manière de le faire.
2. SpyAgent
Nous allons commencer par l'indicateur SpyAgent. Il n'est utilisé qu'en mode multi-devises et est nécessaire pour bien mettre à jour le panneau, lorsqu'un nouveau trait arrive sur d'autres graphiques. Je n'entrerai pas dans les détails de ce concept. Ils sont décrits dans l'article « La mise en œuvre d'un mode multi-devises dans MetaTrader 5 ».
L'indicateur SpyAgent s'exécute sur le graphique du symbole spécifié et envoie deux événements : l'événement d'initialisation et l'événement du nouveau trait. Les deux événements sont de type CHARTEVENT_CUSTOM. Afin de gérer ces événements, nous devons utiliser le gestionnaire OnChartEvent(...) (cela sera montré plus loin dans l'article).
Examinons le code de SpyAgent :
//+------------------------------------------------------------------+ //| SpyAgent.mq5 | //| Marcin Konieczny | //| | //+------------------------------------------------------------------+ #property copyright "Marcin Konieczny" #property indicator_chart_window #property indicator_plots 0 input long chart_id=0; // chart id input ushort custom_event_id=0; // event id //+------------------------------------------------------------------+ //| Indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { if(prev_calculated==0) EventChartCustom(chart_id,0,0,0.0,_Symbol); // sends initialization event else EventChartCustom(chart_id,(ushort)(custom_event_id+1),0,0.0,_Symbol); // sends new tick event return(rates_total); }Il est assez simple. La seule chose qu'il fait est de recevoir de nouveaux traits et d'envoyer des événements CHARTEVENT_CUSTOM.
3. CTable
CTable est la classe de base du panneau. Il stocke des informations sur les paramètres du panneau et gère ses composants. Il met à jour (redessine) le panneau, si nécessaire.
Examinons la déclaration de CTable :
//+------------------------------------------------------------------+ //| CTable class | //+------------------------------------------------------------------+ class CTable { private: int xDistance; // distance from right border of the chart int yDistance; // distance from top of the chart int cellHeight; // table cell height int cellWidth; // table cell width string font; // font name int fontSize; color fontColor; CList *rowList; // list of row objects bool tfMode; // is in multi-timeframe mode? ENUM_TIMEFRAMES timeframes[]; // array of timeframes for multi-timeframe mode string symbols[]; // array of currency pairs for multi-currency mode //--- private methods //--- sets default parameters of the table void Init(); //--- draws text label in the specified table cell void DrawLabel(int x,int y,string text,string font,color col); //--- returns textual representation of given timeframe string PeriodToString(ENUM_TIMEFRAMES period); public: //--- multi-timeframe mode constructor CTable(ENUM_TIMEFRAMES &tfs[]); //--- multi-currency mode constructor CTable(string &symb[]); //--- destructor ~CTable(); //--- redraws table void Update(); //--- methods for setting table parameters void SetDistance(int xDist,int yDist); void SetCellSize(int cellW,int cellH); void SetFont(string fnt,int size,color clr); //--- appends CRow object to the of the table void AddRow(CRow *row); };
Comme vous pouvez le voir, tous les composants du panneau (lignes) sont stockés sous forme de liste de pointeurs CRow, de sorte que chaque composant que nous souhaitons ajouter au panneau doit étendre la classe CRow. CRow peut être vu comme un contrat entre le panneau et ses composants. CTable ne contient aucun code pour le calcul de ses cellules. Il incombe aux classes d'étendre CRow. CTable n'est qu'une structure pour contenir les composants CRow et les redessiner si nécessaire.
Passons en revue les méthodes de CTable. La classe comporte deux constructeurs. Le premier est utilisé pour le mode multi-délais et il est assez simple. Nous n'avons qu'à fournir un tableau de délais que nous voulons afficher.
//+------------------------------------------------------------------+ //| Multi-timeframe mode constructor | //+------------------------------------------------------------------+ CTable::CTable(ENUM_TIMEFRAMES &tfs[]) { //--- copy all timeframes to own array ArrayResize(timeframes,ArraySize(tfs),0); ArrayCopy(timeframes,tfs); tfMode=true; //--- fill symbols array with current chart symbol ArrayResize(symbols,ArraySize(tfs),0); for(int i=0; i<ArraySize(tfs); i++) symbols[i]=Symbol(); //--- set default parameters Init(); }
Le deuxième constructeur est utilisé pour le mode multi-devises et couvre un tableau de symboles (instruments). Celui-ci envoie également des SpyAgents. Il les rattache un par un aux graphiques appropriés.
//+------------------------------------------------------------------+ //| Multi-currency mode constructor | //+------------------------------------------------------------------+ CTable::CTable(string &symb[]) { //--- copy all symbols to own array ArrayResize(symbols,ArraySize(symb),0); ArrayCopy(symbols,symb); tfMode=false; //--- fill timeframe array with current timeframe ArrayResize(timeframes,ArraySize(symb),0); ArrayInitialize(timeframes,Period()); //--- set default parameters Init(); //--- send SpyAgents to every requested symbol for(int x=0; x<ArraySize(symbols); x++) if(symbols[x]!=Symbol()) // don't send SpyAgent to own chart if(iCustom(symbols[x],0,"SpyAgent",ChartID(),0)==INVALID_HANDLE) { Print("Error in setting of SpyAgent on "+symbols[x]); return; } }
La méthode Init crée la liste de lignes (sous forme d'objet CList - CList est une liste dynamique de types CObject) et définit les valeurs par défaut pour les variables internes CTable (police, taille de police, couleur, dimension de cellule et distance du coin supérieur droit du graphique ).
//+------------------------------------------------------------------+ //| Sets default parameters of the table | //+------------------------------------------------------------------+ CTable::Init() { //--- create list for storing row objects rowList=new CList; //--- set defaults xDistance = 10; yDistance = 10; cellWidth = 60; cellHeight= 20; font="Arial"; fontSize=10; fontColor=clrWhite; }
Le destructeur est assez simple. Il supprime la liste des lignes et supprime tous les objets de graphique (étiquettes) créés par le panneau.
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTable::~CTable() { int total=ObjectsTotal(0); //--- remove all text labels from the chart (all object names starting with nameBase prefix) for(int i=total-1; i>=0; i--) if(StringFind(ObjectName(0,i),nameBase)!=-1) ObjectDelete(0,ObjectName(0,i)); //--- delete list of rows and free memory delete(rowList); }
La méthode AddRow ajoute la nouvelle ligne à la liste des lignes. Notez que rowList est un objet CList, qui se redimensionne automatiquement. Cette méthode appelle également la méthode Init pour chaque objet CRow ajouté. Il est nécessaire de permettre à l'objet d'initialiser correctement ses variables internes. Par exemple, il peut utiliser l'appel Init pour créer des indicateurs ou des descripteurs de fichiers.
//+------------------------------------------------------------------+ //| Appends new row to the end of the table | //+------------------------------------------------------------------+ CTable::AddRow(CRow *row) { rowList.Add(row); row.Init(symbols,timeframes); }
La méthode Update est un peu plus compliquée. Elle est utilisée pour redessiner le panneau.
Fondamentalement, elle se compose de trois parties, qui sont :
- Dessiner la première colonne (les noms des lignes)
- Dessiner la première ligne (les noms des plages horaires ou des symboles selon le mode sélectionné)
- Dessiner les cellules internes (les valeurs des composants)
Il convient de noter que nous demandons à chaque composant de calculer sa propre valeur en fonction du symbole et du délai fournis. Nous laissons également le composant décider de la police et de la couleur à utiliser.
//+------------------------------------------------------------------+ //| Redraws the table | //+------------------------------------------------------------------+ CTable::Update() { CRow *row; string symbol; ENUM_TIMEFRAMES tf; int rows=rowList.Total(); // number of rows int columns; // number of columns if(tfMode) columns=ArraySize(timeframes); else columns=ArraySize(symbols); //--- draw first column (names of rows) for(int y=0; y<rows; y++) { row=(CRow*)rowList.GetNodeAtIndex(y); //--- note: we ask row object to return its name DrawLabel(columns,y+1,row.GetName(),font,fontColor); } //--- draws first row (names of timeframes or currency pairs) for(int x=0; x<columns; x++) { if(tfMode) DrawLabel(columns-x-1,0,PeriodToString(timeframes[x]),font,fontColor); else DrawLabel(columns-x-1,0,symbols[x],font,fontColor); } //--- draws inside table cells for(int y=0; y<rows; y++) for(int x=0; x<columns; x++) { row=(CRow*)rowList.GetNodeAtIndex(y); if(tfMode) { //--- in multi-timeframe mode use current symbol and different timeframes tf=timeframes[x]; symbol=_Symbol; } else { //--- in multi-currency mode use current timeframe and different symbols tf=Period(); symbol=symbols[x]; } //--- note: we ask row object to return its font, //--- color and current calculated value for given timeframe and symbol DrawLabel(columns-x-1,y+1,row.GetValue(symbol,tf),row.GetFont(symbol,tf),row.GetColor(symbol,tf)); } //--- forces chart to redraw ChartRedraw(); }
La méthode DrawLabel est utilisée pour dessiner des étiquettes de texte dans la cellule spécifiée du panneau. Tout d'abord, elle vérifie si une étiquette pour cette cellule existe déjà. Sinon, elle en crée une nouvelle.
Ensuite, elle définit toutes les propriétés d'étiquette nécessaires et son texte.
//+------------------------------------------------------------------+ //| Draws text label in the specified cell of the table | //+------------------------------------------------------------------+ CTable::DrawLabel(int x,int y,string text,string font,color col) { //--- create unique name for this cell string name=nameBase+IntegerToString(x)+":"+IntegerToString(y); //--- create label if(ObjectFind(0,name)<0) ObjectCreate(0,name,OBJ_LABEL,0,0,0); //--- set label properties ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_RIGHT_UPPER); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,xDistance+x*cellWidth); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,yDistance+y*cellHeight); ObjectSetString(0,name,OBJPROP_FONT,font); ObjectSetInteger(0,name,OBJPROP_COLOR,col); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fontSize); //--- set label text ObjectSetString(0,name,OBJPROP_TEXT,text); }
D'autres méthodes ne seront pas présentées ici, car elles sont très simples et moins importantes. Le code complet est téléchargeable au bas de l'article.
4. Extension de CRow
CRow est une classe de base pour tous les composants, qui peut être utilisée par le panneau.
Examinons le code de la classe CRow :
//+------------------------------------------------------------------+ //| CRow class | //+------------------------------------------------------------------+ //| Base class for creating custom table rows | //| one or more methods of CRow should be overriden | //| when creating own table rows | //+------------------------------------------------------------------+ class CRow : public CObject { public: //--- default initialization method virtual void Init(string &symb[],ENUM_TIMEFRAMES &tfs[]) { } //--- default method for obtaining string value to display in the table cell virtual string GetValue(string symbol,ENUM_TIMEFRAMES tf) { return("-"); } //--- default method for obtaining color for table cell virtual color GetColor(string symbol,ENUM_TIMEFRAMES tf) { return(clrWhite); } //--- default method for obtaining row name virtual string GetName() { return("-"); } //--- default method for obtaining font for table cell virtual string GetFont(string symbol,ENUM_TIMEFRAMES tf) { return("Arial"); } };
Il étend CObject, car seuls les CObjects peuvent être stockés dans une structure CList. Il a cinq méthodes, qui sont presque vides. Pour être plus précis, la plupart d'entre elles ne renvoient que des valeurs par défaut. Ces méthodes sont conçues pour être remplacées lors de l'extension de CRow. Nous n'avons pas besoin de les remplacer toutes, seulement celles que nous voulons.
À titre d'exemple, créons le composant de panneau le plus simple possible - le composant du prix de l'offre courante. Il peut être utilisé en mode multi-devises pour afficher les prix actuels de divers instruments.
Pour y parvenir, nous créons une classe CPriceRow, qui ressemble à ce qui suit :
//+------------------------------------------------------------------+ //| CPriceRow class | //+------------------------------------------------------------------+ class CPriceRow : public CRow { public: //--- overrides default GetValue(..) method from CRow virtual string GetValue(string symbol,ENUM_TIMEFRAMES tf); //--- overrides default GetName() method from CRow virtual string GetName(); }; //+------------------------------------------------------------------+ //| Overrides default GetValue(..) method from CRow | //+------------------------------------------------------------------+ string CPriceRow::GetValue(string symbol,ENUM_TIMEFRAMES tf) { MqlTick tick; //--- gets current price if(!SymbolInfoTick(symbol,tick)) return("-"); return(DoubleToString(tick.bid,(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS))); } //+------------------------------------------------------------------+ //| Overrides default GetName() method from CRow | //+------------------------------------------------------------------+ string CPriceRow::GetName() { return("Price"); }
Les méthodes que nous avons choisi de remplacer ici sont GetValue et GetName. GetName renvoie simplement le nom de cette ligne, qui sera affiché dans la première colonne du panneau. GetValue obtient le dernier trait sur le symbole spécifié et renvoie le dernier cours acheteur. C'est tout ce dont nous avons besoin.
C'était assez simple. Faisons quelque chose de différent. Nous allons maintenant créer un composant qui affiche la valeur RSI courante.
Le code est similaire au précédent :
//+------------------------------------------------------------------+ //| CRSIRow class | //+------------------------------------------------------------------+ class CRSIRow : public CRow { private: int rsiPeriod; // RSI period string symbols[]; // symbols array ENUM_TIMEFRAMES timeframes[]; // timeframes array int handles[]; // array of RSI handles //--- finds the indicator handle for a given symbol and timeframe int GetHandle(string symbol,ENUM_TIMEFRAMES tf); public: //--- constructor CRSIRow(int period); //--- overrides default GetValue(..) method from CRow virtual string GetValue(string symbol,ENUM_TIMEFRAMES tf); //--- overrides default GetName() method from CRow virtual string GetName(); //--- overrides default Init(..) method from CRow virtual void Init(string &symb[],ENUM_TIMEFRAMES &tfs[]); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CRSIRow::CRSIRow(int period) { rsiPeriod=period; } //+------------------------------------------------------------------+ //| Overrides default Init(..) method from CRow | //+------------------------------------------------------------------+ void CRSIRow::Init(string &symb[],ENUM_TIMEFRAMES &tfs[]) { int size=ArraySize(symb); ArrayResize(symbols,size); ArrayResize(timeframes,size); ArrayResize(handles,size); //--- copies arrays contents into own arrays ArrayCopy(symbols,symb); ArrayCopy(timeframes,tfs); //--- gets RSI handles for all used symbols or timeframes for(int i=0; i<ArraySize(symbols); i++) handles[i]=iRSI(symbols[i],timeframes[i],rsiPeriod,PRICE_CLOSE); } //+------------------------------------------------------------------+ //| Overrides default GetValue(..) method from CRow | //+------------------------------------------------------------------+ string CRSIRow::GetValue(string symbol,ENUM_TIMEFRAMES tf) { double value[1]; //--- gets RSI indicator handle int handle=GetHandle(symbol,tf); if(handle==INVALID_HANDLE) return("err"); //--- gets current RSI value if(CopyBuffer(handle,0,0,1,value)<0) return("-"); return(DoubleToString(value[0],2)); } //+------------------------------------------------------------------+ //| Overrides default GetName() method from CRow | //+------------------------------------------------------------------+ string CRSIRow::GetName() { return("RSI("+IntegerToString(rsiPeriod)+")"); } //+------------------------------------------------------------------+ //| finds the indicator handle for a given symbol and timeframe | //+------------------------------------------------------------------+ int CRSIRow::GetHandle(string symbol,ENUM_TIMEFRAMES tf) { for(int i=0; i<ArraySize(timeframes); i++) if(symbols[i]==symbol && timeframes[i]==tf) return(handles[i]); return(INVALID_HANDLE); }
Nous avons quelques nouvelles méthodes ici. Le constructeur permet de fournir la période RSI et la stocke sous forme de variable membre. La méthode Init est utilisée pour créer des descripteurs d'indicateur RSI. Ces descripteurs sont stockés dans le tableau handles[]. La méthode GetValue copie la dernière valeur du tampon RSI et la renvoie. La méthode GetHandle privée est utilisée pour trouver le descripteur d'indicateur approprié dans le tableau handles[]. GetName est explicite.
Comme nous pouvons le voir, la construction de composants de panneaux est assez simple. De la même manière, nous pouvons créer des composants pour presque toutes les conditions personnalisées. Il n'est pas nécessaire que ce soit la valeur de l'indicateur. Ci-dessous, je présente une condition personnalisée basée sur SMA. Elle vérifie si le prix courant est supérieur à la moyenne mobile et affiche « Oui » ou « Non ».
//+------------------------------------------------------------------+ //| CPriceMARow class | //+------------------------------------------------------------------+ class CPriceMARow : public CRow { private: int maPeriod; // period of moving average int maShift; // shift of moving average ENUM_MA_METHOD maType; // SMA, EMA, SMMA or LWMA string symbols[]; // symbols array ENUM_TIMEFRAMES timeframes[]; // timeframes array int handles[]; // array of MA handles //--- finds the indicator handle for a given symbol and timeframe int GetHandle(string symbol,ENUM_TIMEFRAMES tf); public: //--- constructor CPriceMARow(ENUM_MA_METHOD type,int period,int shift); //--- overrides default GetValue(..) method of CRow virtual string GetValue(string symbol,ENUM_TIMEFRAMES tf); // overrides default GetName() method CRow virtual string GetName(); //--- overrides default Init(..) method from CRow virtual void Init(string &symb[],ENUM_TIMEFRAMES &tfs[]); }; //+------------------------------------------------------------------+ //| CPriceMARow class constructor | //+------------------------------------------------------------------+ CPriceMARow::CPriceMARow(ENUM_MA_METHOD type,int period,int shift) { maPeriod= period; maShift = shift; maType=type; } //+------------------------------------------------------------------+ //| Overrides default Init(..) method from CRow | //+------------------------------------------------------------------+ void CPriceMARow::Init(string &symb[],ENUM_TIMEFRAMES &tfs[]) { int size=ArraySize(symb); ArrayResize(symbols,size); ArrayResize(timeframes,size); ArrayResize(handles,size); //--- copies arrays contents into own arrays ArrayCopy(symbols,symb); ArrayCopy(timeframes,tfs); //--- gets MA handles for all used symbols or timeframes for(int i=0; i<ArraySize(symbols); i++) handles[i]=iMA(symbols[i],timeframes[i],maPeriod,maShift,maType,PRICE_CLOSE); } //+------------------------------------------------------------------+ //| Overrides default GetValue(..) method of CRow | //+------------------------------------------------------------------+ string CPriceMARow::GetValue(string symbol,ENUM_TIMEFRAMES tf) { double value[1]; MqlTick tick; //--- obtains MA indicator handle int handle=GetHandle(symbol,tf); if(handle==INVALID_HANDLE) return("err"); //--- gets the last MA value if(CopyBuffer(handle,0,0,1,value)<0) return("-"); //--- gets the last price if(!SymbolInfoTick(symbol,tick)) return("-"); //--- checking the condition: price > MA if(tick.bid>value[0]) return("Yes"); else return("No"); } //+------------------------------------------------------------------+ //| Overrides default GetName() method of CRow | //+------------------------------------------------------------------+ string CPriceMARow::GetName() { string name; switch(maType) { case MODE_SMA: name = "SMA"; break; case MODE_EMA: name = "EMA"; break; case MODE_SMMA: name = "SMMA"; break; case MODE_LWMA: name = "LWMA"; break; } return("Price>"+name+"("+IntegerToString(maPeriod)+")"); } //+------------------------------------------------------------------+ //| finds the indicator handle for a given symbol and timeframe | //+------------------------------------------------------------------+ int CPriceMARow::GetHandle(string symbol,ENUM_TIMEFRAMES tf) { for(int i=0; i<ArraySize(timeframes); i++) if(symbols[i]==symbol && timeframes[i]==tf) return(handles[i]); return(INVALID_HANDLE); }
Le code est plus long, car la moyenne mobile a trois paramètres : période, décalage et type. GetName est un peu plus compliqué car il construit le nom en fonction du type et de la période MA. GetValue fonctionne presque de la même manière que dans le cas de CRSIRow, mais au lieu de renvoyer la valeur de l'indicateur, il renvoie « Oui » si le prix est supérieur à SMA ou « Non » s'il est inférieur.
Le dernier exemple est un peu plus complexe. C'est la classe CPriceChangeRow, qui montre le changement de prix de la barre courante. Elle fonctionne en trois modes :
- Affichage des flèches (vert vers le haut ou rouge vers le bas) ;
- Affichage du changement de prix sous forme de valeur (vert ou rouge) ;
- Affichage de la variation de prix en pourcentage (vert ou rouge).
Le code ressemble à ceci :
//+------------------------------------------------------------------+ //| CPriceChangeRow class | //+------------------------------------------------------------------+ class CPriceChangeRow : public CRow { private: bool percentChange; bool useArrows; public: //--- constructor CPriceChangeRow(bool arrows,bool percent=false); //--- overrides default GetName() method from CRow virtual string GetName(); //--- overrides default GetFont() method from CRow virtual string GetFont(string symbol,ENUM_TIMEFRAMES tf); //--- overrides default GetValue(..) method from CRow virtual string GetValue(string symbol,ENUM_TIMEFRAMES tf); //--- overrides default GetColor(..) method from CRow virtual color GetColor(string symbol,ENUM_TIMEFRAMES tf); }; //+------------------------------------------------------------------+ //| CPriceChangeRow class constructor | //+------------------------------------------------------------------+ CPriceChangeRow::CPriceChangeRow(bool arrows,bool percent=false) { percentChange=percent; useArrows=arrows; } //+------------------------------------------------------------------+ //| Overrides default GetName() method from CRow | //+------------------------------------------------------------------+ string CPriceChangeRow::GetName() { return("PriceChg"); } //+------------------------------------------------------------------+ //| Overrides default GetFont() method from CRow | //+------------------------------------------------------------------+ string CPriceChangeRow::GetFont(string symbol,ENUM_TIMEFRAMES tf) { //--- we use Wingdings font to draw arrows (up/down) if(useArrows) return("Wingdings"); else return("Arial"); } //+------------------------------------------------------------------+ //| Overrides default GetValue(..) method from CRow | //+------------------------------------------------------------------+ string CPriceChangeRow::GetValue(string symbol,ENUM_TIMEFRAMES tf) { double close[1]; double open[1]; //--- gets open and close of current bar if(CopyClose(symbol,tf,0, 1, close) < 0) return(" "); if(CopyOpen(symbol, tf, 0, 1, open) < 0) return(" "); //--- current bar price change double change=close[0]-open[0]; if(useArrows) { if(change > 0) return(CharToString(233)); // returns up arrow code if(change < 0) return(CharToString(234)); // returns down arrow code return(" "); }else{ if(percentChange) { //--- calculates percent change return(DoubleToString(change/open[0]*100.0,3)+"%"); }else{ return(DoubleToString(change,(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS))); } } } //+------------------------------------------------------------------+ //| Overrides default GetColor(..) method from CRow | //+------------------------------------------------------------------+ color CPriceChangeRow::GetColor(string symbol,ENUM_TIMEFRAMES tf) { double close[1]; double open[1]; //--- gets open and close of current bar if(CopyClose(symbol,tf,0, 1, close) < 0) return(clrWhite); if(CopyOpen(symbol, tf, 0, 1, open) < 0) return(clrWhite); if(close[0] > open[0]) return(clrLime); if(close[0] < open[0]) return(clrRed); return(clrWhite); }
Le constructeur a deux paramètres. Le premier décide d'afficher ou non les flèches. S’il est vrai, le deuxième paramètre est ignoré. S'il est faux, le deuxième paramètre décide s'il faut afficher les changements en pourcentage ou simplement les changements de prix.
Pour cette classe, j'ai décidé de remplacer par quatre méthodes de CRow : GetName, GetValue, GetColor et GetFont. GetName est le plus simple et renvoie simplement le nom. GetFont est utilisé, car il donne la possibilité d'afficher des flèches ou d'autres caractères de la police Wingdings. GetColor renvoie la couleur citron lorsque le prix augmente et rouge lorsqu'il baisse. La couleur blanche est renvoyée lorsqu'elle reste en place ou en cas d'erreurs. GetValue obtient les prix d'ouverture et de clôture de la dernière barre, calcule la différence et la renvoie. En mode flèche, il renvoie les codes de caractères Wingdings des flèches haut et bas.
5. Comment utiliser le tout
Pour utiliser le panneau, nous devons créer un nouvel indicateur. Appelons-le TableSample.
Les événements que nous devons gérer sont :
- OnInit();
- OnDeinit();
- OnCalculate(...);
- OnChartEvent(...) (uniquement en cas d'utilisation du mode multi-devises)
Nous avons également besoin d'un pointeur vers l'objet CTable, qui sera créé dynamiquement dans OnInit(). Tout d'abord, nous devons décider quel mode nous utiliserons (multi-délai ou multi-devises). L'exemple de code ci-dessous montre le mode multi-devises, mais tout ce qui est nécessaire pour le mode multi-délai se trouve également ici dans les commentaires. Pour le mode multi-devises, nous devons créer un tableau de symboles et le transmettre au constructeur CTable. Pour le mode multi-délai, nous créerions un tableau de délai et le transmettrions au deuxième constructeur CTable.
Après cela, nous devons créer tous les composants nécessaires et les ajouter au panneau à l'aide de la méthode AddRow. En option, les paramètres du panneau peuvent être ajustés. Après tout, nous devons dessiner le panneau pour la première fois, nous appelons donc Update à la fin de OnInit(). OnDeinit est simple. La seule chose qu'il fait est de supprimer l'objet CTable, ce qui provoque l'appel du destructeur CTable.
OnCalculate(...) et OnChartEvent(...) sont identiques. Ils appellent uniquement la méthode Update. OnChartEvent(...) n'est nécessaire que si le panneau fonctionne en mode multi-devises. Dans ce mode, il gère les événements déclenchés par les SpyAgents. En mode multi-délai, seul OnCalculate(...) est nécessaire, car nous devons surveiller uniquement le symbole du graphique actuel.
//+------------------------------------------------------------------+ //| TableSample.mq5 | //| Marcin Konieczny | //| | //+------------------------------------------------------------------+ #property copyright "Marcin Konieczny" #property version "1.00" #property indicator_chart_window #property indicator_plots 0 #include <Table.mqh> #include <PriceRow.mqh> #include <PriceChangeRow.mqh> #include <RSIRow.mqh> #include <PriceMARow.mqh> CTable *table; // pointer to CTable object //+------------------------------------------------------------------+ //| Indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- timeframes used in table (in multi-timeframe mode) ENUM_TIMEFRAMES timeframes[4]={PERIOD_M1,PERIOD_H1,PERIOD_D1,PERIOD_W1}; //--- symbols used in table (in multi-currency mode) string symbols[4]={"EURUSD","GBPUSD","USDJPY","AUDCHF" }; //-- CTable object creation // table = new CTable(timeframes); // multi-timeframe mode table=new CTable(symbols); // multi-currency mode //--- adding rows to the table table.AddRow(new CPriceRow()); // shows current price table.AddRow(new CPriceChangeRow(false)); // shows change of price in the last bar table.AddRow(new CPriceChangeRow(false,true)); // shows percent change of price in the last bar table.AddRow(new CPriceChangeRow(true)); // shows change of price as arrows table.AddRow(new CRSIRow(14)); // shows RSI(14) table.AddRow(new CRSIRow(10)); // shows RSI(10) table.AddRow(new CPriceMARow(MODE_SMA,20,0)); // shows if SMA(20) > current price //--- setting table parameters table.SetFont("Arial",10,clrYellow); // font, size, color table.SetCellSize(60, 20); // width, height table.SetDistance(10, 10); // distance from upper right chart corner table.Update(); // forces table to redraw return(0); } //+------------------------------------------------------------------+ //| Indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- calls table destructor and frees memory delete(table); } //+------------------------------------------------------------------+ //| Indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- update table: recalculate/repaint table.Update(); return(rates_total); } //+------------------------------------------------------------------+ //| OnChartEvent handler | //| Handles CHARTEVENT_CUSTOM events sent by SpyAgent indicators | //| nedeed only in multi-currency mode! | //+------------------------------------------------------------------+ void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { table.Update(); // update table: recalculate/repaint } //+------------------------------------------------------------------+
Après avoir attaché cet indicateur au graphique, il commence à se mettre à jour et nous pouvons enfin voir le panneau fonctionner.
6. Installation
Tous les fichiers doivent être compilés. SpyAgent et TableSample sont des indicateurs et doivent être copiés dans terminal_data_folder\MQL5\Indicators. Les fichiers restants sont des fichiers include et doivent être placés dans terminal_data_folder\MQL5\Include. Pour exécuter le panneau, attachez l'indicateur TableSample à n'importe quel graphique. Il n'est pas nécessaire de joindre SpyAgent. Ils seront lancés automatiquement.
Conclusion
L'article fournit une implémentation orientée objet du panneau multi-délai et multi-devises pour MetaTrader 5. Il montre comment réaliser un design, qui est facilement extensible et permet de construire des panneaux personnalisés avec peu d'effort.
Tout le code présenté dans cet article peut être téléchargé ci-dessous.
Traduit de l’anglais par MetaQuotes Ltd.
Article original : https://www.mql5.com/en/articles/357





- 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