Como fazer os objetos desenhados por XY mudarem suavemente (MT4 vs MT5)

 

Ajudar a resolver um problema no terminal MT5.

Decidi transferir meu produto de mql4 para mql5.

Ela utiliza retângulos que são desenhados em coordenadas XY.

No MT4 é muito suave e sem solavancos ao mudar de escala vertical, mas no MT5 a mesma abordagem leva a alguns congelamentos e à notória "não-moleza".

Eu fiz especialmente um protótipo simplificado para demonstrar o efeito. É o mesmo para MT4 e MT5. Compare a diferença quando você muda a escala vertical (com um mouse na escala de preços).


No MT5 tudo estará sem freios, mas o redesenho é estúpido. Quanto mais objetos, pior. Mas no MT4 tudo é suave.

Eu anexei arquivos mq4 e mq5 de origem e colei código mq5.


Por favor, ajude-me a facilitar as coisas.


Quero entender: "isto é" MT5 ou "isto é" meu código para 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);}
Arquivos anexados:
 
Vitaliy Kuznetsov:

Ajudar a resolver um problema no terminal MT5.

Decidi transferir meu produto de mql4 para mql5.

Ela utiliza retângulos que são desenhados em coordenadas XY.

No MT4 tudo é suave e sem solavancos ao mudar a escala vertical, mas no MT5 a mesma abordagem leva a alguns congelamentos e a uma notável "não-moleza".

Eu fiz especialmente um protótipo simplificado para demonstrar o efeito. É o mesmo para MT4 e MT5. Compare a diferença quando você muda a escala vertical (com um mouse na escala de preços).


No MT5 tudo estará sem freios, mas o redesenho é estúpido. Quanto mais objetos, pior. Mas no MT4 tudo é suave.

Eu anexei arquivos mq4 e mq5 de origem e colei código mq5.


Por favor, ajude-me a facilitar as coisas.


Quero entender: é "tal e tal" MT5 ou "tal e tal" meu código para MT5.


Ainda não estudei e executei este código, pois não estou em meu computador, mas a primeira coisa que me chama a atenção são as duas funções assíncronasChartXYToTimePrice e ChartTimePriceToXY.
Eles são irrealisticamente lentos. Tenho lutado com a MQ há muito tempo sobre este assunto, mas é um total desrespeito.
Lembro que cada função é executada por ~16 ms, ou seja, em loop 30 leva 30*2*16 =~1 seg.
Nenhuma suavidade está mesmo fora de questão.
Tente primeiro tirar estas funções do laço, depois ele funcionará 30 vezes mais rápido. Tenho-o implementado no iCanvas.
 
Verifique isto no perfilador.
 
bem, sim, você se deparou com 4 funções assíncronas que levam 99,76% do tempo para serem executadas.
Eu venho tentando provar à MQ há anos que estas funções não devem ser assíncronas, porque a tabela de propriedades do gráfico já existe e basta tomar estas propriedades e não adianta executar um processo assíncrono.
Mas todos os esforços são em vão.
Isto realmente tresanda a idiotice.
Sei do que estou falando, porque estou programando em muitas linguagens incluindo Kanvas e usando ativamente o modelo orientado a eventos.
E no MQL4 pré-histórico este problema é mínimo.

 
Talvez não seja um bug, mas uma característica? Algo lá é otimizado com os gráficos. E funciona mal porque é desejável usar todas as coisas para os fins a que se destinam. Existem objetos gráficos especializados que estão vinculados a coordenadas de preço - portanto, devem ser utilizados para esta tarefa.
 
Nikolai Semko:
Sim, você tem 4 funções assíncronas que levam 99,76% de todo o tempo.
...
E na MQL4 pré-histórica este problema é mínimo.

Sim, tudo voa no MT4.

Nikolai Semko:
Tente tirar estas funções primeiro do laço, então ele funcionará 30 vezes mais rápido. Eu implementei isto no iCanvas.

Se você não se importa, pode me dar um exemplo de como fazê-lo melhor?

 
Vitaliy Kuznetsov:

Sim, tudo voa no MT4.

Se você não se importa, pode me dar um exemplo de como fazer isso de forma mais inteligente?

O caminho mais curto é anexar meu biblio iCanvas, que permitirá não utilizar funções assíncronas em loop.
Você não precisa usar a tela em si. Ele ficará pendurado em sua ficha para tudo isso, mas será vazio e transparente.
Se um método mais longo sem usar iCanvas, então você terá que procurar nesta bíblia novamente, como ela é implementada ali, mas não é para os fracos de coração. :)
Vou tentar explicar brevemente o que o iCanvas faz.
Possui manipulador internoOnChartEvent, que captura o eventoCHARTEVENT_CHART_CHANGE e preenche sua estrutura interna W, usando todas as mesmas funções assíncronas ChartGet, mas o faz apenas uma vez com a ocorrência deste evento.
Isto permite mais ou menos otimizar a velocidade.
Também nesta biblioteca pendura o objeto Canvas, que estica a tela inteira e se ajusta a ela quando a tela muda.


Aqui, por exemplo, três linhas de código foram adicionadas e muitas linhas descartadas:


#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);
}
//+------------------------------------------------------------------+
Em qualquer caso, você pode aumentar o número de objetos com este algoritmo e isso não afetará muito o desempenho.
Arquivos anexados:
iCanvas.mqh  52 kb
 
Dmitry Fedoseev:
Ou talvez não seja um bug, mas uma característica? Algo lá é otimizado com gráficos. E funciona mal porque é desejável usar todas as coisas para os fins a que se destinam. Existem objetos gráficos especializados que estão vinculados a coordenadas de preço - portanto, devem ser utilizados para esta tarefa.

Eu também pensava assim, até adquirir mais conhecimento sobre o assunto.
A razão é inequívoca - subentendida.
Alguma "autoridade" cometeu um erro no início (cerca de 10 anos atrás) e ninguém ainda pensa que ele poderia ter cometido um erro.

Eles até parecem ter concordado e prometido melhorá-lo, mas não -acabou. Ainda está lá.



 
Nikolai Semko:

O caminho mais curto é anexar minha bíblia iCanvas, o que lhe permitirá evitar o uso de funções assíncronas no laço.
Você não precisa usar a tela em si. Ele ficará pendurado em sua ficha para tudo isso, mas será vazio e transparente.
Se um caminho mais longo sem usar iCanvas, então você terá que procurar nesta bíblia novamente, como ela é implementada ali, mas não é para os fracos de coração. :)
Vou tentar explicar brevemente o que o iCanvas faz.
Possui manipulador internoOnChartEvent, que captura o eventoCHARTEVENT_CHART_CHANGE e preenche sua estrutura interna W, usando todas as mesmas funções assíncronas ChartGet, mas o faz apenas uma vez com a ocorrência deste evento.
Isto permite mais ou menos otimizar a velocidade.
Também nesta biblioteca pendura o objeto Canvas, que estica a tela inteira e se ajusta a ela quando a tela muda.


Aqui, por exemplo, três linhas de código foram adicionadas e muitas linhas foram descartadas:

Em qualquer caso, você pode aumentar o número de objetos com este algoritmo e isso não afetará muito o desempenho.

Obrigado por uma solução semelhante para o problema. De fato, a velocidade de renderização aumentou. Parece que vou ter que dominar a biblioteca.

Também gostaria de especificar a seguinte nuança. Ela se compila com esta formulação com um aviso:

y = Canvas.Y(price);

E se compila com este sem um aviso, mas a velocidade é um pouco mais lenta.

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

O que é mais correto?)

 
Vitaliy Kuznetsov:

Obrigado por este tipo de solução para o problema. De fato, a velocidade de renderização melhorou. Parece que vou ter que aprender a biblioteca.

Gostaria também de esclarecer a seguinte nuance. Ela se compila com esta formulação com um aviso:

E se compila com este sem um aviso, mas a velocidade é um pouco mais lenta.

O que é mais correto?)

Que tipo de "y" você tem?

porque se int e objetivamente"a velocidade cai um pouco" isto é um BAG

 
Não sei bem que tipo de Tela.Y(preço);

de que tipo retorna
Razão: