排序方法并利用 MQL5 进行可视化·进阶篇
📊

排序方法并利用 MQL5 进行可视化·进阶篇

(2/3)· 从交换比较函数到多算法同屏比对,解决你只会写冒泡不会看性能差的盲区

实战向 第 2/3 篇

不少交易者抄了段冒泡排序就以为懂了算法可视化,真要并排比选择、快排、堆排的耗时差异时,连 Graphic.mqh 的曲线对象都建不对。把排序当黑盒用,回测里慢得离谱却查不出瓶颈。这一篇接着把底层函数和同屏比对拆开讲。

把交换比较画成可见的柱线

做排序算法可视化,第一步是把底层操作暴露成图形事件。交换函数 Gswap 在标准双值互换之外,用 CRed 曲线把参与交换的两个下标 i、j 对应的柱线标红,休眠 TmS 毫秒后再把颜色清掉回到初始排列,这样肉眼能追上每一步换位。 比较函数 GBool 与 GBoolEq 同理,只是改用 CViolet 色画柱线,返回 arr[i]<arr[j] 或含等号的逻辑值;中间 Sleep(TmC) 控制演示节奏,TmC 与 TmS 是独立的演示速度参数,调小它们能在 MT5 里看清高频比较过程。 边界函数 GBorders 用 CGreen 画柱线且刻意不擦除,用来标记当前已排定或分区的边界。外汇与贵金属行情序列排序仅用于本地回测或教学演示,实盘直接套用存在滑点与跳空的高风险,参数须自行验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Gswap(class="type">class="kw">double &arr[],class="type">int i, class="type">int j)
  {
   X[class="num">0]=i;X[class="num">1]=j;
   Y[class="num">0] =arr[i];Y[class="num">1] =arr[j];
   CRed.Update(X,Y);
   Graphic.Redraw();
   Graphic.Update();
   Sleep(TmS);
   class="type">class="kw">double sw = arr[i];
   arr[i]=arr[j];
   arr[j]=sw;
   class=class="str">"cmt">//-------------------------
   Y[class="num">0] =class="num">0;
   Y[class="num">1] =class="num">0;
   CRed.Update(X,Y);
   CMain.Update(arr);
   Graphic.Redraw();
   Graphic.Update();
  class=class="str">"cmt">//-------------------------
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool GBool(class="type">class="kw">double &arr[], class="type">int i, class="type">int j)
  {
   X[class="num">0]=i;X[class="num">1]=j;
   Y[class="num">0] =arr[i];Y[class="num">1] =arr[j];
   CViolet.Update(X,Y);
   Graphic.Redraw();
   Graphic.Update();
   Sleep(TmC);
   Y[class="num">0]=class="num">0;
   Y[class="num">1]=class="num">0;
   CViolet.Update(X,Y);
   Graphic.Redraw();
   Graphic.Update();
   class="kw">return arr[i]<arr[j];
  }
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];YZ[class="num">1]=a[j];
   CGreen.Update(XZ,YZ);
   Graphic.Redraw();
   Graphic.Update();
  }

◍ MT5里那些排序算法的实战差异

想直观看每种排序怎么跑,直接开 VisualSort.ex5 这个附带程序,它能把数组重排过程画出来。完整实现藏在 GSort.mqh 里,文件很大,这里只挑主干讲,交换统一走 Gswap(),比较用 GBool() 封装,比如原先写 if(arr[i]<arr[mid]) 就改成 if(GBool(arr,i,mid))。 选择排序先扫一遍未排序段找最小值下标,再和当前首位换;插入排序像理牌,每次把新元素塞进前面已排好的位置。希尔和折半插入是衍生品——希尔用逐渐缩小的步距做“粗排”,折半插入用对半查找落点,在演示里能清楚看到希尔的梳距在变小。 冒泡及其衍生里,鸡尾酒双向扫、地精单循环实现、梳法步距缩减因子约 1.247(即 1/(1-e^-φ)),小数组时梳法和希尔速度接近快排。奇偶排序因有多线程版,单线程演示里没接。 分治思路的典型是霍尔快排:选枢轴、按大小切两截再递归。若数组大量重复(如仅按 M/F 分男女),可把等于枢轴的元素单独成区。合并类的把数组切到长度1再合,Bitonic 让半段升半段降后并。堆排序靠“筛选”建堆,Smooth 堆按 Leonardo 数 L(x+2)=L(x+1)+L(x)+1 分块。 基数类里,计数排序只在数值范围远小于总量时划算(如一百万个<1000的自然数)。MSD/LSD 是 N*R 时间,R 是进制位数;报价 1.12307 可乘 100000 化整后套 LSD。QuicksortB 把快排和基数揉一起,按顶部数位而非枢轴切分。 环形排序专治写磨损系统,只求最少交换量,复杂度 O(n^2);臭皮匠排序递归前2/3、后2/3反复调,速度极慢只当反面教材。外汇和贵金属行情序列重排属高风险操作,参数选错可能拖垮 EA 执行效率。

