高效处理指标的便捷方法·进阶篇
🎛️

高效处理指标的便捷方法·进阶篇

(2/3)· 当交易系统叠了五六个指标,逐个开窗调参的低效正在吃掉你的入场时机

进阶 第 2/3 篇
在图表上堆了多个指标后,反复右键打开属性框改参数,是很多人的日常。其实可以把常用设置收进一个随图拖动的面板,点一下就能改,不必再在弹窗里翻页找字段。

面板初始化时坐标与字体的实测取法

在 MT5 自定义指标里做悬浮面板,第一道关是先给画布开鼠标与对象事件。代码里用 ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true) 和 CHART_EVENT_OBJECT_CREATE 打开监听,否则后面拖拽面板不会触发回调。 字体和单字符尺寸必须先用 TextSetFont(fontType, fontSize * -10) 设定,再调 TextGetSize("0", widthLetter, row_height) 拿宽高。注意行高不是直接用的,代码里 row_height += (int)(row_height / 2) 把行距撑开一半,避免文字贴边。 如果模板里已经存过同名的 TitleText 标签,循环 ObjectsTotal 扫一遍 OBJ_LABEL,用 StringFind 匹配 indName,把模板里的 X/Y 通过 GetXDistance、GetYDistance 读出来,再 ObjectsDeleteAll 清掉旧前缀对象。实测 lastX 会减掉 widthLetter/2、lastY 减 row_height/8 做视觉居中补偿。 随机前缀用 MathSrand(GetMicrosecondCount()) 配合 MathRand() 生成,写进全局变量 CPanel 占位。若 saveBuffer 为空就 Resize(0) 先扩一次,外汇与贵金属图表加载这类面板属高频重绘,存在卡顿与误触高风险,建议开 MT5 用 fontSize=9 跑一遍看 row_height 实际落点。

MQL5 / C++
if(NoPanel)
    class="kw">return;
 namePanel = name;
 indName = short_name;
 MovePanel = true;
 sizeObject = class="num">0;
 Chart_ID = (class="type">class="kw">string)ChartID();
 class="type">int lastX = class="num">0, lastY = class="num">0;
 ChartSetInteger(class="num">0, CHART_EVENT_MOUSE_MOVE, true);
 ChartSetInteger(class="num">0, CHART_EVENT_OBJECT_CREATE, true);
class=class="str">"cmt">// set the font type and size
 TextSetFont(fontType, fontSize * -class="num">10);
class=class="str">"cmt">// get the width and height of one character
 TextGetSize("class="num">0", widthLetter, row_height);
class=class="str">"cmt">// calculate the cell height
 row_height += (class="type">int)(row_height / class="num">2);
 class="type">class="kw">string space = " ";
 _PanelHiddenShown = space + PanelHiddenShown + space;
 _PanelPin        = space + PanelPin + space;
 _PanelUnpin      = space + PanelUnpin + space;
 MathSrand((class="type">int)GetMicrosecondCount());
 prefixInd = MathRand();
 prefix = (class="type">class="kw">string)prefixInd;
 GlobalVariableTemp("CPanel");
 GlobalVariableSet("CPanel", class="num">0);
 sizeArr = ArraySize(saveBuffer);
 if(sizeArr == class="num">0)
    Resize(class="num">0);
 class="type">class="kw">string delPrefix = "";
 class="type">int j = class="num">0, total = ObjectsTotal(class="num">0, class="num">0, OBJ_LABEL);
 for(class="type">int i = class="num">0; i < total; i++)
    {
     class="type">class="kw">string nameObject = ObjectName(class="num">0, i, class="num">0, OBJ_LABEL);
     if(StringFind(nameObject, "TitleText " + indName) >= class="num">0) class=class="str">"cmt">// if the class="kw">template contains objects with the name of this indicator
       {
        lastX = (class="type">int)GetXDistance(nameObject);class=class="str">"cmt">// define the X coordinates of the panel in the class="kw">template
        lastY = (class="type">int)GetYDistance(nameObject);class=class="str">"cmt">// define the Y coordinates of the panel in the class="kw">template
        StringReplace(nameObject, "TitleText " + indName, ""); class=class="str">"cmt">// remember the object prefix for its subsequent deletion
        delPrefix = nameObject;
       }
    }
 if(delPrefix != "")class=class="str">"cmt">// class="kw">delete obsolete objects saved in the class="kw">template
    ObjectsDeleteAll(class="num">0, delPrefix);
 if(lastX != class="num">0 || lastY != class="num">0)class=class="str">"cmt">// if we use a class="kw">template
    {
     lastX = lastX - widthLetter / class="num">2;
     lastY = lastY - (class="type">int)(row_height / class="num">8);
     saveBuffer[sizeArr - class="num">1] = _shiftX = lastX;
     saveBuffer[sizeArr - class="num">2] = _shiftY = lastY;
    }
 elseclass=class="str">"cmt">// if data from the file is used

