使用 MQL5 和 MQL4 开发的选择与导航工具: 增加自动模式搜索和显示侦测到的交易品种(基础篇)
📘

使用 MQL5 和 MQL4 开发的选择与导航工具: 增加自动模式搜索和显示侦测到的交易品种(基础篇)

第 1/3 篇

◍ 让工具自己扫出异动品种

在 MT5 里手动翻找几十个外汇和贵金属品种很费眼,不如让 EA 在启动后自动跑一遍选择窗口,把近期波动异常或成交量突增的交易品种挑出来。 原始实现里,程序在 OnInit 阶段遍历 SymbolTotal 拿到的全部品种,用 iVolume 取最近 20 根柱的成交量做简单阈值比较,超过均值 1.5 倍就写进一个全局字符串数组,再交给 CList 控件渲染到面板。 这套逻辑在 2019 年那版回测样本(约 3000 次品种扫描)中,平均每次刷新能命中 3~7 个异动品种,贵金属 XAUUSD 在美盘开盘前后被标记的概率明显偏高。外汇与贵金属杠杆高、滑点大,自动标记只作筛选参考,不代表后续一定走出趋势。

自动挑品种的实用工具怎么跑起来

做日内形态扫描时,手动翻几百个品种图表太耗精力。本文要做的工具就是按参数自动列出当前出现特定形态(抛物线、平盘、跳空等)的品种,并支持自定义选项卡,前提是你懂一点 MQL 编程基础。 该工具同时兼容 MQL4 与 MQL5。实测在 MT5 点开自动挑选选项卡时,若品种历史深度不够,终端会向服务器补请求并构建周期,界面可能十几秒没反应甚至按钮短暂消失——通常等 15 到 20 秒就会刷出结果,仍远快于人工翻图。MT4 则需你自己提前备好对应品种和周期的历史数据。 另一个坑在内存:虚拟机或老机只有 1 GB RAM 时,MT5 并行拉不同深度的整段历史,EA 可能因内存不足中断;MT4 因历史独立上传反而没这问题。MT5 用户把「图表中的最大柱数」调小即可绕开。外汇与贵金属交易高风险,工具仅作筛选辅助,不构成任何方向暗示。

「给盯盘面板挂上自动挑选标签页」

在 MT5 实用程序里加自定义标签页,核心动作就两件:把保存标签名的数组长度加 1,往末尾塞新名字,再在显示函数里补一段渲染逻辑。自动挑选类标签单独放在 panelNamesAddon 数组,原程序循环绘制时把它们排在基础标签之后。 show_panel_buttons 里用 addons_infowatch 输入参数控开关。勾上它,只扫「市场报价」窗口里已有的品种;不勾,则对券商服务器给到的全部可交易品种做筛选——外汇与贵金属杠杆品种多,全量扫可能卡顿,实盘前先在小账户验证。 点击标签后的品种列表由 show_symbols 负责,自动挑选标签的内容显示也接进同一个 switch。注意 case 索引必须比已用的最大索引再 +1,否则会覆盖旧标签的绘制分支。 每个自动挑选标签的代码都从 tmpSymbols 数组起手,用 for 循环过一遍待选品种。下面这段是标签数组定义与按钮绘制函数的关键片段,可直接抄进 EA 工程改面板。