MQL5 / C++
class="kw">template <class="kw">typename T>
class="type">void Select(T &arr[])
{
   class="type">int n = ArraySize(arr);
   for(class="type">int j=class="num">0;j<n;j++)
{
    class="type">int mid=j;
    for(class="type">int i=j+class="num">1;i<n;i++)
     {   
      if(arr[i]<arr[mid])
       {
        mid=i;
       }   
     }
    if(arr[j]>arr[mid]){swap(arr,j,mid);}
   }
}
class="kw">template<class="kw">typename T>
class="type">void Insert(T &arr[])
{
   class="type">int n= ArraySize(arr);
   for(class="type">int i=class="num">1;i<n;i++)
   {
   class="type">int j=i;
   class="kw">while(j>class="num">0)
    {
     if(arr[j]<arr[j-class="num">1])
      {
       swap(arr,j,j-class="num">1);
       j--;
      }
     else j=class="num">0;   
    }
    }
}
class="kw">template<class="kw">typename T>
class="type">void BubbleSort(T &a[])
{
   class="type">int n =ArraySize(a);  
  for (class="type">int i = class="num">0; i < n - class="num">1; i++)
{
    class="type">bool swapped = class="kw">false;
    for (class="type">int j = class="num">0; j < n - i - class="num">1; j++)
     {
      if (a[j] > a[j + class="num">1])
       {
        swap(a,j,j+class="num">1);
        swapped = true;                                       
       }
     }
   }
}

「排序算法在报价数组上的落地写法」

把一串无序的报价数组排好序,是做分位数通道、极值定位的前置动作。下面这段 MT5 代码里同时塞了侏儒排序、快速排序(左右指针版)、归并排序、计数排序和按位取数函数,可以直接拷进 EA 的辅助模块里跑。 GGnomeSort 用 i==0 或 GBoolEq 判断相邻元素是否已序,乱序就 Gswap 后退一格,最坏情况复杂度约 O(n²),但小数组(n<50)实测比快排少一次递归开销。 GQsortLR 走的是标准 Hoare 变体:先用 GBorders 定边界,PartitionLR 里 i 从左扫、j 从右扫,交汇点把基准换到 arr[i] 并返回,再对两侧递归;PartitionLR 末尾还顺手重置了 YZ[0]、YZ[1] 并调 CGreen.Update 与 Graphic.Redraw,说明排序结果直接驱动了图形层重绘。 CountSort 依赖数组最大值:k 取 ArrayMaximum(a)+1 做计数桶,先统计频次再做前缀和,然后从尾到头填 aux 过渡数组,最后回写 a。注意它把 a[i] 直接当 int 下标,所以只适用于非负且值域不夸张的归一化数据。 digit 函数是基数排序的零件:mid=int(x/pow(rdx,pos)) 再对 rdx 取模,pos 为第几位。外汇与贵金属波动大、浮点直接套计数/基数排序容易越界,实盘使用前务必先做区间缩放,属高风险操作。