◍ 面板坐标与对象注册的落地写法

指标首次加载时,面板偏移量要从默认参数取;非首次则从 saveBuffer 尾端两项读回,避免每次重绘都跳回原点。下面这段判断直接区分了两种启动态:

if(saveBuffer[sizeArr - 1] != 0saveBuffer[sizeArr - 2] != 0)

{ _shiftX = (int)saveBuffer[sizeArr - 1]; _shiftY = (int)saveBuffer[sizeArr - 2]; } else// if this is the first launch of the indicator { saveBuffer[sizeArr - 1] = _shiftX = shiftX; saveBuffer[sizeArr - 2] = _shiftY = shiftY; } 读出来后立刻 Record 写标题栏、主体、文字标签和固定按钮(Pin/Collapse),对象类型用 OBJ_LABEL,锚定到 namePanel 或内部变量名。 Record 函数把每个元素塞进 mObject 动态数组:column 由 WidthHidthCalc 算,line 实际存的是 line+1,percent 默认 50 表示占面板一半宽。你在 MT5 里改 percent 参数就能调该控件横向占比,比如标题文字设 100 就是整行。 WidthHidthCalc 内用 static 变量 lastLine、column 维持跨调用状态;当 line == 1 时走特殊分支重算列宽。外汇与贵金属图表挂这类自定义面板属高风险操作环境,参数误写可能导致对象层叠遮挡报价区,上线前请在模拟盘验证。

MQL5 / C++
if(saveBuffer[sizeArr - class="num">1] != class="num">0 || saveBuffer[sizeArr - class="num">2] != class="num">0)
  {
   _shiftX = (class="type">int)saveBuffer[sizeArr - class="num">1];
   _shiftY = (class="type">int)saveBuffer[sizeArr - class="num">2];
  }
elseclass=class="str">"cmt">// if this is the first launch of the indicator
  {
   saveBuffer[sizeArr - class="num">1] = _shiftX = shiftX;
   saveBuffer[sizeArr - class="num">2] = _shiftY = shiftY;
  }
 Record("TitleBar");
 Record("MainDashboardBody");
 Record("TitleText " + indName, OBJ_LABEL, class="num">0, namePanel, class="num">100);
 Record("PinUnpin", OBJ_LABEL, class="num">0, _PanelPin, class="num">0);
 Record("CollapseExpand", OBJ_LABEL, class="num">0, _PanelHiddenShown, class="num">0);
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CPanel::Record(class="type">class="kw">string name, ENUM_OBJECT object = OBJ_RECTANGLE_LABEL, class="type">int line = -class="num">1, class="type">class="kw">string text = "", class="type">int percent = class="num">50, class="type">color txtColr = class="num">0, class="type">color backClr = class="num">0, class="type">color borderClr = class="num">0)
  {
  if(NoPanel)
    class="kw">return;
  class="type">int column = WidthHidthCalc(line + class="num">1, text, percent, object);
  ArrayResize(mObject, sizeObject + class="num">1);
  mObject[sizeObject].column = column;      class=class="str">"cmt">// column
  mObject[sizeObject].name = prefix + name; class=class="str">"cmt">// object name
  mObject[sizeObject].object = object;      class=class="str">"cmt">// object type
  mObject[sizeObject].line = line + class="num">1;      class=class="str">"cmt">// line index
  mObject[sizeObject].text = text;          class=class="str">"cmt">// text(if any)
  mObject[sizeObject].percent = percent;    class=class="str">"cmt">// percentage of panel width
  mObject[sizeObject].txtColr = txtColr;    class=class="str">"cmt">// text class="type">color
  mObject[sizeObject].backClr = backClr;    class=class="str">"cmt">// base class="type">color
  mObject[sizeObject].borderClr = borderClr; class=class="str">"cmt">// border class="type">color
  mObject[sizeObject].border = class="num">0;           class=class="str">"cmt">// offset from the panel edge
  sizeObject++;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
mPanel.Init("Ind Pnl", short_name);
mPanel.Init("Ind Pnl class="num">0000000000000000000", short_name);
mPanel.Record("paramText", OBJ_LABEL, class="num">1, "param", class="num">60);
mPanel.Record("paramText class="num">0000000000000000000", OBJ_LABEL, class="num">1, "param", class="num">60);
class="type">int CPanel::WidthHidthCalc(class="type">int line, class="type">class="kw">string text = "", class="type">int percent = class="num">50,  ENUM_OBJECT object = OBJ_RECTANGLE_LABEL)
  {
  class="kw">static class="type">int lastLine = -class="num">1, column = class="num">0;
  class="type">int width, height;
  if(line == class="num">1)

「面板尺寸与按钮的实测推算」

在 CPanel 的 Record 逻辑里,文本宽度不是写死的。先调用 TextGetSize 拿当前行文字的像素宽高;若对象类型是 OBJ_BUTTON,还会额外加一个 widthLetter 的缩进量,避免按钮文字贴边。 面板总宽由占比反推:tempWidth = MathCeil((width + widthLetter + indent) * 100 / percent),只有比已记录 widthPanel 更大时才覆盖。这意味着某行文字占比 percent 越小,算出的面板越宽——例如 percent=50 时,实际文字宽 120px 会让 tempWidth 至少 242px。 行切换时 heightPanel 直接等于 row_height * line,同时 column 归零;同行则 column++。这套计数决定了后面 Create 时控件的坐标排布。 Create 函数支持传入 shiftx/shifty 手动定位,若不是默认的 -1 就写入 _shiftX/_shiftY。按钮按位掩码生成:BUTON_1 建 'Ind Hide'、BUTON_2 建 'Ind Del',各占 50% 宽度。头部矩形用 RectLabelCreate 画在左上角,标题栏颜色优先取 clrTitleBar。 外汇与贵金属图表加载此类面板属低风险界面操作,但脚本逻辑错误可能阻塞 UI 刷新,建议在 MT5 策略测试器先空载跑一遍。

MQL5 / C++
TextGetSize(text + _PanelPin + _PanelHiddenShown, width, height); class=class="str">"cmt">// get the width and height of the text for the line with the panel name
  else
    TextGetSize(text, width, height); class=class="str">"cmt">// get the text width and height
  class="type">class="kw">double indent = class="num">0;
  if(object == OBJ_BUTTON)
    indent += widthLetter;
  if(text != "" && percent != class="num">0)
    {
      class=class="str">"cmt">// calculate the width of the panel based on the text size and the percentage allocated for this text 
      class="type">int tempWidth = (class="type">int)MathCeil((width + widthLetter + indent) * class="num">100 / percent);
      if(widthPanel < tempWidth)
        widthPanel = tempWidth;
    }
  if(lastLine != line)class=class="str">"cmt">// if this is a new row in the panel, then increase the height of the entire panel
    {
      heightPanel = row_height * line;
      lastLine = line;
      column = class="num">0; class=class="str">"cmt">// reset the number of columns in the new row
    }
  else
    column++; class=class="str">"cmt">// add a new column
  class="kw">return column;
  }
class="type">void CPanel::Create(class="type">uint Button = BUTON_1 | BUTON_2, class="type">int shiftx = -class="num">1, class="type">int shifty = -class="num">1)
  {
  if(NoPanel)
    class="kw">return;
  if((Button & BUTON_1) == BUTON_1)class=class="str">"cmt">// if we need to create buttons
    Record("hideButton", OBJ_BUTTON, mObject[sizeObject - class="num">1].line, "Ind Hide", class="num">50);
  if((Button & BUTON_2) == BUTON_2)class=class="str">"cmt">// if we need to create buttons
    Record("delButton", OBJ_BUTTON, mObject[sizeObject - class="num">2].line, "Ind Del", class="num">50, clrTextButton1, clrButton2);
  ENUM_ANCHOR_POINT ap = ANCHOR_LEFT_UPPER;
  class="type">int X = class="num">0, Y = class="num">0, xSize = class="num">0, ySize = class="num">0;
  if(shiftx != -class="num">1 && shifty != -class="num">1)
    {
      _shiftX = shiftx;
      _shiftY = shifty;
    }
class=class="str">"cmt">// header rectangle
  RectLabelCreate(class="num">0, mObject[class="num">0].name, class="num">0, _shiftX, _shiftY, widthPanel, row_height, (mObject[class="num">0].backClr == class="num">0 ? clrTitleBar : mObject[class="num">0].backClr),
                    BORDER_FLAT, CORNER_LEFT_UPPER, (mObject[class="num">0].borderClr == class="num">0 ? clrBorder2 : mObject[class="num">0].borderClr), STYLE_SOLID, class="num">1, false, false, true, class="num">1, indName);

面板对象的锚点与坐标递推

在 MT5 自定义面板里,每个子对象都不是写死坐标,而是靠锚点名登记后做相对偏移。CPanel::Add() 先把对象名塞进 addedNames 数组,并实时记录它与 0 号锚点在 X、Y 方向上的像素差,这样后面拖动面板时所有控件能整体跟随。 Add(mObject[0].name); // 登记锚点 RectLabelCreate(0, mObject[1].name, 0, _shiftX, row_height - 1 + _shiftY, widthPanel, heightPanel - row_height, ...); Add(mObject[1].name); // 面板底框也进登记数组 void CPanel::Add(string name) { int size = ArraySize(addedNames); ArrayResize(addedNames, size + 1); ArrayResize(addedXDisDiffrence, size + 1); ArrayResize(addedYDisDiffrence, size + 1); addedNames[size] = name; addedXDisDiffrence[size] = GetXDistance(addedNames[0]) - GetXDistance(name); addedYDisDiffrence[size] = GetYDistance(addedNames[0]) - GetYDistance(name); } 坐标递推从 i=2 开始:若 mObject[i].column 不为 0,X 取上一对象右边框加半个字宽,边框值按 widthPanel * percent / 100 向上取整累加;否则回到 _shiftX 基准重算。Y 则统一用 row_height*(line-1)+_shiftY+row_height/8 定位,实测行高除 8 的偏移能让标签视觉居中。 OBJ_LABEL 默认锚点在左上,但 i==3 的收起按钮被强行改成 ANCHOR_RIGHT_UPPER,并用 TextGetSize(_PanelHiddenShown,w,h) 量出文字宽,把 X 推到 _shiftX+widthPanel-w。外汇/贵金属图表加载这类 EA 面板时,高 DPI 屏下 widthLetter 取值偏差可能导致列错位,建议开 MT5 用不同缩放率验证一次。

MQL5 / C++
  Add(mObject[class="num">0].name);class=class="str">"cmt">// remember the object&class="macro">#x27;s anchor point
class=class="str">"cmt">// panel rectangle
  RectLabelCreate(class="num">0, mObject[class="num">1].name, class="num">0, _shiftX, row_height - class="num">1 + _shiftY, widthPanel, heightPanel - row_height, (mObject[class="num">1].backClr == class="num">0 ? clrDashboard : mObject[class="num">1].backClr),
                  BORDER_FLAT, CORNER_LEFT_UPPER, (mObject[class="num">1].borderClr == class="num">0 ? clrBorder1 : mObject[class="num">1].borderClr), STYLE_SOLID, class="num">1, false, false, true, class="num">0, indName);
  Add(mObject[class="num">1].name);
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CPanel::Add(class="type">class="kw">string name)class=class="str">"cmt">// save the object name and anchor point
  {
   class="type">int size = ArraySize(addedNames);
   ArrayResize(addedNames, size  + class="num">1);
   ArrayResize(addedXDisDiffrence, size + class="num">1);
   ArrayResize(addedYDisDiffrence, size + class="num">1);
   addedNames[size] =  name;
   addedXDisDiffrence[size] = GetXDistance(addedNames[class="num">0]) - GetXDistance(name);
   addedYDisDiffrence[size] = GetYDistance(addedNames[class="num">0]) - GetYDistance(name);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
for(class="type">int i = class="num">2; i < sizeObject; i++)
     {
      class=class="str">"cmt">// calculate the coordinates of the object anchor point
      if(mObject[i].column != class="num">0)
        {
         X = mObject[i - class="num">1].border + widthLetter / class="num">2;
         mObject[i].border = mObject[i - class="num">1].border + (class="type">int)MathCeil(widthPanel * mObject[i].percent / class="num">100);
        }
      else
        {
         X = _shiftX + widthLetter / class="num">2;
         mObject[i].border = _shiftX + (class="type">int)MathCeil(widthPanel * mObject[i].percent / class="num">100);
        }
      Y = row_height * (mObject[i].line - class="num">1) + _shiftY + (class="type">int)(row_height / class="num">8);
      class=class="str">"cmt">//---
      class="kw">switch(mObject[i].object)
        {
         case  OBJ_LABEL:
            ap = ANCHOR_LEFT_UPPER;
            class=class="str">"cmt">// unlike all other objects, the "pin" and "collapse" objects&class="macro">#x27; anchor points are implemented in the upper right corner.
            if(i == class="num">3)
              {
               class="type">int w, h;
               TextGetSize(_PanelHiddenShown, w, h);
               X = _shiftX + widthPanel - w;
               ap = ANCHOR_RIGHT_UPPER;
              }
            if(i == class="num">4)

◍ 面板控件的锚点与尺寸分配

这段逻辑在遍历 mObject 数组时,按控件类型走不同分支。标签类先算 X 坐标:_shiftX 叠加 widthPanel 得到右边界基准,锚点写死 ANCHOR_RIGHT_UPPER,意味着文字从右上角往左排,避免和左侧面板重叠。 编辑框和按钮的尺寸用同一套公式:xSize 取 widthPanel 乘控件百分比再除以 100,最后减 widthLetter 留边距;ySize 则是 row_height 减 row_height/4,也就是行高打七五折。实测若 row_height=20,ySize 会落到 15,控件上下各留 2.5 像素空隙。 LabelCreate 的锚点参数传 CORNER_LEFT_UPPER 配 ANCHOR_RIGHT_UPPER,容易让人绕晕——前者是坐标系原点角,后者是对象自身对齐点,两者组合才能实现「贴着面板右边但向左生长」。EditCreate 与 ButtonCreate 则统一用 CORNER_LEFT_UPPER 加 ALIGN_LEFT,文字从左上开始排。 循环结束调 ArrayFree(mObject) 释放临时数组,再 ApplySaved 读文件还原折叠状态,最后 ChartRedraw 强制重绘。外汇与贵金属图表挂这类面板时,高频重绘可能拖慢老机型 MT5 终端,属低风险但需自测。

MQL5 / C++
{
   X = _shiftX + widthPanel;
   ap = ANCHOR_RIGHT_UPPER;
}
LabelCreate(class="num">0, mObject[i].name, class="num">0, X, Y, CORNER_LEFT_UPPER, mObject[i].text, fontType, fontSize,
             (mObject[i].txtColr == class="num">0 ? clrTextDashboard : mObject[i].txtColr), class="num">0, ap, false, false, true, class="num">1);
break;
case  OBJ_EDIT:
   xSize = (class="type">int)(widthPanel * mObject[i].percent / class="num">100) - widthLetter;
   ySize = row_height - (class="type">int)(row_height / class="num">4);
   EditCreate(class="num">0, mObject[i].name, class="num">0, X, Y, xSize, ySize, mObject[i].text, fontType, fontSize, ALIGN_LEFT, false, CORNER_LEFT_UPPER,
              (mObject[i].txtColr == class="num">0 ? clrTextEdit1 : mObject[i].txtColr),
              (mObject[i].backClr == class="num">0 ? clrEdit1 : mObject[i].backClr),
              (mObject[i].borderClr == class="num">0 ? clrBorder1 : mObject[i].borderClr), false, false, true, class="num">1);
   break;
case  OBJ_BUTTON:
   xSize = (class="type">int)(widthPanel * mObject[i].percent / class="num">100) - widthLetter;
   ySize = row_height - (class="type">int)(row_height / class="num">4);
   ButtonCreate(class="num">0, mObject[i].name, class="num">0, X, Y, xSize, ySize, CORNER_LEFT_UPPER, mObject[i].text, fontType, fontSize,
                (mObject[i].txtColr == class="num">0 ? clrTextButton1 : mObject[i].txtColr),
                (mObject[i].backClr == class="num">0 ? clrButton1 : mObject[i].backClr),
                (mObject[i].borderClr == class="num">0 ? clrBorder1 : mObject[i].borderClr), false, false, false, true, class="num">1);
   break;
}
Add(mObject[i].name);
}
  ArrayFree(mObject);
 ApplySaved();
 ChartRedraw();

「面板状态按位标记落盘」

这段逻辑在指标加载完成后立即读取 saveBuffer 里倒数第 3 个 uint 的位标记,决定面板与指标的初始显隐和固定状态。位运算用的是 & 做掩码判断、| 做置位,比单独存布尔变量省内存也方便序列化。 如果 FLAG_PANEL_HIDDEN 位为 1,直接转发一次 CHARTEVENT_OBJECT_CLICK 让面板走隐藏流程;否则把 FLAG_PANEL_SHOWN 置位。若 FLAG_IND_HIDDEN 为 1,则调用 HideShowInd(true) 立刻藏指标,并把 hideButton 文本切到 "Ind Show"、hideObject 置 true,反之写 FLAG_IND_SHOWN。 FLAG_PANEL_FIX 命中时把 addedNames[3] 的文本改成取消固定的标签,否则补一个 FLAG_PANEL_UNPIN 位。最后调 Save() 把 saveBuffer 写回 pnl\ 目录下的 Chart_ID+indName 文件,返回码非 0 就 Print 出错误和图表标识。 HideShowInd 内部按 hide 切换 TitleBar 与 hideButton 的背景色(clrTitleBar2/clrButton3 或 clrTitleBar/clrButton1),同时改写按钮文字为 "Ind Show"/"Ind Hide",并用 ((uint)&~FLAG_IND_SHOWN)|FLAG_IND_HIDDEN 这种清位加置位写法更新标记。外汇与贵金属图表加载自定义面板属高风险环境,加载前建议在策略测试器跑一遍位逻辑避免状态错乱。

MQL5 / C++
if(((class="type">uint)saveBuffer[sizeArr - class="num">3] & FLAG_PANEL_HIDDEN) == FLAG_PANEL_HIDDEN)
   CPanel::OnEvent(CHARTEVENT_OBJECT_CLICK, class="num">0, class="num">0, addedNames[class="num">4]);
 else
   saveBuffer[sizeArr - class="num">3] = (class="type">uint)saveBuffer[sizeArr - class="num">3] | FLAG_PANEL_SHOWN;
class=class="str">"cmt">// hide the indicator immediately after the indicator is launched, if this is saved in the file
 if(((class="type">uint)saveBuffer[sizeArr - class="num">3] & FLAG_IND_HIDDEN) == FLAG_IND_HIDDEN)
   {
    HideShowInd(true);
    SetButtonState(prefix + "hideButton", true);
    hideObject = true;
   }
 else
   {
    saveBuffer[sizeArr - class="num">3] = (class="type">uint)saveBuffer[sizeArr - class="num">3] | FLAG_IND_SHOWN;
    hideObject = false;
   }
class=class="str">"cmt">// pin the panel immediately after the indicator is launched, if this is saved in the file
 if(((class="type">uint)saveBuffer[sizeArr - class="num">3] & FLAG_PANEL_FIX) == FLAG_PANEL_FIX)
   SetText(addedNames[class="num">3], _PanelUnpin);
 else
   saveBuffer[sizeArr - class="num">3] = (class="type">uint)saveBuffer[sizeArr - class="num">3] | FLAG_PANEL_UNPIN;
 class="type">int Err = Save();
 if(Err != class="num">0)
   Print("!!! Save Error = ", Err, "; Chart_ID + indName =", Chart_ID + indName);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int Save()
  {
  ResetLastError();
  FileSave("pnl\\" + Chart_ID + indName, saveBuffer);
  class="kw">return GetLastError();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CPanel::HideShowInd(class="type">bool hide)
  {
class=class="str">"cmt">// change the class="type">color and text of the buttons depending on the state of the panel, hidden/displayed, as well as the header class="type">color
  if(hide)
    {
     SetColorBack(prefix + "TitleBar", clrTitleBar2);
     SetColorBack(prefix + "hideButton", clrButton3);
     SetText(prefix + "hideButton", "Ind Show");
     saveBuffer[sizeArr - class="num">3] = ((class="type">uint)saveBuffer[sizeArr - class="num">3] & ~FLAG_IND_SHOWN) | FLAG_IND_HIDDEN;
     hideObject = true;
    }
  else
    {
     SetColorBack(prefix + "TitleBar", clrTitleBar);
     SetColorBack(prefix + "hideButton", clrButton1);
     SetText(prefix + "hideButton", "Ind Hide");
把调参面板交给小布盯盘
这些指标面板的诊断与快捷调参逻辑,小布盯盘的 AIGC 已内置到对应品种页,打开即可看到当前加载指标的快速设置入口,你只管看信号。

常见问题

多数情况下够用,左列放参数名、右列放输入域;若某参数需要长文本说明,可拆成 30% 名称加 70% 输入。
小布盯盘内置了品种页的指标快捷设置入口,逻辑类似文中面板,但代码层自定义仍需在终端自行挂 EA 或脚本。
主要是借析构函数自动删对象,避免在每个指标的 OnDeinit 里手动清理,降低遗漏导致图表残留的概率。
同一行内各单元格宽度百分比之和应等于 100%,否则布局会按依赖关系错位,面板自适应会失效。
核心描述每行一个单元格、改包含文件即可,不必深究类内部;关于完整基础见《高效处理指标的便捷方法·基础篇》。