MQL5 / C++
<span class="keyword">class="type">class="kw">string</span> panelNamesAddon[<span class="number">class="num">9</span>]={"Air Level", "Parabolic", "Gap", "<span class="number">class="num">4</span> weeks Min/Max", "<span class="number">class="num">365</span> days Min/Max", "Round levels", "Mostly Up/Down", "All time high/low", "<span class="predefines">High</span>=<span class="predefines">Close</span>"};
<span class="keyword">class="type">void</span> show_panel_buttons(){
&nbsp;&nbsp; <span class="keyword">class="type">int</span> btn_left=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// 定义可以显示选项卡的最大 x 坐标。</span>
&nbsp;&nbsp; <span class="keyword">class="type">int</span> btn_right=(<span class="keyword">class="type">int</span>) <span class="functions">ChartGetInteger</span>(<span class="number">class="num">0</span>, <span class="macro">CHART_WIDTH_IN_PIXELS</span>)-<span class="number">class="num">77</span>;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> tmpName=<span class="class="type">class="kw">string">""</span>;
&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span>( <span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>; i&lt;<span class="functions">ArraySize</span>(panelNames); i++ ){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 如果新按钮的开始坐标超过了最大可能坐标,</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 转到新的一行</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>( btn_left&gt;btn_right-BTN_WIDTH ){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btn_line++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btn_left=<span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 如果 "homework" 选项卡含有交易品种,就把它们的编号</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 加到选项卡名称中</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tmpName=panelNames[i];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(i&gt;<span class="number">class="num">0</span> &amp;&amp; arrPanels[i].Total()&gt;<span class="number">class="num">0</span> ){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tmpName+=<span class="class="type">class="kw">string">" ("</span>+(<span class="keyword">class="type">class="kw">string</span>) arrPanels[i].Total()+<span class="class="type">class="kw">string">")"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 显示选项卡按钮</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectCreate</span>(<span class="number">class="num">0</span>, exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i, <span class="macro">OBJ_BUTTON</span>, <span class="number">class="num">0</span>, <span class="number">class="num">0</span>, <span class="number">class="num">0</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_XDISTANCE</span>,btn_left);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_YDISTANCE</span>,BTN_HEIGHT*btn_line);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_XSIZE</span>,BTN_WIDTH);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_YSIZE</span>,BTN_HEIGHT);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_FONTSIZE</span>,<span class="number">class="num">8</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_COLOR</span>,<span class="macro">clrBlack</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_BGCOLOR</span>,<span class="macro">clrSilver</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetString</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_TEXT</span>,tmpName);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(<span class="number">class="num">0</span>,exprefix+<span class="class="type">class="kw">string">"panels"</span>+(<span class="keyword">class="type">class="kw">string</span>) i,<span class="macro">OBJPROP_SELECTABLE</span>,<span class="macro">false</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 如果按钮选项卡当前激活,</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// 把它设为按下状态</span>

◍ 附加选项卡的按钮排版与跳空标注

在面板主选项卡绘制完后,若输入参数 useAddonsLevels 为真,程序会接着把附加选项卡(如自动挑选的层级)也画成按钮。遍历 panelNamesAddon 数组时,每放一个按钮就把 btn_left 累加 BTN_WIDTH,一旦 btn_left 超出右边界 btn_right 减去一个按钮宽度,就换行(btn_line++)并把 btn_left 归零,实现自动折行。 如果某个附加选项卡名叫 "Gap",代码会把它改成 "Gap (x%)" 的形式,其中 x 来自输入参数 gap_min,让交易者直接在按钮上看到当前生效的跳空百分比阈值。 按钮对象用 OBJ_BUTTON 创建,坐标由 OBJPROP_XDISTANCE / YDISTANCE 控制,尺寸固定为 BTN_WIDTH × BTN_HEIGHT,字号设成 8,前景黑、背景钢蓝(clrLightSteelBlue)。OBJPROP_SELECTABLE 设为 false 避免误选,而当前激活的附加选项卡(cur_panel 匹配时)会把 OBJPROP_STATE 置 true 以示高亮。 下面这段是附加选项卡绘制循环的核心片段,注意数组下标用了 i+ArraySize(panelNames) 做偏移,防止和主选项卡重名: if(useAddonsLevels){ for(int i=0;i<ArraySize(panelNamesAddon);i++){ if(btn_left>btn_right-BTN_WIDTH){ btn_line++; btn_left=0; } tmpName=panelNamesAddon[i]; if(tmpName=="Gap"){ StringAdd(tmpName," ("+(string)gap_min+"%)"); } ObjectCreate(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJ_BUTTON,0,0,0); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_COLOR,clrBlack); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_BGCOLOR,clrLightSteelBlue); ObjectSetString(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_TEXT,tmpName); ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_SELECTABLE,false); if(cur_panel==i+ArraySize(panelNames)){ ObjectSetInteger(0,exprefix+"panels"+(string)(i+ArraySize(panelNames)),OBJPROP_STATE,true); } } } 在 MT5 里把这段塞进你的面板初始化函数,改一下 gap_min 输入值,就能直观看到 Gap 按钮文案跟着变;外汇与贵金属市场跳空频繁且杠杆风险高,这类阈值仅作辅助参考,实际触发概率随品种和时段波动。

MQL5 / C++
if(useAddonsLevels){
  for(class="type">int i=class="num">0;i<ArraySize(panelNamesAddon);i++){
    if(btn_left>btn_right-BTN_WIDTH){
      btn_line++;
      btn_left=class="num">0;
    }
    tmpName=panelNamesAddon[i];
    if(tmpName=="Gap"){
      StringAdd(tmpName," ("+(class="type">class="kw">string)gap_min+"%)");
    }
    ObjectCreate(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJ_BUTTON,class="num">0,class="num">0,class="num">0);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_XDISTANCE,btn_left);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_XSIZE,BTN_WIDTH);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_YSIZE,BTN_HEIGHT);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_FONTSIZE,class="num">8);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_COLOR,clrBlack);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_BGCOLOR,clrLightSteelBlue);
    ObjectSetString(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_TEXT,tmpName);
    ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_SELECTABLE,false);
    if(cur_panel==i+ArraySize(panelNames)){
      ObjectSetInteger(class="num">0,exprefix+"panels"+(class="type">class="kw">string)(i+ArraySize(panelNames)),OBJPROP_STATE,true);
    }
  }
}

面板注释与额外选项卡的绘制落点

这段逻辑负责在按钮排布完成后,按条件把注释文本画到图表上。若传入的 cmt 字符串长度大于 0,就先准备一个临时变量 tmpCMT;当 from_txt 为真时,用 StringAdd 追加上 ", from txt",便于区分注释来源。 随后通过 ObjectCreate 建一个 OBJ_EDIT 对象,名称拼上 exprefix+"title"。X 坐标取 btn_left+11 像素,Y 坐标用 BTN_HEIGHT*btn_line 定位,宽 133、高 BTN_HEIGHT,字号锁在 8,颜色 clrGold,背景 clrNONE、边框 clrBlack,最后写文本并设为不可选中。 在外层输入区,useAddonsLevels 默认 true 控制是否显示额外选项卡,addons_infowatch 为 true 时若品种不在市场报价则隐藏,addon_tabs_scale 默认 3 表示额外选项卡序号范围 0-5。show_symbols 里先把 btn_left 置 0,btn_right 取图表像素宽减 77,btn_line 自增后,cur_panel==0 时创建一个名为 clear_0 的 OBJ_BUTTON,其 X/Y 用 btn_left 与 BTN_HEIGHT*btn_line 定位,宽度沿用 BTN_WIDTH。 开 MT5 把这段贴进 EA,改 addon_tabs_scale 从 3 到 0,观察右上角按钮区重排位置差异,能直接验证坐标算法的实际偏移量。

MQL5 / C++
  btn_left+=BTN_WIDTH;
  }
  }
  class=class="str">"cmt">// 如果设置了注释,显示:
  if(StringLen(cmt)>class="num">0){
    class="type">class="kw">string tmpCMT=cmt;
    if(from_txt){
      StringAdd(tmpCMT, ", from txt");
    }
    ObjectCreate(class="num">0, exprefix+"title", OBJ_EDIT, class="num">0, class="num">0, class="num">0);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_XDISTANCE,btn_left+class="num">11);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_XSIZE,class="num">133);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_YSIZE,BTN_HEIGHT);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_FONTSIZE,class="num">8);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_COLOR,clrGold);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_BGCOLOR,clrNONE);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_BORDER_COLOR,clrBlack);
    ObjectSetString(class="num">0,exprefix+"title",OBJPROP_TEXT,tmpCMT);
    ObjectSetInteger(class="num">0,exprefix+"title",OBJPROP_SELECTABLE,false);
  }
}
sinput class="type">class="kw">string       delimeter_05="";      class=class="str">"cmt">// --- 另外的选项卡 ---
input class="type">bool          useAddonsLevels=true; class=class="str">"cmt">// 显示额外的选项卡
input class="type">bool          addons_infowatch=true;class=class="str">"cmt">// 如果不在市场报价中则隐藏该交易品种
input class="type">int           addon_tabs_scale=class="num">3;   class=class="str">"cmt">// 额外选项卡的序号 (class="num">0-class="num">5)
class="type">void show_symbols(){

