MQL5 Cookbook: 自定义信息面板上的仓位属性·综合运用
(3/3)·手动跑脚本看仓位太笨?用 EA + 图形对象做常驻面板,订单一来就自己更新
持仓信息面板的逐行标签铺设
在 MT5 的自定义信息面板里,持仓关键字段是靠连续调用 CreateLabel 铺出来的。下面这段从建仓价位一直排到类型标签,覆盖了单子最核心的 10 个观察维度:建仓价、当前价、利润、交易量、止损价、获利价、时间、编号、类型。 每一行都是「名称标签 + 数值标签」成对出现:名称固定在左列 x_first_column,数值取右列 x_second_column,纵向坐标由 y_prop_array 索引控制。索引从 5 到 13 连续递增,说明面板布局是严格按数组顺序向下排的,改一处 y 偏移就可能让整列错位。 最后一行只写了类型名称标签(索引 13),没跟数值标签,原文在此截断。若你要补全,得补一行 pos_prop_values[13] 配合 GetValInfoPanel(13) 调用,否则面板右侧这一行会空着。外汇与贵金属杠杆高,面板数值仅作盯盘参考,实际下单仍以终端成交为准。
CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">4],GetValInfoPanel(class="num">4),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">4],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">5],"建仓价位 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">5],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">5],GetValInfoPanel(class="num">5),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">5],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">6],"当前价位 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">6],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">6],GetValInfoPanel(class="num">6),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">6],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">7],"利润 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">7],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">7],GetValInfoPanel(class="num">7),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">7],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">8],"交易量 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">8],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">8],GetValInfoPanel(class="num">8),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">8],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">9],"止损价位 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">9],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">9],GetValInfoPanel(class="num">9),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">9],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">10],"获利价位 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">10],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">10],GetValInfoPanel(class="num">10),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">10],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">11],"时间 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">11],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">11],GetValInfoPanel(class="num">11),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">11],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">12],"编号 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">12],class="num">2); CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">12],GetValInfoPanel(class="num">12),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">12],class="num">2); class=class="str">"cmt">//--- CreateLabel(class="num">0,class="num">0,pos_prop_names[class="num">13],"类型 :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[class="num">13],class="num">2);
◍ 持仓属性逐行回填与取值逻辑
信息面板每一行的纵向坐标靠一个循环预填:第 0 行直接用基准 y_property,其余行按 line_height 递增偏移,这样后续标签绘制不会重叠。 CreateLabel 那行把第 13 号属性画到第二列指定坐标后,立刻调 ChartRedraw 强制重画,避免 MT5 图表缓存导致标签迟滞。外汇与贵金属杠杆高,面板数值仅作辅助参考,实际平仓仍以券商成交回报为准。 GetPropertyValue 用 switch 按编号返回持仓字段:0 是品种名,1 是 magic 号转整型字符串,2 无注释时回 '-',3~4 的 swap 与 commission 固定保留两位小数,5~6 的开仓价与当前价跟随 _Digits 精度。复制这段代码到 EA 里,把 INFOPANEL_SIZE 改成你自己的行数就能直接复用。
CreateLabel(class="num">0,class="num">0,pos_prop_values[class="num">13],GetValInfoPanel(class="num">13),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[class="num">13],class="num">2); class=class="str">"cmt">//--- ChartRedraw(); class=class="str">"cmt">// 重画图表 } class=class="str">"cmt">//--- 使用信息面板中每一行的坐标填充数组 for(class="type">int i=class="num">0; i<INFOPANEL_SIZE; i++) { if(i==class="num">0) y_prop_array[i]=y_property; else y_prop_array[i]=y_property+line_height*i; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 返回仓位属性值的字符串 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string GetPropertyValue(class="type">int number) { class=class="str">"cmt">//--- 没有持仓或某属性的符号 class=class="str">"cmt">// 比如没有注释,止损价位或者获利价位 class="type">class="kw">string empty="-"; class=class="str">"cmt">//--- 如果有持仓,返回所请求属性的值 if(pos_open) { class="kw">switch(number) { case class="num">0 : class="kw">return(pos_symbol); class="kw">break; case class="num">1 : class="kw">return(IntegerToString((class="type">int)pos_magic)); class="kw">break; class=class="str">"cmt">//--- 如有注释返回注释的值,否则返回没有注释的符号 case class="num">2 : class="kw">return(pos_comment!="" ? pos_comment : empty); class="kw">break; case class="num">3 : class="kw">return(DoubleToString(pos_swap,class="num">2)); class="kw">break; case class="num">4 : class="kw">return(DoubleToString(pos_commission,class="num">2)); class="kw">break; case class="num">5 : class="kw">return(DoubleToString(pos_price,_Digits)); class="kw">break; case class="num">6 : class="kw">return(DoubleToString(pos_cprice,_Digits)); class="kw">break; case class="num">7 : class="kw">return(DoubleToString(pos_profit,class="num">2)); class="kw">break; case class="num">8 : class="kw">return(DoubleToString(pos_volume,class="num">2)); class="kw">break;
「面板清理与EA生命周期钩子」
上面这段 switch 的收尾逻辑很直白:case 9 到 13 分别把止损、止盈、开仓时间、订单 ticket 和持仓类型转成字符串返回,default 一律给空串。若当前无任何持仓,函数直接返回 '-',面板格子不会显示脏数据。 DeleteInfoPanel() 负责把背景、抬头以及 INFOPANEL_SIZE 个属性名和值对象逐个用 DeleteObjectByName 清除,最后调 ChartRedraw() 强制重绘。实盘里若面板对象没删干净,MT5 图表会残留文本框,肉眼就能看出 EA 卸载不彻底。 OnInit 只做一件事:调 GetPositionProperties() 读属性并画面板,返回 0 表示初始化成功。OnDeinit 则先打印去初始化原因文本,当 reason 等于 REASON_REMOVE(即从图表移除 EA)时才调 DeleteInfoPanel() 清场——如果是换周期或重编译触发去初始化,面板对象会保留在图上。 外汇与贵金属杠杆高、滑点跳空频繁,EA 卸载时残留对象可能干扰后续手动下单,建议自己在 MT5 里挂上 EA 再拖离图表,确认对象树里 InfoPanel 前缀全清空。
case class="num">9 : class="kw">return(pos_sl!=class="num">0.0 ? DoubleToString(pos_sl,_Digits) : empty); class="kw">break; case class="num">10 : class="kw">return(pos_tp!=class="num">0.0 ? DoubleToString(pos_tp,_Digits) : empty); class="kw">break; case class="num">11 : class="kw">return(TimeToString(pos_time,TIME_DATE|TIME_MINUTES)); class="kw">break; case class="num">12 : class="kw">return(IntegerToString((class="type">int)pos_id)); class="kw">break; case class="num">13 : class="kw">return(PositionTypeToString(pos_type)); class="kw">break; class="kw">default : class="kw">return(empty); } } class=class="str">"cmt">//--- class=class="str">"cmt">//如果没有持仓,返回缺少持仓的符号 "-" class="kw">return(empty); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 删除信息面板 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void DeleteInfoPanel() { DeleteObjectByName("InfoPanelBackground"); class=class="str">"cmt">// 删除面板背景 DeleteObjectByName("InfoPanelHeader"); class=class="str">"cmt">// 删除面板抬头 class=class="str">"cmt">//--- 删除仓位属性及其值 for(class="type">int i=class="num">0; i<INFOPANEL_SIZE; i++) { DeleteObjectByName(pos_prop_names[i]); class=class="str">"cmt">// 删除属性 DeleteObjectByName(pos_prop_values[i]); class=class="str">"cmt">// 删除值 } class=class="str">"cmt">//--- ChartRedraw(); class=class="str">"cmt">// 重画图表 } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 读取属性并设置面板 GetPositionProperties(); class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA去初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- 在日志中打印去初始化原因 Print(GetDeinitReasonText(reason)); class=class="str">"cmt">//--- 当从图表上移除时 if(reason==REASON_REMOVE) class=class="str">"cmt">//--- 从图表上删除所有有关信息面板的对象 DeleteInfoPanel(); }
把EA卸载原因翻译成人话
EA在MT5图表上停止运行,背后总有具体触发源。系统通过去初始化原因码(0~9)告知你到底是手动移除、重编译,还是账户切换。 下面这段处理函数把 reason_code 映射成可读文本,直接挂到面板或日志里就能看到。外汇与贵金属品种换账户或换模板时EA可能被静默卸载,属于高风险环境下的常见坑。 [CODE] 逐行拆解: string GetDeinitReasonText(int reason_code) — 接收整型原因码,返回描述串 string text=""; — 先置空结果 switch(reason_code) — 按码分支 case REASON_PROGRAM: 0,EA自己调ExpertRemove()停的 case REASON_REMOVE: 1,程序从图表被移除 case REASON_RECOMPILE: 2,EA被重新编译 case REASON_CHARTCHANGE: 3,品种或周期变了 case REASON_CHARTCLOSE: 4,图表关了 case REASON_PARAMETERS: 5,输入参数被改 case REASON_ACCOUNT: 6,切了账户 case REASON_TEMPLATE: 7,套了别的模板 case REASON_INITFAILED: 8,OnInit返回零初始化失败 case REASON_CLOSE: 9,终端关闭 default: 原因不明 开MT5把这个函数塞进自己的EA,在OnDeinit里打印GetDeinitReasonText(reason),下次策略莫名离线就能立刻定位。
class="type">class="kw">string GetDeinitReasonText(class="type">int reason_code) { class="type">class="kw">string text=""; class=class="str">"cmt">//--- class="kw">switch(reason_code) { case REASON_PROGRAM : class=class="str">"cmt">// class="num">0 text="EA 交易通过调用 ExpertRemove()函数停止工作."; class="kw">break; case REASON_REMOVE : class=class="str">"cmt">// class="num">1 text="&class="macro">#x27;"+EXPERT_NAME+"&class="macro">#x27; 程序已被从图表上移除."; class="kw">break; case REASON_RECOMPILE : class=class="str">"cmt">// class="num">2 text="&class="macro">#x27;"+EXPERT_NAME+"&class="macro">#x27; 程序已经被重新编译."; class="kw">break; case REASON_CHARTCHANGE : class=class="str">"cmt">// class="num">3 text="图表的交易品种或周期已经改变."; class="kw">break; case REASON_CHARTCLOSE : class=class="str">"cmt">// class="num">4 text="图表被关闭."; class="kw">break; case REASON_PARAMETERS : class=class="str">"cmt">// class="num">5 text="输入参数被用户改变."; class="kw">break; case REASON_ACCOUNT : class=class="str">"cmt">// class="num">6 text="激活了一个不同的账户."; class="kw">break; case REASON_TEMPLATE : class=class="str">"cmt">// class="num">7 text="应用了一个不同的图表模板."; class="kw">break; case REASON_INITFAILED : class=class="str">"cmt">// class="num">8 text="有标志表明OnInit() 处理函数返回零值."; class="kw">break; case REASON_CLOSE : class=class="str">"cmt">// class="num">9 text="终端被关闭."; class="kw">break; class="kw">default : text="原因不明."; } class=class="str">"cmt">//--- }
◍ 文本返回前的收口处理
这段代码片段出现在指标或脚本的字符串拼接逻辑末尾,仅有一行 return text;,作用是把已组装好的字符串直接返回给调用方。
从工程角度看,它意味着前面所有格式化、条件判断已在 text 变量中定型,此处不做二次加工。若你在 MT5 自定义指标里看到类似收尾,可直接在 return 前插一行 Print(text); 验证输出内容是否符合预期。
外汇与贵金属市场高风险,任何输出逻辑改动都应在策略测试器内先跑历史数据确认无报错。
class="kw">return text;
}「记住这一条就够了」
这套仓位属性面板的核心源码在附带的 positionpropertiespanel.mq5 里,文件 81.93 KB,直接丢进 MT5 的 MetaEditor 就能编译跑起来。 作者把函数拆成独立模块,后面几篇 Cookbook 会复用或改写,比如策略测试器里分析仓位、获取全部仓位属性,都是同一套底子往上加东西。 如果你刚上手 MQL5,别跳着看,顺着系列顺序读,逻辑链才接得上;有经验的可以从新发布的改版文切入。外汇和贵金属波动剧烈,高杠杆下任何面板都只是辅助,信号误读可能直接吃掉本金。 打开 MT5 把那個 mq5 加载到图表,勾选只看当前品种还是全部品种,先确认你的终端能正常吐出属性再谈二次开发。