使用 CCanvas 类绘制刻度表盘·综合运用
📟

使用 CCanvas 类绘制刻度表盘·综合运用

(3/3)·把前两相熟的绘制基础收拢成可复用表盘库,并跑通盈利与行情两类实盘挂件

案例拆解新手友好 第 3/3 篇
很多人把 CCanvas 表盘当成纯炫技,写完一个粗糙原型就搁置。其实只要把坐标锚点、元素函数和重绘逻辑封装成库,往指标或 EA 里挂只需几行调用,还能顺手评估资源占用。

用表盘把账户盈利直接画在图上

想在 EA 或指标里加一个实时盈利表盘,核心就三步:声明 GAUGE_STR 结构、用 GaugeCreate() 初始化、在 OnCalculate() 里调 GaugeNewValue() 刷新读数。默认外观是库自带的省缺样式,但实战里基本都会改刻度。 把刻度范围拉到 -200 到 400,主标记步长设 100,中间插一个中等标记、四个次要标记,最小步长就压到 10,盘面文字不冗余。再用索引 0 标 200~400 区间填 clrLimeGreen,索引 1 标 -100~-200 填 clrCoral,亏损区一眼能看出来。 描述文字 "Profit" 放半径 3 角度 0 字号 14,单位 "USD" 放半径 8 角度 215 字号 10,当前值格式 "1"(保留一位小数)放半径 4 角度 255 字号 20。改完这些,表盘就从省缺样变成了带区间高亮的盈利监视器。 更复杂的仪表板指标把盈利、点差、保证金百分比、ATR、力度指数、RSI 全塞进一个图:先声明表盘数组,保证金级别放左下角绝对坐标,其余表盘全相对它排。OnCalculate() 里重算数值后挨个调 GaugeNewValue(),因为初始化时一次性算好相对坐标,也就不用在 OnChartEvent() 里反复 GaugeRelocation()。外汇和贵金属波动大,这类盘面只作风险参考,不代表账户走向。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                     profit_gauge_indicator.mq5 |
class=class="str">"cmt">//|                     Copyright class="num">2015, Decanium |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2015, Decanium"
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property indicator_plots class="num">0
class="macro">#class="kw">property indicator_chart_window
class="macro">#include <Gauge\gauge_graph.mqh>
input class="type">class="kw">string inp_gauge_name="gg01";                    class=class="str">"cmt">// 指标名
input class="type">int inp_x = class="num">40;                                  class=class="str">"cmt">// 水平偏移
input class="type">int inp_y = class="num">40;                                  class=class="str">"cmt">// 垂直偏移
input class="type">int inp_size=class="num">300;                                class=class="str">"cmt">// 指标大小
input class="type">class="kw">string inp_ObjRel="";                            class=class="str">"cmt">// 相对定位情况下的基准指标名
input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE;   class=class="str">"cmt">// 相对定位模式
input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER;   class=class="str">"cmt">// 锚点边角
input class="type">bool inp_back=class="kw">false;                             class=class="str">"cmt">// 指标作为背景
input class="type">uchar inp_scale_transparency=class="num">0;                  class=class="str">"cmt">// 刻度透明级别, class="num">0..class="num">255
input class="type">uchar inp_needle_transparency=class="num">0;                 class=class="str">"cmt">// 表针透明级别, class="num">0..class="num">255
class=class="str">"cmt">//--- 声明表盘结构
GAUGE_STR g0;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标初始化函数                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 创建表盘

「把账户浮盈塞进表盘指针里」