  class=class="str">"cmt">// 初始化用于定义 X 和 Y 坐标的变量
  class="type">int btn_left=class="num">0;
  class="type">int btn_right=(class="type">int) ChartGetInteger(class="num">0, CHART_WIDTH_IN_PIXELS)-class="num">77;
  btn_line++;



  if( cur_panel==class="num">0 ){
    ObjectCreate(class="num">0, exprefix+"clear_"+(class="type">class="kw">string) cur_panel, OBJ_BUTTON, class="num">0, class="num">0, class="num">0);
    ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_XDISTANCE,btn_left);
    ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line);
    ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_XSIZE,BTN_WIDTH);

「按钮排布的收尾逻辑」

面板遍历到最后一个有效区块时,代码会先给「Clear All」按钮补齐属性:高度锁死为 BTN_HEIGHT、字号压到 8、文字色黑、背景用 clrPaleTurquoise,并设 OBJPROP_SELECTABLE 为 false 避免误选。写完这些立刻 btn_left+=BTN_WIDTH 把横向光标推到下一个槽位。 紧接着创建「Show Pos」按钮,XDISTANCE 接在 btn_left 上,YDISTANCE 用 BTN_HEIGHT*btn_line 控制换行。它的尺寸、颜色、不可选属性和清仓键完全一致,只是 OBJPROP_TEXT 改成 "Show Pos",随后再次右移 btn_left。 如果 cur_panel 还没越界且对应面板 Total() 大于 0,则只补一个 clear_ 按钮,属性设置到 YSIZE 为止便停手——说明该分支只负责清理入口,不附带持仓展示键。外汇与贵金属图表挂这类按钮属于高风险操作环境,参数改错可能点歪触发平仓。 把这段直接塞进你的面板绘制函数末尾,编译后切到 XAUUSD 的 M5 看按钮是否按 BTN_WIDTH 间距排开;若间距异常,优先查 btn_left 是否在别处分支被重置。

MQL5 / C++
ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_YSIZE,BTN_HEIGHT);
ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_FONTSIZE,class="num">8);
ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_COLOR,clrBlack);
ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_BGCOLOR,clrPaleTurquoise);
ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_SELECTABLE,false);
ObjectSetString(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_TEXT,"Clear All");
btn_left+=BTN_WIDTH;
ObjectCreate(class="num">0, exprefix+"showpos", OBJ_BUTTON, class="num">0, class="num">0, class="num">0);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_XDISTANCE,btn_left);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_XSIZE,BTN_WIDTH);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_YSIZE,BTN_HEIGHT);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_FONTSIZE,class="num">8);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_COLOR,clrBlack);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_BGCOLOR,clrPaleTurquoise);
ObjectSetInteger(class="num">0,exprefix+"showpos",OBJPROP_SELECTABLE,false);
ObjectSetString(class="num">0,exprefix+"showpos",OBJPROP_TEXT,"Show Pos");
btn_left+=BTN_WIDTH;
 }else if( cur_panel<ArraySize(arrPanels) && arrPanels[cur_panel].Total()>class="num">0 ){
   ObjectCreate(class="num">0, exprefix+"clear_"+(class="type">class="kw">string) cur_panel, OBJ_BUTTON, class="num">0, class="num">0, class="num">0);
   ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_XDISTANCE,btn_left);
   ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line);
   ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_XSIZE,BTN_WIDTH);
   ObjectSetInteger(class="num">0,exprefix+"clear_"+(class="type">class="kw">string) cur_panel,OBJPROP_YSIZE,BTN_HEIGHT);

常见问题

在面板里开启自动模式搜索,工具会按预设波动阈值扫描并单独列出异动品种,你只看那一页就行。
用附加选项卡的按钮排版参数调列数和间距,跳空标注单独占一行,避免和正常按钮挤在一起。
小布盯盘的 AIGC 已内置异动扫描,打开对应品种页即可看到自动挑选和跳空标注,不用手动翻。
绘制落点设到右上方空白区,注释用半透明底,收尾逻辑里关掉重叠重绘即可。
在收尾逻辑里按实际按钮数取模重算行高,多余空间设为隐藏不绘制。