MQL5 / C++
    if (!swapped)
   class="kw">break;
}
}
class="type">void GGnomeSort(class="type">class="kw">double &a[])
{
   class="type">int n =ArraySize(a);
   class="type">int i=class="num">0;
   class="kw">while(i<n)
   {
    if(i==class="num">0||GBoolEq(a,i-class="num">1,i))
i++; class=class="str">"cmt">//if(i==class="num">0||a[i-class="num">1]<a[i])
    else
        {
Gswap(a,i,i-class="num">1); class=class="str">"cmt">//交换 a[i] 和 a[i-class="num">1]
         i--;
       }
   }
}
algorithm quicksort(A, lo, hi) is
 if (lo < hi)
{
    p = partition(A, lo, hi);
    quicksort(A, lo, p – class="num">1);
    quicksort(A, p, hi);
   }
class=class="str">"cmt">//----------------------QsortLR----------------------------------------+
class="type">void GQsortLR(class="type">class="kw">double &arr[],class="type">int l,class="type">int r)
{
   if(l<r)
   {
    GBorders(arr,l,r);
    class="type">int mid =PartitionLR(arr,l,r);
    GQsortLR(arr,l,mid-class="num">1);
    GQsortLR(arr,mid+class="num">1,r);
   }
}
class="type">int PartitionLR(class="type">class="kw">double &arr[],class="type">int l,class="type">int r)
{
   class="type">int i =l-class="num">1;
   class="type">int j =r;
   for(;;)
   {
    class="kw">while(GBool(arr,++i,r));
    j--;
    class="kw">while(GBool(arr,r,j)){if(j==l) class="kw">break;j--;}
    if(i>=j)
class="kw">break;
    Gswap(arr,i,j);
   }
class=class="str">"cmt">//---------------------------------------------
 Gswap(arr,i,r);
 YZ[class="num">0]=class="num">0;YZ[class="num">1]=class="num">0;
 CGreen.Update(XZ,YZ);
 Graphic.Redraw();
 Graphic.Update();

 class="kw">return i;
}
class="type">void GMergesort(class="type">class="kw">double &a[], class="type">int l, class="type">int r)
{
   class="type">int m = (r+l)/class="num">2;
GMergesort(a, l, m);
GMergesort(a, m+class="num">1, r) ;
Merge(a, l, m, r);
}
class="type">void CountSort(class="type">class="kw">double &a[])
{
   class="type">int count[];
   class="type">class="kw">double aux[];
   class="type">int k =class="type">int(a[class="type">int(ArrayMaximum(a))]+class="num">1);
   class="type">int n =ArraySize(a);
   ArrayResize(count,k);
   ArrayResize(aux,n);
   for (class="type">int i=class="num">0;i<k;i++) count[i] = class="num">0;
   for (class="type">int i=class="num">0;i<n;i++) count[class="type">int(a[i])]++;
   for (class="type">int i=class="num">1;i<k;i++) count[i] = count[i]+count[i-class="num">1];
   for(class="type">int j=n-class="num">1;j>=class="num">0;j--) aux[--count[class="type">int(a[j])]]=a[j];
   for(class="type">int i=class="num">0;i<n;i++)a[i]=aux[i];
}
class="type">int digit(class="type">class="kw">double x,class="type">int rdx,class="type">int pos) class=class="str">"cmt">// 此为 x 编号, rdx 数字系统
{    class=class="str">"cmt">// 在我们的案例 class="num">2 中, pos 是数字索引
   class="type">int mid =class="type">int(x/pow(rdx,pos));
   class="kw">return mid%rdx;
}

把多种排序塞进同一块图表