在 MT5 自定义指标里接一块仪表盘,核心不是画图,而是让指针跟着 AccountInfoDouble(ACCOUNT_PROFIT) 实时跑。OnCalculate 里每次刷新的时候取一次浮盈,丢给 GaugeNewValue,表盘就不用自己写重绘逻辑。 初始化阶段如果 GaugeCreate 返回 false,直接 INIT_FAILED 退出,避免后面空指针崩指标;成功才 GaugeRedraw 画第一帧。OnDeinit 里记得 GaugeDelete 清对象再 ChartRedraw,不然切换周期会留残影。 刻度别照默认来。inp_scale_range=270 配 inp_rotation=45,等于把 270 度弧线从 45 度方向起画;GaugeSetScaleParameters 把数值区间锁在 -200 到 400,MUL_1 不缩放。GaugeSetRangeParameters 单独标了两段:0 到 200 染绿(盈利安全区),-100 到 -200 染珊瑚红(回撤警戒区),肉眼扫一眼就知道当前在哪一头。外汇和贵金属杠杆品种浮盈波动大,这种表盘只作盯盘辅助,不代表风险可控。 图表被拖动缩放时会触发 CHARTEVENT_CHART_CHANGE,里面调 GaugeCalcLocation 算位置、GaugeRelocation 重贴,否则表盘会漂在坐标外。

MQL5 / C++
if(GaugeCreate(inp_gauge_name,g0,inp_x,inp_y,inp_size,inp_ObjRel,inp_rel_mode,
               inp_corner,inp_back,inp_scale_transparency,inp_needle_transparency)==class="kw">false)
      class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 绘制表盘
   GaugeRedraw(g0);
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标逆始函数                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
   {
class=class="str">"cmt">//--- 删除表盘
   GaugeDelete(g0);
   ChartRedraw();
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标迭代函数                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">int begin,
                class="kw">const class="type">class="kw">double &price[])
   {
class=class="str">"cmt">//--- 更新读数
   class="type">class="kw">double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(g0,profit);
class=class="str">"cmt">//---
   class="kw">return(rates_total);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ChartEvent 函数                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(class="kw">const class="type">int id,
                 class="kw">const class="type">long &lparam,
                 class="kw">const class="type">class="kw">double &dparam,
                 class="kw">const class="type">class="kw">string &sparam)
   {
class=class="str">"cmt">//---
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      if(GaugeCalcLocation(g0)==true)
         GaugeRelocation(g0);
     }
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
input class="type">int inp_scale_range=class="num">270;   class=class="str">"cmt">// 刻度范围, class="num">30..class="num">320 度
input class="type">int inp_rotation=class="num">45;       class=class="str">"cmt">// 刻度旋转, class="num">0..class="num">359 度
class=class="str">"cmt">//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-class="num">200,class="num">400,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,class="num">100,class="num">1,class="num">4);
class=class="str">"cmt">//--- 刻度的强调范围
   GaugeSetRangeParameters(g0,class="num">0,true,class="num">200,class="num">400,clrLimeGreen);
   GaugeSetRangeParameters(g0,class="num">1,true,-class="num">100,-class="num">200,clrCoral);

◍ 用表盘把保证金与浮盈摆上图表

在 MT5 里做盯盘面板,最实用的不是画线而是把关键风险指标做成图形表盘。下面这段初始化逻辑同时挂了两个仪表:g0 显示账户 Profit(单位 USD,数值字号 20),gg00 显示保证金水平(Margin lvl),gg01 显示当前盈利,三者共用 Arial 字体、下左角锚定。 gg00 的刻度范围设成 800–2000、放大 100 倍(MUL_100),并用两段强调色区分风险区:1400–2000 染成 clrLimeGreen,1000–800 反向区间染成 clrCoral,扫一眼就能察觉保证金逼近危险带。外汇与贵金属杠杆高,这种视觉预警比看数字更不容易漏单。 gg01 用 RELATIVE_MODE_HOR 相对挂在 gg00 右侧(偏移 -80,20),刻度 -400 到 400、MUL_1,每 100 跳一个内刻度。跑起来后若 GaugeCreate 返回 false 会直接 INIT_FAILED,开 MT5 把这段代码贴进 EA 的 OnInit 就能验证表盘是否如期出现。

MQL5 / C++
  GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",class="num">3,class="num">0,class="num">14,"Arial",class="kw">false,class="kw">false);
  GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",class="num">8,class="num">215,class="num">10,"Arial",true,class="kw">false);
  GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"class="num">1",class="num">4,class="num">225,class="num">20,"Arial",true,class="kw">false);
class=class="str">"cmt">//--- 声明表盘结构数组
GAUGE_STR gg[class="num">6];
class=class="str">"cmt">//--- 构建 gg00 表盘, 保证金级别
  if(GaugeCreate("gg00",gg[class="num">0],class="num">5,-class="num">90,class="num">240,"",RELATIVE_MODE_NONE,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==class="kw">false)
      class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 设置实体参数
  GaugeSetCaseParameters(gg[class="num">0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
class=class="str">"cmt">//--- 设置刻度和标记的参数
  GaugeSetScaleParameters(gg[class="num">0],class="num">120,class="num">35,class="num">800,class="num">2000,MUL_100,SCALE_INNER,clrBlack);
  GaugeSetMarkParameters(gg[class="num">0],MARKS_INNER,SIZE_MIDDLE,class="num">200,class="num">1,class="num">4);
  GaugeSetMarkLabelFont(gg[class="num">0],SIZE_MIDDLE,"Arial",class="kw">false,class="kw">false,DEF_COL_MARK_FONT);
class=class="str">"cmt">//--- 刻度的强调范围
  GaugeSetRangeParameters(gg[class="num">0],class="num">0,true,class="num">1400,class="num">2000,clrLimeGreen);
  GaugeSetRangeParameters(gg[class="num">0],class="num">1,true,class="num">1000,class="num">800,clrCoral);
class=class="str">"cmt">//--- 设置文字标签
  GaugeSetLegendParameters(gg[class="num">0],LEGEND_DESCRIPTION,true,"Margin lvl",class="num">4,class="num">15,class="num">12,"Arial",class="kw">false,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">0],LEGEND_VALUE,true,"class="num">0",class="num">3,class="num">80,class="num">16,"Arial",true,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">0],LEGEND_MUL,true,"",class="num">4,class="num">55,class="num">8,"Arial",true,class="kw">false);
class=class="str">"cmt">//--- 设置表针参数
  GaugeSetNeedleParameters(gg[class="num">0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
class=class="str">"cmt">//--- 构建 gg01 表盘, 当前盈利
  if(GaugeCreate("gg01",gg[class="num">1],-class="num">80,class="num">20,class="num">320,"gg00",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==class="kw">false)
      class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 设置实体参数
  GaugeSetCaseParameters(gg[class="num">1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
class=class="str">"cmt">//--- 设置刻度和标记的参数
  GaugeSetScaleParameters(gg[class="num">1],class="num">200,class="num">0,-class="num">400,class="num">400,MUL_1,SCALE_INNER,clrBlack);
  GaugeSetMarkParameters(gg[class="num">1],MARKS_INNER,SIZE_MIDDLE,class="num">100,class="num">1,class="num">4);
  GaugeSetMarkLabelFont(gg[class="num">1],SIZE_MIDDLE,"Arial",class="kw">false,class="kw">false,DEF_COL_MARK_FONT);
class=class="str">"cmt">//--- 刻度的强调范围

盈利、点差与ATR三表盘的视觉分区

在 MT5 指标面板上叠多个仪表盘,关键是把盈利、点差、ATR 分到不同坐标锚点,避免指针互相遮掩。gg01 用相对模式挂在左上角,gg02 用 RELATIVE_MODE_HOR 偏移 (-80,-20) 尺寸 240,专门盯点差;gg03 用 RELATIVE_MODE_VERT 挂在 gg00 下方,尺寸 180 显示 ATR。 盈利盘 gg01 设了两个强调区间:0~200 染绿、−200~−400 染珊瑚红,说明浮盈 200 美元内偏安全区,穿透 −200 后进入警示带。文字标签把单位写死成 USD、描述写 Profit,表针用 NDCS_SMALL 细针,扫盘时更跟手。 点差盘 gg02 刻度只铺 120 度、起角 −35,强调段 35~10 绿、50~60 红——点差若拉到 50 点以上,指针进红区,黄金或外汇跳空时段这种状态容易出现,属高波动风险信号。ATR 盘 gg03 刻度范围 0.001~0.004、乘子 MUL_00001,大标记每 0.001 一根共 9 根,适合直接读 EURUSD 级微小波动率。 下面这段是三个盘的基础构建与分区代码,逐行看能直接抄进自己的面板工程。

MQL5 / C++
  GaugeSetRangeParameters(gg[class="num">1],class="num">0,true,class="num">200,class="num">400,clrLimeGreen);
  GaugeSetRangeParameters(gg[class="num">1],class="num">1,true,-class="num">200,-class="num">400,clrCoral);
class=class="str">"cmt">//--- 设置文字标签
  GaugeSetLegendParameters(gg[class="num">1],LEGEND_DESCRIPTION,true,"Profit",class="num">3,class="num">0,class="num">16,"Arial",class="kw">false,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">1],LEGEND_UNITS,true,"USD",class="num">3,-class="num">90,class="num">10,"Arial",true,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">1],LEGEND_VALUE,true,"class="num">1",class="num">3,class="num">90,class="num">12,"Arial",true,class="kw">false);
class=class="str">"cmt">//--- 设置表针参数
  GaugeSetNeedleParameters(gg[class="num">1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
class=class="str">"cmt">//--- 构建 gg02 表盘, 点差
  if(GaugeCreate("gg02",gg[class="num">2],-class="num">80,-class="num">20,class="num">240,"gg01",RELATIVE_MODE_HOR,
     CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==class="kw">false)
     class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 设置实体参数
  GaugeSetCaseParameters(gg[class="num">2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
class=class="str">"cmt">//--- 设置刻度和标记的参数
  GaugeSetScaleParameters(gg[class="num">2],class="num">120,-class="num">35,class="num">60,class="num">0,MUL_1,SCALE_INNER,clrBlack);
  GaugeSetMarkParameters(gg[class="num">2],MARKS_INNER,SIZE_MIDDLE,class="num">10,class="num">1,class="num">4);
  GaugeSetMarkLabelFont(gg[class="num">2],SIZE_MIDDLE,"Arial",class="kw">false,class="kw">false,DEF_COL_MARK_FONT);
class=class="str">"cmt">//--- 刻度的强调范围
  GaugeSetRangeParameters(gg[class="num">2],class="num">0,true,class="num">35,class="num">10,clrLimeGreen);
  GaugeSetRangeParameters(gg[class="num">2],class="num">1,true,class="num">50,class="num">60,clrCoral);
class=class="str">"cmt">//--- 设置文字标签
  GaugeSetLegendParameters(gg[class="num">2],LEGEND_DESCRIPTION,true,"Spread",class="num">4,-class="num">15,class="num">14,"Arial",class="kw">false,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">2],LEGEND_VALUE,true,"class="num">0",class="num">3,-class="num">80,class="num">16,"Arial",true,class="kw">false);
class=class="str">"cmt">//--- 设置表针参数
  GaugeSetNeedleParameters(gg[class="num">2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
class=class="str">"cmt">//--- 构建 gg03 表盘, ATR
  if(GaugeCreate("gg03",gg[class="num">3],class="num">30,class="num">0,class="num">180,"gg00",RELATIVE_MODE_VERT,
     CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==class="kw">false)
     class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 设置实体参数
  GaugeSetCaseParameters(gg[class="num">3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
class=class="str">"cmt">//--- 设置刻度和标记的参数
  GaugeSetScaleParameters(gg[class="num">3],class="num">270,class="num">45,class="num">0.001,class="num">0.004,MUL_00001,SCALE_INNER,clrBlack);
  GaugeSetMarkParameters(gg[class="num">3],MARKS_INNER,SIZE_LARGE,class="num">0.001,class="num">9,class="num">3);

「ATR与RSI表盘的刻度及标签配置」

在 MT5 自定义仪表盘里,gg03 表盘专门承载 ATR 读数。调用 GaugeSetRangeParameters 时把强调范围设成 0.002、步长 0.001 并染成黄色,意味着盘面只在 0~0.002 这一窄带高亮,适合抓贵金属分时波动里 2 个微点内的异常扩张。 GaugeSetLegendParameters 对 gg03 写了三行:描述写 "ATR" 置于 (-140,26),数值位填 "5" 放在 (180,14) 且加粗,乘数字段留空。被注释掉的单位行原本想标 "USD",实战里外汇对用点数更直观,所以直接砍掉。 gg04 是 RSI 盘,GaugeCreate 挂到 gg02 下方(RELATIVE_MODE_VERT,左下角偏移 -30,0),刻度 270° 扫 0~100、MUL_10 放大十倍便于裸眼读。标记用 MARKS_INNER、10 等分、长 4 宽 1,表针走 NDCS_SMALL 细针。 gg05 做力度盘,相对 gg03 水平右移 60 像素(RELATIVE_MODE_HOR),量程 -4 到 4、MUL_1 不缩放,标记间隔设为 1。外汇与贵金属杠杆高,这类盘只是辅助观测,信号失真时可能误导仓位,务必回测校验。

MQL5 / C++
  GaugeSetMarkLabelFont(gg[class="num">3],SIZE_LARGE,"Arial",class="kw">false,class="kw">false,DEF_COL_MARK_FONT);
class=class="str">"cmt">//--- 刻度的强调范围
  GaugeSetRangeParameters(gg[class="num">3],class="num">0,true,class="num">0.002,class="num">0.001,clrYellow);
class=class="str">"cmt">//--- 设置文字标签
  GaugeSetLegendParameters(gg[class="num">3],LEGEND_DESCRIPTION,true,"ATR",class="num">7,-class="num">140,class="num">26,"Arial",class="kw">false,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">3],LEGEND_VALUE,true,"class="num">5",class="num">2,class="num">180,class="num">14,"Arial",true,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">3],LEGEND_MUL,true,"",class="num">2,class="num">0,class="num">20,"Arial",true,class="kw">false);
class=class="str">"cmt">//--- 设置表针参数
  GaugeSetNeedleParameters(gg[class="num">3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
class=class="str">"cmt">//--- 构建 gg04 表盘, RSI
  if(GaugeCreate("gg04",gg[class="num">4],-class="num">30,class="num">0,class="num">180,"gg02",RELATIVE_MODE_VERT,
                 CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==class="kw">false)
     class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 设置实体参数
  GaugeSetCaseParameters(gg[class="num">4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
class=class="str">"cmt">//--- 设置刻度和标记的参数
  GaugeSetScaleParameters(gg[class="num">4],class="num">270,class="num">45,class="num">0,class="num">100,MUL_10,SCALE_INNER,clrBlack);
  GaugeSetMarkParameters(gg[class="num">4],MARKS_INNER,SIZE_LARGE,class="num">10,class="num">1,class="num">4);
  GaugeSetMarkLabelFont(gg[class="num">4],SIZE_LARGE,"Arial",class="kw">false,class="kw">false,DEF_COL_MARK_FONT);
class=class="str">"cmt">//--- 设置文字标签
  GaugeSetLegendParameters(gg[class="num">4],LEGEND_DESCRIPTION,true,"RSI",class="num">7,-class="num">140,class="num">26,"Arial",class="kw">false,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">4],LEGEND_VALUE,true,"class="num">2",class="num">2,class="num">180,class="num">16,"Arial",true,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">4],LEGEND_MUL,true,"",class="num">2,class="num">0,class="num">20,"Arial",true,class="kw">false);
class=class="str">"cmt">//--- 设置表针参数
  GaugeSetNeedleParameters(gg[class="num">4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
class=class="str">"cmt">//--- 构建 gg05 表盘, 力度
  if(GaugeCreate("gg05",gg[class="num">5],-class="num">10,class="num">60,class="num">180,"gg03",RELATIVE_MODE_HOR,
                 CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==class="kw">false)
     class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 设置实体参数
  GaugeSetCaseParameters(gg[class="num">5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
class=class="str">"cmt">//--- 设置刻度和标记的参数
  GaugeSetScaleParameters(gg[class="num">5],class="num">270,class="num">45,-class="num">4,class="num">4,MUL_1,SCALE_INNER,clrBlack);
  GaugeSetMarkParameters(gg[class="num">5],MARKS_INNER,SIZE_LARGE,class="num">1,class="num">1,class="num">4);

◍ 把多仪表盘读数接进实时账户与指标

这段逻辑负责把第 6 个仪表盘(索引 5,力度指数)的视觉样式定死,再在每帧把账户与三个指标的真实数值推给对应表盘。外汇与贵金属杠杆高,保证金级别和盈利读数剧烈跳动是常态,仪表盘只是把风险可视化,不预示方向。 先给 gg[5] 设标签字体为 Arial、SIZE_LARGE,并划出两段强调区间:range 0 对应 -1 到 -4 显示海绿色,range 1 对应 1 到 4 显示猩红,负值绿、正值红一眼可辨。图例里把描述写死为 "Force",数值初始串 "5",乘数留空。 随后用 for 循环对 gg[0]~gg[5] 各调一次 GaugeRedraw 并写入 0 值,相当于初始化六表盘。真正更新在后面:点差取 spread[rates_total-1],盈利取 AccountInfoDouble(ACCOUNT_PROFIT),保证金级别取 ACCOUNT_MARGIN_LEVEL,分别喂给 gg[2]、gg[1]、gg[0]。 ATR、RSI、Force 三个句柄走同一套路:BarsCalculated 大于 0 才用 CopyBuffer 取最新 1 根缓冲,失败就 Print 报错,成功就 GaugeNewValue 推给 gg[3]、gg[4]、gg[5]。开 MT5 把这段塞进 OnCalculate 末尾,就能看到六表盘随 tick 刷新;若某表盘不动,先查对应 CopyBuffer 是否返回 -1。

MQL5 / C++
  GaugeSetMarkLabelFont(gg[class="num">5],SIZE_LARGE,"Arial",class="kw">false,class="kw">false,DEF_COL_MARK_FONT);
class=class="str">"cmt">//--- 刻度的强调范围
  GaugeSetRangeParameters(gg[class="num">5],class="num">0,true,-class="num">1,-class="num">4,clrMediumSeaGreen);
  GaugeSetRangeParameters(gg[class="num">5],class="num">1,true,class="num">1,class="num">4,clrCrimson);
class=class="str">"cmt">//--- 设置文字标签
  GaugeSetLegendParameters(gg[class="num">5],LEGEND_DESCRIPTION,true,"Force",class="num">7,-class="num">140,class="num">20,"Arial",class="kw">false,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">5],LEGEND_VALUE,true,"class="num">5",class="num">2,class="num">180,class="num">14,"Arial",true,class="kw">false);
  GaugeSetLegendParameters(gg[class="num">5],LEGEND_MUL,true,"",class="num">3,class="num">0,class="num">10,"Arial",true,class="kw">false);
class=class="str">"cmt">//--- 设置表针参数
  GaugeSetNeedleParameters(gg[class="num">5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);
class=class="str">"cmt">//--- 绘制表盘
  for(class="type">int i=class="num">0; i<class="num">6;i++)
    {
      GaugeRedraw(gg[i]);
      GaugeNewValue(gg[i],class="num">0);
    }
class=class="str">"cmt">//--- 更新读数
class=class="str">"cmt">//--- 点差
  GaugeNewValue(gg[class="num">2],spread[rates_total-class="num">1]);
class=class="str">"cmt">//--- 当前盈利   
  class="type">class="kw">double profit=AccountInfoDouble(ACCOUNT_PROFIT);
  GaugeNewValue(gg[class="num">1],profit);
class=class="str">"cmt">//--- 保证金级别
  class="type">class="kw">double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
  GaugeNewValue(gg[class="num">0],margin_level);
class=class="str">"cmt">//--- 指示器 ATR
  calculated=BarsCalculated(handle_ATR);
  if(calculated>class="num">0)
    {
      class="type">class="kw">double ival[class="num">1];
      if(CopyBuffer(handle_ATR,class="num">0,class="num">0,class="num">1,ival)<class="num">0)
         Print("ATR CopyBuffer 错误");
      else
         GaugeNewValue(gg[class="num">3],ival[class="num">0]);
    }
class=class="str">"cmt">//--- 指示器 RSI
  calculated=BarsCalculated(handle_RSI);
  if(calculated>class="num">0)
    {
      class="type">class="kw">double ival[class="num">1];
      if(CopyBuffer(handle_RSI,class="num">0,class="num">0,class="num">1,ival)<class="num">0)
         Print("RSI CopyBuffer 错误");
      else
         GaugeNewValue(gg[class="num">4],ival[class="num">0]);
    }
class=class="str">"cmt">//--- 指示器力度指数
  calculated=BarsCalculated(handle_Force);
  if(calculated>class="num">0)
    {
      class="type">class="kw">double ival[class="num">1];
      if(CopyBuffer(handle_Force,class="num">0,class="num">0,class="num">1,ival)<class="num">0)
         Print("力度指数 CopyBuffer 错误");
      else
         GaugeNewValue(gg[class="num">5],ival[class="num">0]);
    }

表针重绘的 CPU 账本

表针层在每次读数刷新时都会整层重绘,高频行情下甚至可能每秒触发数次,所以填充方式的资源开销不是小事。写个脚本把每种画法压测 1000 次,直接量出耗时毫秒数,比肉眼猜靠谱。 压测结果很直白:带边缘抗锯齿的填充画法,耗时是普通无抗锯齿填充的上百倍,也比只画抗锯齿轮廓不填充的做法慢几十倍。视觉上的顺滑,在 CPU 账单上确实标了价。 实盘里如果图表挂了多个自定义 Canvas 表针,又碰上外汇或贵金属这种高波动品种,重绘卡顿可能直接拖慢下单响应。开 MT5 跑下面这段,把 cycles 调到和你真实表针数量同量级,就能看到自己的机器扛不扛得住。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 test_fill.mq5 |
class=class="str">"cmt">//|                                 Copyright class="num">2015, Decanium |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2015, Decanium"
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#include <Canvas/Canvas2.mqh>
CCanvas canvas;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 脚本程序开始函数                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
   Print("***** 开始测试 *****");
class=class="str">"cmt">//---
   class="type">class="kw">string ObjName="test";
   ObjectDelete(class="num">0,ObjName);
   canvas.CreateBitmapLabel(ObjName,class="num">10,class="num">10,class="num">400,class="num">400,COLOR_FORMAT_ARGB_NORMALIZE);
class=class="str">"cmt">//---
   class="type">int x[class="num">3]={class="num">200,class="num">185,class="num">215};
   class="type">int y[class="num">3]={class="num">70, class="num">250,class="num">250};
   class="type">int cycles=class="num">1000;
   class="type">uint col=ColorToARGB(clrRed,class="num">255);
   class="type">uint c1,c2;
class=class="str">"cmt">//--- 测试采用边缘抗锯齿填充区域
   canvas.Erase();
   c1=GetTickCount();
   for(class="type">int i=class="num">0; i<cycles; i++)
     {
      canvas.Fill(class="num">10, class="num">10, col);
      canvas.LineAA2(x[class="num">0], y[class="num">0], x[class="num">1], y[class="num">1], class="num">0);
      canvas.LineAA2(x[class="num">1], y[class="num">1], x[class="num">2], y[class="num">2], class="num">0);
      canvas.LineAA2(x[class="num">2], y[class="num">2], x[class="num">0], y[class="num">0], class="num">0);
      canvas.Fill2(class="num">10, class="num">10, class="num">0);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("填充 AA: ",c2-c1," 毫秒, ",cycles," 循环, ",
         DoubleToString(class="type">class="kw">double(c2-c1)/class="type">class="kw">double(cycles),class="num">2)," 每循环毫秒");
class=class="str">"cmt">//--- 测试抗锯齿等高线但不填充
   canvas.Erase();
   c1=GetTickCount();
   for(class="type">int i=class="num">0; i<cycles; i++)
     {
      canvas.LineAA2(x[class="num">0], y[class="num">0], x[class="num">1], y[class="num">1], col);
      canvas.LineAA2(x[class="num">1], y[class="num">1], x[class="num">2], y[class="num">2], col);
      canvas.LineAA2(x[class="num">2], y[class="num">2], x[class="num">0], y[class="num">0], col);
     }
   c2=GetTickCount();

「无抗锯齿填充的耗时实测」

在 MT5 的 Canvas 绘制里,关闭抗锯齿做三角填充和线段描绘,耗时会明显低于开启 AA 的版本。上面这段循环用 GetTickCount 掐表,把 cycles 次填充的总毫秒数和单次均值直接打印到日志。 具体看输出:假设 cycles=10000,日志会给出『不填充 AA: X 毫秒, 10000 循环, Y 每循环毫秒』以及『填充: X2 毫秒, 10000 循环, Y2 每循环毫秒』两组数。你把自己的 cycles 调成 5000 或 50000,重跑就能对比不同负载下每循环毫秒的线性变化。 外汇与贵金属图表叠加大量图形时,这种微秒级差异可能在高频刷新中累积成卡顿,属于高风险环境下的性能隐患,建议实盘前在策略测试器里先验证。

MQL5 / C++
  canvas.Update(true);
  Print("不填充 AA: ",c2-c1," 毫秒, ",cycles," 循环, ",
         DoubleToString(class="type">class="kw">double(c2-c1)/class="type">class="kw">double(cycles),class="num">2)," 每循环毫秒");
class=class="str">"cmt">//--- 测试不使用抗锯齿填充区域
  canvas.Erase();
  c1=GetTickCount();
  for(class="type">int i=class="num">0; i<cycles; i++)
    {
      canvas.FillTriangle(x[class="num">0],y[class="num">0],x[class="num">1],y[class="num">1],x[class="num">2],y[class="num">2],col);
      canvas.LineAA2(x[class="num">0], y[class="num">0], (x[class="num">1]+x[class="num">2])/class="num">2, y[class="num">1], col);
    }
  c2=GetTickCount();
  canvas.Update(true);
  Print("填充: ",c2-c1," 毫秒, ",cycles," 循环, ",
         DoubleToString(class="type">class="kw">double(c2-c1)/class="type">class="kw">double(cycles),class="num">2)," 每循环毫秒");
}

◍ 一点提醒

这套表盘绘制程序库的核心目的,是让 EA 或指标直接挂上可视化表盘,而不必自己啃几何与绘图细节。是否真的省事,得你在 MT5 里拖进 gauge_graph.mqh 实测才知道。 资源占用是绕不开的坑。若在 OnCalculate() 里跑耗时计算,终端有概率直接崩。折中方案是用抗锯齿无填充去画表针,既能看清又不至于把 CPU 吃满。 下面这段是设置主值小数位与写入数值的调用,value_radius 和 value_angle 按你画布尺寸调,profit 换成实际浮点即可: 外汇与贵金属波动剧烈、杠杆高风险大,任何可视化辅助都只是帮你读数据,不构成方向判断。

MQL5 / C++
GaugeSetLegendParameters(gauge, LEGEND_VALUE, true, "class="num">1", value_radius, value_angle, class="num">10, "Arial", class="kw">false, class="kw">false);
GaugeNewValue(gauge, profit);
交给小布盯盘看盘口
这些表盘诊断与资源占用的对比,小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同挂件对帧率的影响,把重复劳动交给小布,你专注决策。

常见问题

相对定位的原点跟随锚点边角生成,若未在处理图表事件时调用 GaugeRelocation 重算位置,缩放或滚动会导致表盘脱离原参考对象。
小布盯盘内置的 AIGC 看板可呈现同类可视化挂件与资源评估,若你导出自用 EA 的表盘参数,可在品种页对照帧率与布局表现。
倾向分开处理:GaugeNewValue 更新数据缓存,GaugeRedraw 才触发像素重绘,高频数据下只更缓存可明显降低资源强度。
外汇贵金属波动快、高风险,抗锯齿让刻度边缘更顺眼,但会占用更多 CPU,资源评估段建议先测再挂实盘。
它决定表针填充风格,如单色或渐变,配合 GaugeSetNeedleParameters 可调整中心样式与长度,具体见函数小节。