利用标准库类创建您自己的市场报价·进阶篇
🛠️

利用标准库类创建您自己的市场报价·进阶篇

(2/3)·从 CChartObjectLabel 到自动命名,手把手把标准库类改造成可动态刷新的 EA 状态面板

含代码示例实战向 第 2/3 篇

接上篇,我们继续深挖标准库里几个文本控件的底层类。很多人直接调 Label 却卡在坐标错位和重名冲突,本质是没有吃透 CChartObject 的后代封装。

◍ 标签钉死在窗口还是跟着K线跑

MT5 里文本类对象有两条路线:OBJ_TEXT 绑价格/时间坐标,滚动时贴着 K 线走;OBJ_LABEL 绑窗口像素坐标,滚动时原地不动。做悬浮提示、固定角标就得用后者,否则一拖图表全乱。 CChartObjectLabel 继承自 CChartObjectText,但把定位逻辑换了。它用 X_Distance / Y_Distance 控制距锚点的像素偏移,Corner 决定锚点落在窗口四个角中的哪一个(ENUM_BASE_CORNER)。Time 和 Price 被直接返回 false——说明这东西根本不认价格坐标。 下面这段是类声明的原样,逐行看能少踩坑:X_Distance() const 只读当前 X 像素距;X_Distance(int X) 写距;Y 同理。X_Size / Y_Size 只提供只读,尺寸由系统按字体算。Corner() const 读锚角,Corner(ENUM_BASE_CORNER) 写锚角。Create 接收 chart_id、name、window、X、Y 真正生成对象。Type() 写死返回 OBJ_LABEL,Load/Save 管序列化。 外汇和贵金属波动剧烈、杠杆高风险大,用标签做价签前先在模拟盘验证锚角行为,避免实盘遮挡关键形态。

MQL5 / C++
class CChartObjectLabel : class="kw">public CChartObjectText
{
class="kw">public:
   class="type">int                X_Distance() class="kw">const;
   class="type">bool               X_Distance(class="type">int X);
   class="type">int                Y_Distance() class="kw">const;
   class="type">bool               Y_Distance(class="type">int Y);
   class="type">int                X_Size() class="kw">const;
   class="type">int                Y_Size() class="kw">const;
   ENUM_BASE_CORNER   Corner() class="kw">const;
   class="type">bool               Corner(ENUM_BASE_CORNER corner);
   class="type">bool               Time(class="type">class="kw">datetime time) { class="kw">return(class="kw">false);  }
   class="type">bool               Price(class="type">class="kw">double price) { class="kw">return(class="kw">false);  }
   class="type">bool               Create(class="type">long chart_id,class="type">class="kw">string name,class="type">int window,class="type">int X,class="type">int Y);
   class="kw">virtual class="type">int        Type() class="kw">const        { class="kw">return(OBJ_LABEL); }
   class="kw">virtual class="type">bool       Save(class="type">int file_handle);
   class="kw">virtual class="type">bool       Load(class="type">int file_handle);
};

可编辑输入框对象的类封装与限制

在图表对象类层级里,CChartObjectEdit 承接自 CChartObjectLabel,专门负责生成 OBJ_EDIT 类型的输入字段。它和文本标签一样用像素坐标绑定到图表,但多了尺寸与交互控制,适合做参数录入或状态显示面板。 输入字段和标签的核心差异有三点:前者可用 X_Size / Y_Size 以屏幕像素硬性限定宽高,标签则自动撑开以显示完整文本;前者能用 ReadOnly 方法切换是否允许手动改字,后者无此概念;前者还能通过 BackColor 改背景色。 角度属性在这里被直接封死——Angle 方法写死返回 false,意味着输入框只能水平摆放,想做斜向输入区没必要折腾了。 下面这段是类的公开接口原貌,注意 Create 里 sizeX、sizeY 就是像素级宽高,调它之前得先想清楚输入框在你图表上的占位:

MQL5 / C++
class CChartObjectEdit : class="kw">public CChartObjectLabel
{
class="kw">public:
  class="type">bool                X_Size(class="type">int X);
  class="type">bool                Y_Size(class="type">int Y);
  class="type">class="kw">color               BackColor() class="kw">const;
  class="type">bool                BackColor(class="type">class="kw">color new_color);
  class="type">bool                ReadOnly() class="kw">const;
  class="type">bool                ReadOnly(class="type">bool flag);
  class="type">bool                Angle(class="type">class="kw">double angle) { class="kw">return(class="kw">false);    }
  class="type">bool                Create(class="type">long chart_id,class="type">class="kw">string name,class="type">int window,class="type">int X,class="type">int Y,class="type">int sizeX,class="type">int sizeY);
  class="kw">virtual class="type">int         Type() class="kw">const        { class="kw">return(OBJ_EDIT); }
  class="kw">virtual class="type">bool        Save(class="type">int file_handle);
  class="kw">virtual class="type">bool        Load(class="type">int file_handle);
};

「在图表上造一个真按钮:CChartObjectButton 怎么用」

MT5 标准库里,CChartObjectButton 挂在 CChartObjectEdit 之下,专门用来在图表上画出一个 Windows 风格的下压按钮,而不是普通的编辑框。它和 OBJ_EDIT 输入控件最大的区别,是多了读取和修改「按下/弹起」状态的方法,并且生成的真实图形对象类型是 OBJ_BUTTON。 这个类对外暴露了两个 State() 重载:无参版本返回当前按钮状态(bool),带参版本直接写入新状态。Type() 被重写为固定返回 OBJ_BUTTON,所以你 new 出来的对象在图表上就是按钮而非文本域。 下面这段是类声明的核心骨架,注意它全是虚函数或 inline 实现,继承链上 CObject 才是所有标准库的基类: 别把继承当黑盒 CChartObjectButton 只管按钮自身,列表管理得靠上层的 CList 接 CObject。开 MT5 新建 EA,把按钮塞进图表后,用 ChartRedraw 配合 State() 轮询,就能做出可点击的面板控件,外汇和贵金属图表叠加这类 UI 时需注意点击事件可能受报价刷新干扰,属高风险操作环境。

MQL5 / C++
class CChartObjectButton : class="kw">public CChartObjectEdit
{
class="kw">public:
  class="type">bool          State() class="kw">const;
  class="type">bool          State(class="type">bool state);
  class="kw">virtual class="type">int   Type() class="kw">const { class="kw">return(OBJ_BUTTON); }
  class="kw">virtual class="type">bool  Save(class="type">int file_handle);
  class="kw">virtual class="type">bool  Load(class="type">int file_handle);
};

◍ 给文本对象类加一层结构化输出

前面梳理过图表文本图形对象的标准类层级,现在直接在这一层上面派生新类,把‘标题 + 信息文本’的两段式结构固化下来。对多数轻量看盘需求来说,这种结构足够清晰,不需要更复杂的嵌套。 实际落点可以看一个交易品种参数指标:它用六个字段铺开信息,顶部平铺字段在示例里留空,其余字段都是‘标题 + 文本’的配对。源文件 PricelInfo.mq5 里能翻到完整实现,开 MT5 加载后图表右上角即出结构化块。 贵金属与外汇波动剧烈,这类只读信息面板仅作辅助,不构成任何方向暗示,实盘请自担高风险。

用字段坐标摆脱像素重算

在 MT5 图表上堆多个文本标签时,直接用屏幕像素定坐标是坑。窗口一缩放或换分辨率,所有对象的像素位置全乱,得逐个重算才能维持原相对排布。 更稳的做法是把图表窗口切成等尺寸矩形网格,每个格子给一个横向、纵向编号。这样坐标体系与屏幕物理分辨率脱钩,指定一次就固定了。 MQL5 里建图形文本对象时,可设网格的最大行列数,再把目标格子的行列号当参数传进去。类内部会在分辨率变动时自动换算像素,交易者不用再管重绘。外汇与贵金属波动剧烈、高风险,这类定位只解决布局麻烦,不预示任何价格走向。

「给图表对象造不撞名的流水号」

在 MT5 图表上批量画文本,最烦的不是画图本身,而是每次都要手写一个不重复的对象名。一旦重名,后创建的对象会顶掉前面的,定位和管理直接乱套。

比较稳的做法是用「GMT日期时间 + 毫秒计数」拼字符串:TimeToString(TimeGMT(), TIME_DATETIME_MINUTESTIME_SECONDS) 给出到秒的时间段,DoubleToString(GetTickCount(),0) 给出系统启动后的毫秒数。理论上同一秒内能拉出极多不重复前缀。

但 GetTickCount() 在连续多次调用时极可能返回相同值——操作系统和 CPU 计时器不是连续刷新的。所以光靠时间不够,还得在生成逻辑里做自增兜底,并额外用 ObjectFind 查重。 下面这个函数把静态计数器和查重都封进去了。注意它的硬上限:每秒最多生成 UINT_MAX(4294967295)个独立名,实战里基本碰不到这堵墙。 别把毫秒当绝对时钟 GetTickCount 的分辨率受系统节拍限制,紧密循环里两次取值相等是常态。代码里用 prev_count 自增来消化冲突,而不是假设时间一定往前走,这才是能在实盘脚本里跑稳的关键。

MQL5 / C++
class="type">class="kw">string GetUniqName()
{
  class="kw">static class="type">uint prev_count = class="num">0;
  class="type">uint count = GetTickCount();
  class="kw">while(class="num">1)
  {
    if(prev_count == UINT_MAX)
    {
      prev_count = class="num">0;
    }
    if(count <= prev_count)
    {
      prev_count++;
      count = prev_count;
    }
    else
    {
      prev_count = count;
    }
class=class="str">"cmt">//  确认没有同样名称的对象:
    class="type">class="kw">string name = TimeToString(TimeGMT(), TIME_DATE|TIME_MINUTES|TIME_SECONDS)+" "+DoubleToString(count, class="num">0);
    if(ObjectFind(class="num">0, name) < class="num">0)
    {
      class="kw">return(name);
    }
  }
  class="kw">return(NULL);
}
把面板刷新交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到 EA 运行状态与指标值的自动面板,你只需专注策略逻辑。

常见问题

Label 仅作静态或动态文本展示,不接收用户鼠标输入;Edit 允许图表上直接键入数值,常用于参数微调,但需自行处理 OBJPROP_READONLY 与事件过滤。
倾向使用图表宽高比例的锚定方式,避免不同分辨率下标签溢出。可在 CChartObject 后代里重载坐标计算,按右下角偏移更不容易被价格区域遮挡。
可以,通过 OnChartEvent 捕获按钮 id 后触发内部状态机;概率上更稳的做法是把按钮仅作开关,具体执行交给定时器减少事件阻塞。
目前小布盯盘内置的是标准指标与 EA 状态识别,若你用本文方法扩展了独有文本面板,可截图或导出值给小布做辅助诊断,完全自动解析自定义对象还需后续版本。
常见是在前缀后拼 EA 魔术码加 Counter 静态变量,或用 Symbol()+Period()+微秒戳;这样多图表加载同一 EA 时不会互相覆盖对象。