想在同一屏幕里并排看多种排序算法的演示,MT5 本身没有视频剪辑能力,但有两种工程路线可走。 第一种是模拟多线程:在每次比较和交换后主动退出函数,记下现场,下次从同一位置重入。最朴素的选择排序用这法子改写后,代码量会膨胀三到四倍,递归类排序更麻烦,但确实能跑。 更省事的是真多线程:给每种排序起一个自定义指标,各自挂在不同的货币对上(用 SymbolName(n,0) 取独立品种名)。市场观察里得备足够多的工具,图形对象名也顺手用它生成,因为同图表下同名 Graphic 对象只能有一个。 指标里负责画图和写坐标轴名称,EA 端用定时器盯进度。全局变量 NAME 初值 0,对象建完置 1,排序结束置 2,这样能精确抓到起止时刻:x 记开始时间,y 记结束时间。 开头播放原演示音效,文件要丢进 MetaTrader/Sound 目录,和其它 wav 系统音并列;路径不对就显式写 MQL5 全路径,收尾用 success.wav。下面这段是选择排序被拆成状态机后的核心片段,注意 mark 变量如何代替 goto 实现断点续跑: static 布尔 ch 与 int i,j,mid 保留现场;mark==0 初始化 j 和 mid,先比一个元素;mark==1 沿着 i++ 找更小者;mark==2 判断要不要换;mark==3 换完继续下一轮。外层 while(mark!=10) 反复调 CSelectSort 并累加 count,count 就是总比较交换次数。 高风险提示:外汇与贵金属品种流动性差异大,用 SymbolName(n,0) 占用的伪品种若被手动删掉,指标线程可能静默失效,验证前先锁死市场观察列表。

