Comment faire en sorte que les objets dessinés par XY changent en douceur (MT4 vs MT5)

Vitaliy Kuznetsov  

Aidez à résoudre un problème sur le terminal MT5.

J'ai décidé de transférer mon produit de mql4 à mql5.

Il utilise des rectangles qui sont dessinés sur des coordonnées XY.

Dans MT4, il est très fluide et sans à-coups lors du changement d'échelle verticale, mais dans MT5, la même approche conduit à quelques freezes et à une "non-lisse" notable.

J'ai spécialement fabriqué un prototype simplifié pour démontrer l'effet. Il en va de même pour MT4 et MT5. Comparez la différence lorsque vous changez l'échelle verticale (avec une souris sur l'échelle des prix).


Dans MT5 tout sera sans freins, mais le redécoupage est saccadé. Plus il y a d'objets, pire c'est. Mais dans MT4, tout est fluide.

Je joins les fichiers sources mq4 et mq5 et je colle le code mq5.


S'il vous plaît, aidez-moi à le rendre plus facile.


Je veux comprendre : "ceci est" MT5 ou "ceci est" mon code pour MT5.


//+------------------------------------------------------------------+
//|                                                  PrototypeXY.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#property indicator_buffers 0
#property indicator_plots   0


string obj_name = "Asd_";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(){

return(INIT_SUCCEEDED);}

//+------------------------------------------------------------------+
//| 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(NewBar()){
      DrawObj();
   }
return(rates_total);}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam){
   if(id == CHARTEVENT_CHART_CHANGE){
      DrawObj();
   }
}

//+------------------------------------------------------------------+
//| Выводим на график                                                |
//+------------------------------------------------------------------+

void DrawObj(){
   string GenName = obj_name;
   double startPricePos = SymbolInfoDouble(Symbol(),SYMBOL_BID);
   int step_Pips = 50;
   for(int i=1; i<=20;i++){
      double stp = (step_Pips*i)*SymbolInfoDouble(Symbol(),SYMBOL_POINT);
      RectLabelCreate(GenName+"UP_"+IntegerToString(i),startPricePos + stp);
      RectLabelCreate(GenName+"DN_"+IntegerToString(i),startPricePos - stp);
   }
   ChartRedraw(0);
}

//+------------------------------------------------------------------+ 
//| Создает прямоугольную метку                                      | 
//+------------------------------------------------------------------+ 

void RectLabelCreate(string name,   // имя метки 
                     double price   // цена
                     ){
   const long             chart_ID=0;               // ID графика
         int              sub_window=0;             // номер подокна
         int              x=0;                      // координата по оси X 
         int              y=0;                      // координата по оси Y
         
   datetime time_pos_X_centr = 0; // Время по центру графика
   double price_pos_Y_tmp = 0;
   x=(int)(ChartGetInteger(chart_ID,CHART_WIDTH_IN_PIXELS,sub_window)/2);
   ChartXYToTimePrice(chart_ID,x,y,sub_window,time_pos_X_centr,price_pos_Y_tmp);         
         
   ChartTimePriceToXY(chart_ID,sub_window,time_pos_X_centr,price,x,y);

   const int              width=50;                 // ширина 
   const int              height=10;                // высота 
   const color            back_clr=C'236,233,216';  // цвет фона 
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // тип границы 
   const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER; // угол графика для привязки 
   const color            clr=clrRed;               // цвет плоской границы (Flat) 
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // стиль плоской границы 
   const int              line_width=1;             // толщина плоской границы 
   const bool             back=false;               // на заднем плане 
   const bool             selection=false;          // выделить для перемещений 
   const bool             hidden=true;              // скрыт в списке объектов 
   const long             z_order=0;                // приоритет на нажатие мышью 

   if(!ObjectIsExist(name)){
      if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0)){
         ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
         ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
         ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); 
         ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height); 
         ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr); 
         ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border); 
         ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); 
         ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
         ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); 
         ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width); 
         ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
         ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
         ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
         ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
         ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
      }
   }else{
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); 
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);   
   }
} 


