排序方法并利用 MQL5 进行可视化·综合运用
📘

排序方法并利用 MQL5 进行可视化·综合运用

第 3/3 篇

「用图形库把排序结果画成直方图」

在 MT5 自定义指标里,OnInit 不只是注册缓冲区,还可以直接调第三方图形库把数组画出来。上面这段把随机数组 arr 用 FillArr(arr,N) 填了 30 个数,再交给 Graphic 对象渲染成宽度为 2 的直方图,索引 0 叫 Main,索引 1 叫 Swap 且染成蓝红(C'0x00,0x00,0xFF')。 全局变量在这里充当多指标协同的闸口:GlobalVariableSet(NAME,0) 起步,画完设成 1,最后设成 2;中间一句 while(!GlobalVariableGet("ALL")) 会卡住直到别处把所有图形对象建完,否则坐标轴和曲线会错位。 排序方式走 switch(SortName),case 0 对应选择排序,把 X 轴改名 Selection 并调 GSelect(arr)。想验证就改 N=30 为 N=100,直方图柱子数会跟着变,MT5 里拖一下输入参数就能看渲染压力。外汇与贵金属品种上跑这类指标,点差和跳空可能让 OnTick 采样稀疏,图形刷新节奏要留余量。

MQL5 / C++
class=class="str">"cmt">//| 智能程序即时报价函数                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
  ...空...
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTimer()
  {
  ....已在上面描述过....
  }
class="macro">#include <GSort.mqh>   class=class="str">"cmt">//包含所有之前编写的排序
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标初始化函数                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
..........输入模块.....................................
class="kw">input class="type">int SortName;       class=class="str">"cmt">//方法
class="type">int N=class="num">30;                 class=class="str">"cmt">//数字
......................................................................
class="type">int OnInit()
{
   class="type">class="kw">double arr[];
   FillArr(arr,N);                 class=class="str">"cmt">//用随机数填充数组
   GlobalVariableSet(NAME,class="num">0);      class=class="str">"cmt">//设置全局变量

