如何使XY绘制的对象平稳变化(MT4 vs MT5)

 

帮助解决MT5终端上的一个问题。

决定将我的产品从mql4转移到mql5。

它使用在XY坐标上绘制的矩形。

在MT4中,当改变垂直比例时,它是非常平滑的,没有任何卡顿,但在MT5中,同样的方法会导致一些冻结和明显的 "不平滑 "现象。

我专门做了一个简化的原型来演示效果。这对MT4和MT5是一样的。比较一下你改变垂直刻度时的差异(用鼠标在价格刻度上)。


在MT5中,一切都将没有刹车,但重新绘制时却很生硬。对象越多,情况就越糟。但在MT4中,一切都很顺利。

我附上mq4和mq5的源文件,并粘贴mq5代码。


请帮助我使其顺利进行。


我想了解:"这是 "MT5或 "这是 "我的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);}
附加的文件:
 
Vitaliy Kuznetsov:

帮助解决MT5终端上的一个问题。

决定将我的产品从mql4转移到mql5。

它使用在XY坐标上绘制的矩形。

在MT4中,当改变垂直比例时,它是非常平滑的,没有任何卡顿,但在MT5中,同样的方法会导致一些冻结和明显的 "不平滑 "现象。

我专门做了一个简化的原型来演示效果。这对MT4和MT5是一样的。比较一下你改变垂直刻度时的差异(用鼠标在价格刻度上)。


在MT5中,一切都将没有刹车,但重新绘制时却很生硬。对象越多,情况就越糟。但在MT4中,一切都很顺利。

我附上mq4和mq5的源文件,并粘贴mq5代码。


请帮助我使其顺利进行。


我想了解:是 "这样那样 "的MT5还是 "这样那样 "的我的MT5的代码。


我还没有研究和运行这段代码,因为我不在电脑前,但首先吸引我注意的是两个异步函数ChartXYToTimePrice 和ChartTimePriceToXY。
他们的速度慢得不切实际。在这个问题上,我和MQ争论了很久,但完全没有理会。
我记得每个函数的执行时间是~16毫秒,即在循环30中,需要30*2*16=~1秒。
没有平稳性甚至是不可能的。
首先尝试把这些函数从循环中拿出来,那么它的工作速度会快30倍。我在iCanvas中实现了这一点。
 
分析器 中检查这一点。
 
嗯,是的,你已经遇到了4个异步函数,需要99.76%的时间来执行。
,我多年来一直试图向MQ证明,这些函数不应该是异步的,因为图表属性表已经存在,只要取这些属性就足够了,运行异步进程没有意义。
但所有的努力都是徒劳的。
,这真的充满了白痴的味道。
我知道我在说什么,因为我在用很多语言编程,包括Kanvas,并积极使用事件驱动模型。
而在史前的MQL4中,这个问题是最小的。

 
也许这不是一个错误,而是一个特点?那里的东西是用图形优化的。而且效果很差,因为把所有的东西都用于它们的预期目的是可取的。有专门的图形对象与价格坐标 相联系--所以应该用它们来完成这项任务。
 
Nikolai Semko:
是的,你有4个异步函数,需要99.76%的时间。 ...而在史前的MQL4中,这个问题是最小的。 。



是的,一切都在MT4中飞翔。

Nikolai Semko:
试着先把这些函数从循环中取出来,那么它的工作速度会快30倍。我已经在iCanvas中实现了这一点。

如果你不介意的话,你能给我一个例子,说明如何做得更好吗?

 
Vitaliy Kuznetsov:

是的,一切都在MT4中飞翔。

如果你不介意的话,你能给我一个例子,说明如何更聪明地做这件事吗?

最简单的方法是附上我的iCanvas biblue,这将允许不在循环中使用异步函数。
你不一定要使用画布本身。它将一直挂在你的图表中,但它将是空的和透明的。
如果不使用iCanvas的较长方法,那么你必须在这本圣经中再次查找,因为它是在那里实现的,但这并不适合胆小的人。:))
我将尝试简单解释一下iCanvas的作用。
它有内部处理程序OnChartEvent,它捕捉CHARTEVENT_CHART_CHANGE 事件并填充其内部结构W,使用所有相同的异步ChartGet函数,但只在该事件发生时做一次。
这允许或多或少地优化速度。
这个库中还挂着Canvas对象,它拉伸整个屏幕并在屏幕变化时进行调整。


例如,这里增加了三行代码,丢弃了许多行。


#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);
}
//+------------------------------------------------------------------+
在任何情况下,你可以用这种算法增加对象的数量,而且不会对性能产生太大影响。
附加的文件:
iCanvas.mqh  52 kb
 
Dmitry Fedoseev:
或者说,这不是一个错误,而是一个特点?那里的东西是用图形优化的。而且效果很差,因为把所有的东西都用于它们的预期目的是可取的。有专门的图形对象与价格坐标 相联系--所以应该用它们来完成这项任务。

我以前也这么认为,直到我获得了更多的相关知识。
,原因很明确--思考不足。
一些 "权威人士 "在开始时(大约10年前)犯了一个错误,没有人还认为他可能犯了一个错误。
https://www.mql5.com/ru/forum/1111/page2780#comment_16886162
他们甚至似乎已经同意并承诺改进它,但没有--它已经结束。它还在那里。
https://www.mql5.com/ru/forum/1111/page2781#comment_16904132


 
Nikolai Semko:

最短的方法是附加我的iCanvas bibblet,这将使你避免在循环中使用异步函数。
你根本不需要使用画布本身。它将一直挂在你的图表中,但它将是空的和透明的。
如果不使用iCanvas而采用较长的方式,那么你必须在这本圣经中再次查找,因为它是在那里实现的,但这并不适合胆小的人。:))
我将尝试简单解释一下iCanvas的作用。
它有内部处理程序OnChartEvent,它捕捉CHARTEVENT_CHART_CHANGE 事件并填充其内部结构W,使用所有相同的异步ChartGet函数,但只在该事件发生时做一次。
这允许或多或少地优化速度。
这个库中还挂着Canvas对象,它拉伸整个屏幕并在屏幕变化时进行调整。


例如,这里增加了三行代码,丢弃了许多行。

在任何情况下,你可以用这种算法增加对象的数量,而且不会对性能产生太大影响。

谢谢你对问题的类似解决。事实上,渲染速度已经提高了。看来我得掌握图书馆了。

我还想具体说明以下细微差别。它用这种配方编译时有一个警告。

y = Canvas.Y(price);

而用这个编译时没有警告,但速度有点慢。

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

什么是更正确的?)

 
Vitaliy Kuznetsov:

谢谢你提供这种解决问题的办法。确实,渲染速度得到了改善。看来我得学习图书馆了。

我还想澄清以下细微差别。它用这种配方编译时有一个警告。

而用这个编译时没有警告,但速度有点慢。

什么是更正确的?)

你的 "Y "是什么类型?

因为如果客观地说,"速度下降了一点",这是一个BAG。

 
我不太清楚Canvas.Y(price)是什么类型。

它返回什么类型
原因: