Cómo hacer que los objetos dibujados por XY cambien suavemente (MT4 vs MT5)

 

Ayudar a resolver un problema en el terminal MT5.

He decidido transferir mi producto de mql4 a mql5.

Utiliza rectángulos que se dibujan en coordenadas XY.

En MT4 es muy suave y sin tirones al cambiar de escala vertical, pero en MT5 el mismo planteamiento provoca algunas congelaciones y una notable "no suavidad".

He realizado un prototipo simplificado para demostrar el efecto. Es lo mismo para MT4 y MT5. Compare la diferencia cuando cambie la escala vertical (con el ratón sobre la escala de precios).


En MT5 todo estará sin frenos, pero el redibujado es brusco. Cuantos más objetos, peor. Pero en MT4 todo es suave.

Adjunto los archivos fuente mq4 y mq5 y pego el código mq5.


Por favor, ayúdenme a hacerlo sin problemas.


Quiero entender: "esto es" MT5 o "esto es" mi 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);}
Archivos adjuntos:
 
Vitaliy Kuznetsov:

Ayudar a resolver un problema en el terminal MT5.

He decidido transferir mi producto de mql4 a mql5.

Utiliza rectángulos que se dibujan en coordenadas XY.

En MT4 es muy suave y sin tirones al cambiar de escala vertical, pero en MT5 el mismo planteamiento provoca algunas congelaciones y una notable "no suavidad".

He realizado un prototipo simplificado para demostrar el efecto. Es lo mismo para MT4 y MT5. Compare la diferencia cuando cambie la escala vertical (con el ratón sobre la escala de precios).


En MT5 todo estará sin frenos, pero el redibujado es brusco. Cuantos más objetos, peor. Pero en MT4 todo es suave.

Adjunto los archivos fuente mq4 y mq5 y pego el código mq5.


Por favor, ayúdenme a hacerlo sin problemas.


Quiero entender: es "tal" MT5 o "tal" mi código para MT5.


Todavía no he estudiado y ejecutado este código, ya que no estoy en mi ordenador, pero lo primero que me llama la atención son las dos funciones asíncronasChartXYToTimePrice y ChartTimePriceToXY.
Son irrealmente lentos. Llevo mucho tiempo batallando con MQ en este tema, pero es un ignorante total.
Recuerdo que cada función se ejecuta durante ~16 ms, es decir, en el bucle 30 se tarda 30*2*16 =~1 seg.
Ni siquiera se puede hablar de suavidad.
Intenta primero sacar estas funciones del bucle, entonces funcionará 30 veces más rápido. Lo tengo implementado en iCanvas.
 
Comprueba esto en el perfilador.
 
pues sí, te has encontrado con 4 funciones asíncronas que tardan el 99,76% del tiempo en ejecutarse.
Llevo años intentando demostrar a MQ que estas funciones no deberían ser asíncronas, porque la tabla de propiedades del gráfico ya existe y basta con tomar esas propiedades y no tiene sentido ejecutar un proceso asíncrono.
Pero todos los esfuerzos son en vano.
Esto realmente apesta a idiotez.
Sé de lo que hablo, porque estoy programando en muchos lenguajes incluyendo Kanvas y usando activamente el modelo dirigido por eventos.
Y en el prehistórico MQL4 este problema es mínimo.

 
¿Tal vez no sea un error, sino una característica? Algo allí está optimizado con los gráficos. Y funciona mal porque es deseable utilizar todas las cosas para su propósito. Existen objetos gráficos especializados que están vinculados a las coordenadas de los precios, por lo que deben utilizarse para esta tarea.
 
Nikolai Semko:
Sí, tienes 4 funciones asíncronas que tardan el 99,76% de todo el tiempo.
...
Y en el MQL4 prehistórico este problema es mínimo.

Sí, todo vuela en MT4.

Nikolai Semko:
Intenta sacar estas funciones del bucle primero, entonces funcionará 30 veces más rápido. He implementado esto en iCanvas.

Si no te importa, ¿puedes darme un ejemplo de cómo hacerlo mejor?

 
Vitaliy Kuznetsov:

Sí, todo vuela en MT4.

Si no te importa, ¿puedes darme un ejemplo de cómo hacerlo de forma más inteligente?

El camino más corto es adjuntar mi bibliografía de iCanvas, que permitirá no usar funciones asíncronas en bucle.
No es necesario utilizar el propio lienzo. Estará colgado en su carta durante todo el tiempo, pero estará vacío y será transparente.
Si un camino más largo sin utilizar iCanvas, entonces usted tendrá que buscar en esta biblia de nuevo, ya que se implementa allí, pero no es para los débiles de corazón. :))
Intentaré explicar brevemente lo que hace iCanvas.
Tiene un manejador internoOnChartEvent, que capta el eventoCHARTEVENT_CHART_CHANGE y rellena su estructura interna W, usando todas las mismas funciones asíncronas ChartGet, pero lo hace sólo una vez con la ocurrencia de este evento.
Esto permite optimizar más o menos la velocidad.
También en esta librería cuelga el objeto Canvas, que estira toda la pantalla y se ajusta a ella cuando la pantalla cambia.


Aquí, por ejemplo, se han añadido tres líneas de código y se han descartado muchas líneas:


#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);
}
//+------------------------------------------------------------------+
En cualquier caso, puedes aumentar el número de objetos con este algoritmo y no afectará demasiado al rendimiento.
Archivos adjuntos:
iCanvas.mqh  52 kb
 
Dmitry Fedoseev:
¿O tal vez no sea un error sino una característica? Algo allí está optimizado con los gráficos. Y funciona mal porque es deseable utilizar todas las cosas para su propósito. Existen objetos gráficos especializados que están vinculados a las coordenadas de los precios, por lo que deberían utilizarse para esta tarea.

Yo también lo pensaba, hasta que adquirí más conocimientos sobre el tema.
La razón es inequívoca: la falta de pensamiento.
Alguna "autoridad" se equivocó al principio (hace unos 10 años) y nadie sigue pensando que pudo equivocarse.
https://www.mql5.com/ru/forum/1111/page2780#comment_16886162
Incluso parece que se pusieron de acuerdo y prometieron mejorarlo, pero no, se acabó. Todavía está ahí.
https://www.mql5.com/ru/forum/1111/page2781#comment_16904132


 
Nikolai Semko:

El camino más corto es adjuntar mi bibblet de iCanvas, que le permitirá evitar el uso de funciones asíncronas en el bucle.
No tienes que usar el lienzo en absoluto. Estará colgado en su carta durante todo el tiempo, pero estará vacío y será transparente.
Si un camino más largo sin utilizar iCanvas, entonces usted tendrá que buscar en esta biblia de nuevo, ya que se implementa allí, pero no es para los débiles de corazón. :))
Intentaré explicar brevemente lo que hace iCanvas.
Tiene un manejador internoOnChartEvent, que atrapa el eventoCHARTEVENT_CHART_CHANGE y rellena su estructura interna W, usando todas las mismas funciones asíncronas ChartGet, pero lo hace sólo una vez con la ocurrencia de este evento.
Esto permite optimizar más o menos la velocidad.
También en esta librería cuelga el objeto Canvas, que estira toda la pantalla y se ajusta a ella cuando la pantalla cambia.


Aquí, por ejemplo, se han añadido tres líneas de código y se han descartado muchas líneas:

En cualquier caso, puedes aumentar el número de objetos con este algoritmo y no afectará demasiado al rendimiento.

Gracias por una solución similar al problema. Efectivamente, la velocidad de renderizado ha aumentado. Parece que tendré que dominar la biblioteca.

También me gustaría precisar el siguiente matiz. Compila con esta formulación con una advertencia:

y = Canvas.Y(price);

Y se compila con este sin aviso, pero la velocidad es un poco más lenta.

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

¿Qué es más correcto?)

 
Vitaliy Kuznetsov:

Gracias por este tipo de solución al problema. Efectivamente, la velocidad de renderizado ha mejorado. Parece que tendré que aprender la biblioteca.

También me gustaría aclarar el siguiente matiz. Compila con esta formulación con una advertencia:

Y se compila con este sin aviso, pero la velocidad es un poco más lenta.

¿Qué es más correcto?)

¿Qué tipo de "y" tienes?

porque si int y objetivamente"la velocidad baja un poco" esto es una BOLSA

 
No estoy muy seguro del tipo Canvas.Y(precio);

qué tipo devuelve