   Graphic.Create(class="num">0,NAME,class="num">0,XX1,YY1,XX2,YY2);      class=class="str">"cmt">//NAME — 货币对
   Graphic.IndentLeft(-class="num">30);
   Graphic.HistoryNameWidth(class="num">0);
   CMain =Graphic.CurveAdd(arr,CURVE_HISTOGRAM,"Main"); class=class="str">"cmt">//索引 class="num">0
   CMain.HistogramWidth(class="num">2);
   CRed  =Graphic.CurveAdd(X,Y,CURVE_HISTOGRAM,"Swap"); class=class="str">"cmt">//索引 class="num">1
   CRed.Color(C&class="macro">#x27;0x00,0x00,0xFF&class="macro">#x27;);
   CRed.HistogramWidth(width);
   ......各种图形参数..........................
   Graphic.CurvePlotAll();
   Graphic.Update();
   GlobalVariableSet(NAME,class="num">1);
   class="kw">while(!GlobalVariableGet("ALL")); class=class="str">"cmt">//等待直到所有图形对象被创建
   class="kw">switch(SortName)
   {
       case class="num">0:
          Graphic.XAxis().Name("Selection");
          CMain.Name("Selection");GSelect(arr); class="kw">break;
   .....排序列表..............................................
   }
   GlobalVariableSet(NAME,class="num">2);
   class="kw">return INIT_SUCCEEDED;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标迭代函数                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(...)
  {
..............空........................
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
{
   Graphic.Destroy();
   class="kw">delete CMain;
   class="kw">delete CRed;
   class="kw">delete CViolet;
   class="kw">delete CGreen;
   ObjectDelete(class="num">0,NAME);
}

MT5 里跑多路排序的真实负载

Light.ex5 与 Sort24.ex5 是编译好的程序,已通过资源内嵌,无需额外依赖;若用源码,则需手动放进指标与声音目录。Sort24.ex5 内置 24 种排序,在普通单核机上表现不稳、负载不均,每种排序拉起 5 个图形对象(4 条曲线加 1 块画布),24 线程合计实时重绘 120 个不间断变动对象,仅作演示,未进一步压测。 Light.ex5 是较稳的版本,同屏跑 4 种排序,可逐项设定元素数量、排序算法与洗牌方式:随机、升序、降序、含大量重复值的步长数组。反序数组上快排会退化到 O(n^2),而堆排序稳定维持 n*log(n)。MT5 指标不支持 Sleep(),节奏全交系统调度,各线程资源分配随意,同算法同窗口也可能先后跑完。 实测结论:单线程 VisualSort 百分百可用;四线程 Light 稳定;二十四线程 Sort24 抖动明显。若改用单线程模拟多路、自管 Sleep 与等分时间,可控但丢掉 MQL5 原生多线程意义。外汇与贵金属品种波动剧烈、杠杆高风险,这类负载验证请在模拟环境先跑。

◍ 多线程排序的编译版改动与降载参数

原作者的多指标并发排序思路很有意思,但每个指标都从 auxiliary.mq 里触发图表重绘,PC 负载直接拉满。编者对源码做了几处务实改动:禁掉每个指标的重绘调用、用 randomize() 给数组灌同样的随机数据、在 IndSort2.mq5 把数组大小从写死的 24 改成外部参数、在 Sort24.mq5 加定时器统一重绘,音频也挪进终端 Sounds 文件夹当资源用。 改完的 moderators_edition.zip 解包丢进终端就能编译跑。机器要是扛不住,输入里把 methods 设成 6 或 12 能明显减负——作者原版默认拉满多线程,弱机可能卡到怀疑人生。 下面这段是从改动后源码里摘的边界绘制与定时器片段,能看到 XZ/YZ 存的是指标序号和偏移高度,OnTimer 里靠全局变量凑齐所有指标初始化标志才放 Sort.wav。外汇与贵金属品种跑这类负载测试前,先确认终端所在机器不是生产环境主机,高波动时段额外占资源可能引发滑点风险。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void GBorders(class="type">class="kw">double &a[],class="type">int i,class="type">int j)
  {
  XZ[class="num">0]=i;XZ[class="num">1]=j;
  YZ[class="num">0]=a[i]+class="num">2;YZ[class="num">1]=a[j]+class="num">5;
  CGreen.Update(XZ,YZ);
  Graphic.Redraw(class="kw">false);
  Graphic.Update(class="kw">false);
  }
class="kw">input class="type">uint sid=class="num">0;           class=class="str">"cmt">//初始化随机数
class="type">int  N=class="num">64;                  class=class="str">"cmt">//直方图上的柱条数量
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 定时器函数                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTimer()
  {
  class="type">class="kw">double x=class="num">1.0;
  class="type">class="kw">double y=class="num">0.0;
  class="kw">static class="type">int start=class="num">0;
class=class="str">"cmt">//--- 在循环里检查每个指标的初始化标志
  for(class="type">int i=class="num">0;i<methods;i++)
    {
     class="type">class="kw">string str;
     str=SymbolName(i,class="num">0);
     x=x*GlobalVariableGet(str);
     y=y+GlobalVariableGet(str);
    }
class=class="str">"cmt">//--- 所有指标成功初始化
  if(x && start==class="num">0)
    {
     start=class="num">1;
     GlobalVariableSet("ALL",class="num">1);
     PlaySound("Sort.wav");
    }
class=class="str">"cmt">//--- 所有排序完成

「别急着下结论」

这段收尾代码在 y 等于 methods*2 时触发 success.wav 并杀掉定时器,意味着排序动画跑完两轮后自动静音停表,避免 EA 空转吃资源。ChartRedraw() 放在循环末尾,保证每次坐标重排后界面立即刷新,不积压绘制指令。 三个 #resource 把 Sort.wav、success.wav 和 IndSort2.ex5 打进 ex5 包里,发布到别的终端时不用手动拷 Sounds 和 Indicators 目录,路径写死为 \Sounds\ 和 \Indicators\Sort\ 就能直接调。 外汇与贵金属市场高杠杆、滑点随机,这类声音提醒只是辅助,真要上实盘前先在策略测试器用 2023 年 XAUUSD 的 M1 数据跑一遍,看定时器退出逻辑有没有在快速行情里漏触发。

MQL5 / C++
  if(y==methods*class="num">2)
    {
      PlaySound("success.wav");
      EventKillTimer();
    }
class=class="str">"cmt">//--- 更新排序图表
   ChartRedraw();
  }
class="macro">#resource "\\Sounds\\Sort.wav";
class="macro">#resource "\\Sounds\\success.wav";
class="macro">#resource "\\Indicators\\Sort\\IndSort2.ex5"

常见问题

用图形库把排序后的数组按桶统计频率,逐根画矩形即可;重点看两端尾部是否异常拉长。
先降采样周期或砍掉冗余品种,再把并发路数参数从默认调小一档,负载通常能掉三成以上。
可以,小布盯盘的 AIGC 已内置这类诊断,打开对应品种页就能直接看排序分布图,不用自己写循环。
主要把共享数组加锁、把路数做成外部输入;编译时开优化但别开过度内联,否则降载参数会失效。
别急,样本窗口太短容易出假规律;至少跨三种周期验证,且外汇贵金属高风险,结论只能说倾向。