//+------------------------------------------------------------------+
//| Есть-ли обьект на графике                                        |
//+------------------------------------------------------------------+

bool ObjectIsExist(string name){
   if(ObjectGetString(0,name,OBJPROP_NAME,0)==name)return(true);
return(false);}

//+------------------------------------------------------------------+
//| Появился новый бар                                               |
//+------------------------------------------------------------------+

bool NewBar(){
   static int countLastBar=0;
   int curBars = iBars(Symbol(),PERIOD_CURRENT);
   bool flg = false;
   if(countLastBar!=curBars){
      countLastBar=curBars;
      flg=true;
   }
return(flg);}
Dossiers :
Nikolai Semko  
Vitaliy Kuznetsov:

Aidez à résoudre un problème sur le terminal MT5.

J'ai décidé de transférer mon produit de mql4 à mql5.

Il utilise des rectangles qui sont dessinés sur des coordonnées XY.

Dans MT4, il est très fluide et sans à-coups lors du changement d'échelle verticale, mais dans MT5, la même approche conduit à quelques freezes et à une "non-lisse" notable.

J'ai spécialement fabriqué un prototype simplifié pour démontrer l'effet. Il en va de même pour MT4 et MT5. Comparez la différence lorsque vous changez l'échelle verticale (avec une souris sur l'échelle des prix).


Dans MT5 tout sera sans freins, mais le redécoupage est saccadé. Plus il y a d'objets, pire c'est. Mais dans MT4, tout est fluide.

Je joins les fichiers sources mq4 et mq5 et je colle le code mq5.


S'il vous plaît, aidez-moi à le rendre plus facile.


Je veux comprendre : est-ce " tel et tel " MT5 ou " tel et tel " mon code pour MT5.


Je n'ai pas encore étudié et exécuté ce code, car je ne suis pas devant mon ordinateur, mais la première chose qui attire mon attention, ce sont les deux fonctions asynchronesChartXYToTimePrice et ChartTimePriceToXY.
Ils sont d'une lenteur irréaliste. Je me suis longtemps battu avec MQ sur ce sujet, mais c'est une ignorance totale.
Je me souviens que chaque fonction est exécutée pendant ~16 ms, c'est-à-dire que dans la boucle 30, il faut 30*2*16 =~1 sec.
Il n'est même pas question de douceur.
Essayez d'abord de retirer ces fonctions de la boucle, le travail sera alors 30 fois plus rapide. Je l'ai implémenté dans iCanvas.
Nikolai Semko  
Eh bien oui, vous avez rencontré 4 fonctions asynchrones qui prennent 99,76% du temps pour s'exécuter.
J'essaie de prouver à MQ depuis des années que ces fonctions ne devraient pas être asynchrones, parce que la table des propriétés du graphique existe déjà et qu'il suffit de prendre ces propriétés et qu'il n'y a aucun intérêt à exécuter un processus asynchrone.
Mais tous les efforts sont vains.
Cela pue vraiment l'idiotie.
Je sais de quoi je parle, car je programme dans de nombreux langages, y compris Kanvas, et j'utilise activement le modèle piloté par les événements.
Et dans la préhistoire de MQL4, ce problème est minime.

Vitaliy Kuznetsov  
Nikolai Semko:
Oui, vous avez 4 fonctions asynchrones qui prennent 99,76% du temps.
...
Et dans le MQL4 préhistorique, ce problème est minime.

Oui, tout vole dans MT4.

Nikolai Semko:
Essayez d'abord de retirer ces fonctions de la boucle, le travail sera alors 30 fois plus rapide. J'ai mis en œuvre ce système dans iCanvas.

Si cela ne vous dérange pas, pouvez-vous me donner un exemple de la façon de mieux le faire ?

Nikolai Semko  
Vitaliy Kuznetsov:

Oui, tout vole dans MT4.

Si cela ne vous dérange pas, pouvez-vous me donner un exemple de la façon de le faire plus intelligemment ?

Le moyen le plus court est de joindre ma biblio iCanvas, ce qui permettra de ne pas utiliser les fonctions asynchrones en boucle.
Vous n'avez pas besoin d'utiliser la toile elle-même. Il sera accroché à votre tableau pendant toute la durée de l'opération, mais il sera vide et transparent.
Si vous souhaitez une méthode plus longue sans utiliser iCanvas, vous devrez alors consulter à nouveau cette bible, car elle y est implémentée, mais ce n'est pas pour les peureux. :))
Je vais essayer d'expliquer brièvement ce que fait iCanvas.
Elle dispose d'un gestionnaire interneOnChartEvent, qui attrape l'événementCHARTEVENT_CHART_CHANGE et remplit sa structure interne W, en utilisant toutes les mêmes fonctions ChartGet asynchrones, mais ne le fait qu'une fois à l'occurrence de cet événement.
Cela permet d'optimiser plus ou moins la vitesse.
Cette bibliothèque contient également l'objet Canvas, qui étire l'écran entier et s'y adapte lorsque l'écran change.


Ici, par exemple, trois lignes de code ont été ajoutées et de nombreuses lignes ont été supprimées :


#property indicator_chart_window
#include <Canvas\iCanvas.mqh> //https://www.mql5.com/ru/code/22164

#property indicator_buffers 0
#property indicator_plots   0


string obj_name = "Asd_";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 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(NewBar()) {
      DrawObj();
   }
   return(rates_total);
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam) {
   if(id == CHARTEVENT_CHART_CHANGE) {
      DrawObj();
   }
}
//+------------------------------------------------------------------+
//| Выводим на график                                                |
//+------------------------------------------------------------------+
void DrawObj() {
   string GenName = obj_name;
   double startPricePos = SymbolInfoDouble(Symbol(),SYMBOL_BID);
   int step_Pips = 50;
   for(int i=1; i<=20; i++) {
      double stp = (step_Pips*i)*SymbolInfoDouble(Symbol(),SYMBOL_POINT);
      RectLabelCreate(GenName+"UP_"+IntegerToString(i),startPricePos + stp);
      RectLabelCreate(GenName+"DN_"+IntegerToString(i),startPricePos - stp);
   }
   ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Создает прямоугольную метку                                      |
//+------------------------------------------------------------------+
void RectLabelCreate(string name,   // имя метки
                     double price   // цена
                    ) {
   const long  chart_ID=0;               // ID графика
   int         sub_window=0;             // номер подокна
   int         x=0;                      // координата по оси X
   int         y=0;                      // координата по оси Y
   x=W.Width/2;
   y = Round(Canvas.Y(price));
   //x=(int)(ChartGetInteger(chart_ID,CHART_WIDTH_IN_PIXELS,sub_window)/2);
   //ChartXYToTimePrice(chart_ID,x,y,sub_window,time_pos_X_centr,price_pos_Y_tmp);
   //ChartTimePriceToXY(chart_ID,sub_window,time_pos_X_centr,price,x,y);
   const int              width=50;                 // ширина
   const int              height=10;                // высота
   const color            back_clr=C'236,233,216';  // цвет фона
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // тип границы
   const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER; // угол графика для привязки
   const color            clr=clrRed;               // цвет плоской границы (Flat)
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // стиль плоской границы
   const int              line_width=1;             // толщина плоской границы
   const bool             back=false;               // на заднем плане
   const bool             selection=false;          // выделить для перемещений
   const bool             hidden=true;              // скрыт в списке объектов
   const long             z_order=0;                // приоритет на нажатие мышью
   if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0)) {
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);
      ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner);
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
      ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);
      ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);
      ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
   } else Print(_LastError);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool NewBar() {
   static int countLastBar=0;
   int curBars = iBars(Symbol(),PERIOD_CURRENT);
   bool flg = false;
   if(countLastBar!=curBars) {
      countLastBar=curBars;
      flg=true;
   }
   return(flg);
}
//+------------------------------------------------------------------+
Dans tous les cas, vous pouvez augmenter le nombre d'objets avec cet algorithme et cela n'affectera pas trop les performances.
Dossiers :
iCanvas.mqh  52 kb
Nikolai Semko  
Dmitry Fedoseev:
Ou peut-être que ce n'est pas un bug mais une fonctionnalité ? Il y a quelque chose d'optimisé avec les graphiques. Et cela fonctionne mal parce qu'il est souhaitable d'utiliser toutes les choses aux fins prévues. Il existe des objets graphiques spécialisés qui sont liés aux coordonnées de prix - ils doivent donc être utilisés pour cette tâche.

C'est ce que je pensais aussi, jusqu'à ce que j'acquière plus de connaissances sur le sujet.
La raison est sans ambiguïté : la sous-réflexion.
Une certaine "autorité" a fait une erreur au début (il y a environ 10 ans) et personne ne pense encore qu'il aurait pu faire une erreur.
https://www.mql5.com/ru/forum/1111/page2780#comment_16886162
Ils semblent même avoir accepté et promis de l'améliorer, mais non - c'est fini. C'est toujours là.
https://www.mql5.com/ru/forum/1111/page2781#comment_16904132


Vitaliy Kuznetsov  
Nikolai Semko:

Le moyen le plus court est de joindre mon bibblet iCanvas, qui vous permettra d'éviter d'utiliser des fonctions asynchrones dans la boucle.
Vous n'avez pas besoin d'utiliser le canevas lui-même. Il sera accroché à votre tableau pendant toute la durée de l'opération, mais il sera vide et transparent.
S'il existe un moyen plus long sans utiliser iCanvas, vous devrez alors consulter à nouveau cette bible, car elle est mise en œuvre à cet endroit, mais ce n'est pas pour les peureux. :))
Je vais essayer d'expliquer brièvement ce que fait iCanvas.
Il dispose d'un gestionnaire interneOnChartEvent, qui attrape l'événementCHARTEVENT_CHART_CHANGE et remplit sa structure interne W, en utilisant toutes les mêmes fonctions ChartGet asynchrones, mais ne le fait qu'une seule fois à l'occurrence de cet événement.
Cela permet d'optimiser plus ou moins la vitesse.
Cette bibliothèque contient également l'objet Canvas, qui étire l'écran entier et s'y adapte lorsque l'écran change.


Ici, par exemple, trois lignes de code ont été ajoutées et de nombreuses lignes ont été supprimées :

Dans tous les cas, vous pouvez augmenter le nombre d'objets avec cet algorithme et cela n'affectera pas trop les performances.

Merci pour cette solution similaire au problème. En effet, la vitesse de rendu a augmenté. On dirait que je vais devoir maîtriser la bibliothèque.

Je tiens également à préciser la nuance suivante. Il compile avec cette formulation avec un avertissement :

y = Canvas.Y(price);

Et il compile avec celui-ci sans avertissement, mais la vitesse est un peu plus lente.

y = (int)Canvas.Y(price);

Qu'est-ce qui est le plus correct ?)

Maxim Kuznetsov  
Vitaliy Kuznetsov:

Merci pour ce type de solution au problème. En effet, la vitesse de rendu s'est améliorée. On dirait que je vais devoir apprendre la bibliothèque.

Je voudrais également clarifier la nuance suivante. Il compile avec cette formulation avec un avertissement :

Et il compile avec celui-ci sans avertissement, mais la vitesse est un peu plus lente.

Qu'est-ce qui est le plus correct ?)

Quel type de "y" avez-vous ?

parce que si int et objectivement"la vitesse baisse un peu" c'est un SAC

Vitaliy Kuznetsov  
Je ne suis pas tout à fait sûr du type Canvas.Y(price) ;

quel type il retourne
Raison: