来自专业程序员的提示(第二部分):在智能交易系统、脚本和外部程序之间存储和交换参数(基础篇)
「EA、脚本与外部程序间的参数互通」
在 MT5 里,智能交易系统、脚本和独立外部程序并非孤岛——它们可以通过全局变量(GlobalVariable)和文件两种通道交换参数。前者活在终端内存,随终端关闭而消失;后者落盘到终端目录,适合跨进程长期传递。 一个常被忽略的事实是:全局变量名最大 63 字符,值类型为 double,写冲突时后写覆盖前写,没有锁机制,所以多 EA 并发读写要自己定协议。 下例演示脚本向全局变量写入一个double参数,EA 侧用 GlobalVariableGet 同名读取即可完成一次轻量握手。外汇与贵金属杠杆高,自动化参数误写可能瞬间放大敞口,回测与实盘前务必隔离命名空间。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script to store a parameter as a global variable | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class="type">class="kw">string name="EA_Param_Slippage"; class=class="str">"cmt">// 全局变量名,最长63字符 class="type">class="kw">double value=class="num">3.5; class=class="str">"cmt">// 要传递的双精度参数,例如滑点 GlobalVariableSet(name,value); class=class="str">"cmt">// 写入终端全局变量区,无锁覆盖 } class=class="str">"cmt">//+------------------------------------------------------------------+
重启后怎么把参数找回来
MT5 退出再开,内存里的变量全清零,这是做 EA 和脚本的人绕不开的坑。本文只聊一件事:终端重启之后,参数从哪里原样捞回来。 我自己的 Cayman 项目里,常用的落点有这几类:终端全局变量、图形对象、订单注释、文本文件、应用程序设置(ini)、分析器参数。每一类在重连后的存活能力不一样,全局变量和文本文件最稳,图形对象依赖图表不被删。 把脚本跑出来的参数塞给智能交易系统,或者甩给外部程序、再从小程序收回来,甚至推到手机端,都属于「跨会话传参」的范畴。下面这些路径在 Cayman 的真实代码片段里都验证过,开 MT5 建个脚本就能复现。
◍ 参数该塞进哪种存储容器
检测烛条形态时,零号柱线时间这类参数适合在新柱线出现后只评估一次;交易级别参数可由脚本传给 EA,EA 再建一个仅在新柱线后启动的级别分析器。用户偏好如颜色、绘图规则则适合一次性写进设置文件。 MQL5 下可选四种落点:终端全局变量、图形对象、订单注释、文本文件。它们的容量和寿命差异很大,选错会直接导致策略状态丢失。 终端全局变量存双精度,跨图表可见,但上次调用后只保 4 周;图形对象能挂任意字符串(≤63 字符),仅限当前图表、随图表关闭消失;订单注释字符串上限 23 字符,跟单走、 terminal 关了还在;文本文件无限制、按文件自身寿命算。外汇与贵金属波动剧烈、高风险,存储方案要先想清重启后状态能否续上。
「用一个全局变量塞下多个整数」
MT5 的终端全局变量能被任意图表读取,命名时把 ChartId、Symbol 或 Period 拼进变量名就能圈定作用范围。但类型改不了,文本也存不进去,只能落 double。 double 占 8 字节即 64 位,这就留了操作空间:把几个小整数按位打包进同一个变量。关键是先算清每个值的最大位长,别让位移溢出处挨着。 下面这段代码把 10(8 位)、300(16 位)、true(1 位)压成 25 位,剩下 39 位空闲。打包用左移留坑,解包用右移加位掩码 0xFF / 0xFFFF / 0x1 抠回来,跑完若一致会打印 OK。外汇与贵金属交易用全局变量做跨 EA 通信时,需注意终端异常退出可能残留脏数据,属高风险环节。 别把正态当圣经 位长算错一步,解包出来就是另一组数。动手前先拿纸笔排一遍 8+16+1 的位移边界,再贴进 MT5 跑 OnStart 验证。
class=class="str">"cmt">// ----------------------------------------------------------------------------- class=class="str">"cmt">// 将存至/取自全局变量的整数值打包/解包的示例 | class=class="str">"cmt">// 运用按位运算 | class=class="str">"cmt">// ----------------------------------------------------------------------------- class="type">void OnStart() { class="type">int value10 = class="num">10; class=class="str">"cmt">// 最大 = class="num">255 (class="num">8 位) class="type">int value20 = class="num">300; class=class="str">"cmt">// 最大 = class="num">65535 (class="num">16 位) class="type">bool value30 = true; class=class="str">"cmt">// 最大 = class="num">1 (class="num">1 位) class=class="str">"cmt">// 将值打包成 class="num">25 位 (class="num">8+class="num">16+class="num">1) class=class="str">"cmt">// class="num">39 位 (class="num">64-class="num">25) 保持空闲 class="type">class="kw">ulong packedValue = (value10 << class="num">17) + class=class="str">"cmt">// 保留空间 (class="num">16+class="num">1) 对于 value20, value30 (value20 << class="num">1) + class=class="str">"cmt">// 保留空间 (class="num">1) 对于 value30 value30; class=class="str">"cmt">// 保存全局变量 class="type">class="kw">string nameGVar = "temp"; GlobalVariableSet(nameGVar, packedValue); class=class="str">"cmt">// 读取全局变量 packedValue = (class="type">class="kw">ulong)GlobalVariableGet(nameGVar); class=class="str">"cmt">// 数值解包 class=class="str">"cmt">// 0xFF, 0xFFFF, 0x1 - 最大值的位掩码 class="type">int value11 = (class="type">int)((packedValue >> class="num">17) & 0xFF); class="type">int value21 = (class="type">int)((packedValue >> class="num">1) & 0xFFFF); class="type">bool value31 = (class="type">bool)(packedValue & 0x1); class=class="str">"cmt">// 比较值 if (value11 == value10 && value21 == value20 && value31 == value30) Print("OK"); else PrintFormat("0x%X / 0x%X /0x%X / 0x%X", packedValue, value11, value21, value31); }
用隐藏图形对象存脚本参数
想把脚本参数塞进图表又不想碍眼?直接建一个图形对象,把 OBJPROP_PRICE 设成 0,它在视觉上就消失了,但程序里照样能读能写。 为防图表被关或模板切换导致丢失,把这个对象一并存进图表模板更稳。 读取逻辑很直接:先检测对象在不在,在就提参数;不在就落默认值。这样脚本重开不用反复手填,MT5 里拖一下模板就能复现上次的运行配置。
◍ 用 23 字符注释给订单打标签
MT5 里订单注释最多只能写 23 个字符,这点长度限制很硬,超了就截断了。别小看这 23 个字符,把它按固定结构排布,就能把一笔单子的来龙去脉全塞进去。
一个可直接抄的写法是 SOP/H1/SS/C2/Br/Br/Br:SOP 是发单脚本名(如 SendOrderByPlan),H1 是信号周期,SS 是挂单类型(Sell Stop),C2 是平仓算法编号,后面三个 Br 分别是 D1、H4 和信号周期内的趋势方向(Br 代表看跌)。一套下来刚好 23 字符以内。
实战里我是这么用:挂单触发后,从注释里抠出 C2 对应的平仓算法,动态挂一个 AnalyserVirtSL 虚拟止损分析器,让它按预设条件自动平仓。外汇和贵金属波动大、滑点频繁,这种用法能把策略逻辑和订单绑定,比靠 EA 全局变量更抗重启和断线。
把恢复参数写进注释,是少数既可靠又通用的做法。访问类只需写一次,之后在任何终端、任何时间都能靠解析注释把单子状态重建出来。