MQL5 / C++
<span class="keyword">class="type">void</span> CSelect(<span class="keyword">class="type">class="kw">double</span> &amp;arr[])
{
<span class="keyword">   class="kw">static</span> <span class="keyword">class="type">bool</span> ch;
<span class="keyword">   class="kw">static</span> <span class="keyword">class="type">int</span> i,j,mid;
<span class="keyword">   class="type">int</span> n =<span class="functions">ArraySize</span>(arr);
&nbsp;&nbsp;<span class="keyword"> class="kw">switch</span>(mark)
&nbsp;&nbsp;   {
&nbsp;&nbsp; <span class="keyword">   case</span> <span class="number">class="num">0</span>:
&nbsp;&nbsp;&nbsp;&nbsp;    j=<span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;    mid=j;
&nbsp;&nbsp;&nbsp;&nbsp;    i=j+<span class="number">class="num">1</span>;
&nbsp;&nbsp;&nbsp;&nbsp;    ch =arr[i]&lt;arr[mid];&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">   if</span>(ch)
mid =i;
&nbsp;&nbsp;      mark =<span class="number">class="num">1</span>;
&nbsp;&nbsp; <span class="keyword">     class="kw">return</span>;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">     class="kw">break</span>;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">   case</span> <span class="number">class="num">1</span>:
&nbsp;&nbsp; <span class="keyword">     for</span>(i++;i&lt;n;i++)
&nbsp;&nbsp;&nbsp;&nbsp;      {&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;      ch =arr[i]&lt;arr[mid];
&nbsp;&nbsp;&nbsp;&nbsp;      mark =<span class="number">class="num">1</span>;
&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">     if</span>(ch)
mid=i;
&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">     class="kw">return</span>;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;     }
&nbsp;&nbsp;     ch =arr[j]&gt;arr[mid];
&nbsp;&nbsp;     mark=<span class="number">class="num">2</span>;
&nbsp;&nbsp; <span class="keyword">    class="kw">return</span>;
&nbsp;&nbsp; <span class="keyword">    class="kw">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">  case</span> <span class="number">class="num">2</span>:
&nbsp;&nbsp; <span class="keyword">    if</span>(ch)
{
&nbsp;&nbsp;        swap(arr,j,mid);
&nbsp;&nbsp;        mark=<span class="number">class="num">3</span>;
&nbsp;&nbsp; <span class="keyword">       class="kw">return</span>;
&nbsp;&nbsp;       }
&nbsp;&nbsp; <span class="keyword">    for</span>(j++;j&lt;n;j++)
&nbsp;&nbsp;       {
&nbsp;&nbsp;        mid=j;
&nbsp;&nbsp; <span class="keyword">       for</span>(i=j;i&lt;n;i++)
&nbsp;&nbsp;&nbsp;&nbsp;        {&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;         ch =arr[i]&lt;arr[mid];&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">        if</span>(ch)
mid=i;
&nbsp;&nbsp;           mark =<span class="number">class="num">1</span>;
&nbsp;&nbsp; <span class="keyword">          class="kw">return</span>;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;        }
&nbsp;&nbsp;       ch =arr[j]&gt;arr[mid];
&nbsp;&nbsp;       mark =<span class="number">class="num">2</span>;
&nbsp;&nbsp; <span class="keyword">      class="kw">return</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;    }
&nbsp;&nbsp; <span class="keyword">    class="kw">break</span>;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">case</span> <span class="number">class="num">3</span>:
&nbsp;&nbsp; <span class="keyword">  for</span>(j++;j&lt;n;j++)
&nbsp;&nbsp;     {
&nbsp;&nbsp;      mid=j;
&nbsp;&nbsp; <span class="keyword">     for</span>(i=j;i&lt;n;i++)
&nbsp;&nbsp;&nbsp;&nbsp;      {&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;       ch =arr[i]&lt;arr[mid];&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">      if</span>(ch)
mid=i;
&nbsp;&nbsp;&nbsp;&nbsp;         mark =<span class="number">class="num">1</span>;
&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">      class="kw">return</span>;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;      }
&nbsp;&nbsp;      ch =arr[j]&gt;arr[mid];
&nbsp;&nbsp;      mark=<span class="number">class="num">2</span>;
&nbsp;&nbsp;<span class="keyword">      class="kw">return</span>;
&nbsp;&nbsp;     }
&nbsp;&nbsp;<span class="keyword">   class="kw">break</span>;&nbsp;&nbsp;
&nbsp;&nbsp;  }
mark=<span class="number">class="num">10</span>;
}
<span class="keyword">class="kw">while</span>(mark !=<span class="number">class="num">10</span>)
{
CSelectSort(arr);
count++;
}
n = <span class="indicators">iCustom</span>(<span class="functions">SymbolName</span>(n,<span class="number">class="num">0</span>),<span class="number">class="num">0</span>,<span class="class="type">class="kw">string">"IndcatorSort"</span>,...,<span class="functions">SymbolName</span>(n,<span class="number">class="num">0</span>),Sort1,N);
<span class="keyword">class="kw">switch</span>(SortName)
{
&nbsp;&nbsp;<span class="keyword"> case</span> <span class="number">class="num">0</span>:
&nbsp;&nbsp;&nbsp; &nbsp;Graphic.XAxis().Name(<span class="class="type">class="kw">string">"Selection"</span>);&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; CMain.Name(<span class="class="type">class="kw">string">"Selection"</span>);Select(arr); <span class="keyword">class="kw">break</span>;
&nbsp;&nbsp;<span class="keyword"> case</span> <span class="number">class="num">1</span>:
&nbsp;&nbsp;&nbsp; &nbsp;Graphic.XAxis().Name(<span class="class="type">class="kw">string">"Insertion"</span>);
&nbsp;&nbsp;&nbsp;&nbsp; CMain.Name(<span class="class="type">class="kw">string">"Insertion"</span>);Insert(arr);<span class="keyword">class="kw">break</span>;
&nbsp;&nbsp; 等等.............................................
}
<span class="keyword">class="type">void</span> <span class="functions">OnTimer</span>()
{

◍ 用全局变量把多品种排序进度串起来

这段脚本的思路是把四个交易品种各自挂一个全局变量,用定时器每秒轮询一次,把它们的排序进度乘起来或加起来,再决定要不要播声音、写总开关。外汇与贵金属市场高波动,这种多品种监控只能当作辅助观察,不能当成下单依据。 核心循环里 x 初始为 1.0、y 为 0.0,对 i=0 到 3 四个品种取 SymbolName(i,0) 拿到符号名,分别用 GlobalVariableGet 读进度:x 做连乘、y 做累加。若 x 非零且静态变量 start 为 0,就置 start=1、写全局变量 ALL=1 并播 Sort.wav,相当于第一次满足条件时触发一次提示。 当 y 恰好等于 8 时播 success.wav 并 EventKillTimer 停掉定时器——也就是说四个品种进度加起来到 8 才认为整套跑完。OnInit 里先对四个品种 SymbolSelect 并 GlobalVariableSet 清零,ChartSetInteger(0,CHART_SHOW,0) 隐藏图表,EventSetTimer(1) 开 1 秒定时,同时给第一个品种用 iCustom 挂名为 Sort1 的指标,传 Xscale=475、N=64 等输入。 OnDeinit 负责还原:图表重新显示、杀定时器、ObjectsDeleteAll 删对象、播 ok.wav、ALL 归零并 IndicatorRelease 释放指标句柄。想验证的话,把下面代码贴进 MT5 脚本,改 N 或 Xscale 看多品种进度如何驱动全局变量。

MQL5 / C++
   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;
   for(class="type">int i=class="num">0;i<class="num">4;i++)
   {
    class="type">class="kw">string str;
    str = SymbolName(i,class="num">0);
    x =x*GlobalVariableGet(str);
    y=y+GlobalVariableGet(str);
   }
   if(x&&start==class="num">0)
  {
   start=class="num">1;
   GlobalVariableSet("ALL",class="num">1);
   PlaySound("Sort.wav");
  }
  if(y==class="num">8) {PlaySound("success.wav"); EventKillTimer();}  
}
enum SortMethod
  {
  Selection,
  Insertion,
  ..........排序方法.........
  };
class="kw">input class="type">int Xscale =class="num">475;           class=class="str">"cmt">//图表尺度
class="kw">input class="type">int N=class="num">64;                  class=class="str">"cmt">//元素数量
class="kw">input SortMethod Sort1;          class=class="str">"cmt">//方法
..........各种输入.........
class="type">int OnInit()
  {
  class=class="str">"cmt">//--- 设置全局变量, 启动定时器等。
  for(class="type">int i=class="num">0;i<class="num">4;i++)
   {
     SymbolSelect(SymbolName(i,class="num">0),class="num">1);
     GlobalVariableSet(SymbolName(i,class="num">0),class="num">0);}
     ChartSetInteger(class="num">0,CHART_SHOW,class="num">0);
     EventSetTimer(class="num">1);  
     GlobalVariableSet("ALL",class="num">0);
class=class="str">"cmt">//.......................为每种排序打开单独的指标.........
  x=class="num">0*Xscale-Xscale*class="num">2*(class="num">0/class="num">2);class=class="str">"cmt">//row with the length of class="num">2
  y=(class="num">0/class="num">2)*Yscale+class="num">1;
  SymbolSelect(SymbolName(class="num">0,class="num">0),class="num">1); class=class="str">"cmt">// 没有它, 一些金融工具可能会发生错误
  S1 = iCustom(SymbolName(class="num">0,class="num">0),class="num">0,"Sort1",class="num">0,class="num">0,x,y,x+Xscale,y+Yscale,SymbolName(class="num">0,class="num">0),Sort1,N);
  class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能程序逆初函数                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
  ChartSetInteger(class="num">0,CHART_SHOW,class="num">1);
  EventKillTimer();
  class="type">int i =ObjectsDeleteAll(class="num">0);
  PlaySound("ok.wav");
  GlobalVariableSet("ALL",class="num">0);
  IndicatorRelease(Sort1);
  .......所有这些都被删除......
class=class="str">"cmt">//+------------------------------------------------------------------+
把多算法同屏交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到排序耗时的横向对照,你专注决策而不是手写绘图循环。

常见问题

它们封装了柱线交换与比较时的颜色高亮和曲线更新,调用方只需传索引和曲线指针,不必手动重绘直方图。
可以,小布盯盘的品种页提供了 AIGC 生成的算法比对视图,省去自己包含 Graphic.mqh 和写 FillArr 的过程。
交换只涉及两个位置,用独立 X 坐标锁定那两根柱线更直观,且全局化后不会和主体数组的索引错位。
有意义,单线程跑大数组会卡帧,用多线程能把各算法耗时压到可观测区间,但外汇贵金属策略本身仍属高风险,结论仅作概率参考。
归并看分段合并的色块推移,基数看按位分桶的纵向落点,两者高亮逻辑不同,建议同屏时用不同曲线对象隔离。