来自专业程序员的提示(第二部分):在智能交易系统、脚本和外部程序之间存储和交换参数·综合运用
📘

来自专业程序员的提示(第二部分):在智能交易系统、脚本和外部程序之间存储和交换参数·综合运用

第 3/3 篇

用隐藏对象在脚本与EA间传令

MT5 里想让独立脚本给正在跑的 EA 下指令,又不想走文件或全局变量,可以借用一张隐藏的图表对象当信箱。下面这段把对象的时间框架属性锁成 0,保证它只在当前图表可见、不被多周期复读。 TakeCommand 先按名字找对象,找不到就回 cmdUnknown;找到后把对象文本读进 m_path,把 ZORDER 整数读进 m_cmd,随即删掉对象并完成 Load。也就是说,一条指令自带路径与动作码,用完即焚,不残留。 Dump 则把参数表里每一项用 PrintFormat 打到日志,前缀带 sender 方便在多EA环境里区分来源。实盘前可在策略测试器里跑一次脚本发令,看 EA 日志是否精确打印出 sender 与条目,验证通道通畅。外汇与贵金属波动剧烈,这类自动联动逻辑上线前务必在模拟盘跑够样本。

MQL5 / C++
   ObjectSetInteger(class="num">0, nameObj, OBJPROP_TIMEFRAMES, class="num">0);
}
class=class="str">"cmt">// -----------------------------------------------------------------------------
class=class="str">"cmt">// 从脚本接收命令                                                                 |
class=class="str">"cmt">// -----------------------------------------------------------------------------
NCommand Params::TakeCommand() {
    class="type">class="kw">string nameObj = NAME_OBJECT_CMD;
    if (ObjectFind(class="num">0, nameObj) < class="num">0) class="kw">return cmdUnknown;
    
    m_path = ObjectGetString(class="num">0, nameObj, OBJPROP_TEXT);
    m_cmd = (NCommand)ObjectGetInteger(class="num">0, nameObj, OBJPROP_ZORDER);
    ObjectDelete(class="num">0, nameObj);
    Load(m_path);
    
    class="kw">return m_cmd;
}
class=class="str">"cmt">// -----------------------------------------------------------------------------
class=class="str">"cmt">// | 转储参数                                                                    |
class=class="str">"cmt">// -----------------------------------------------------------------------------
class="type">void Params::Dump(class="type">class="kw">string sender) {
    for (class="type">int j = class="num">0; j < m_items.Total(); j++) {
        PrintFormat("%s / %s", sender, m_items.At(j));
    }
}

◍ 将参数传递给外部程序

不同系统之间的数据交换单元实际上是一个 json 文件。 以前它曾是一个 xml 文件。 json 文件的主要优点是: 易于创建(生成/格式化) 所有高级语言均完美支持 可读性 例如,有一个包含以下字段的 Bar 类:m_time、m_open、m_high、m_low、m_close、m_body。 其中 m_body 是烛条颜色:阳线、阴线或十字星。 Bar 类有一个 ToJson() 方法,它生成一个 json 字符串 我们可以改用 StringFormat,但这会在重新排列或删除数值时导致出问题。 格式化 “\n\t” 可以删除,因为网上有很多 json 格式化服务。 其中之一是 JSON 解析器 。 一次设置有效 json 的接收,并在您需要时调用 bar.ToJson() 函数。 一个外部程序,例如 C# 应用程序,可以将任何复杂度的 json 文件转换为对象。 如何从 MQL 传输一个 json 文件? 这很简单。 例如,将 json 文件加载(保存)到 Files/Json 终端目录。 一个外部程序监视此目录中是否有新文件。 若发现文件,程序读取它,将其转换为对象,并立即删除文件,或将其移动到存档(用于统计)。 [CODE] <span class="keyword">string</span> Bar::ToJson() { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span> <span class="string">"{"</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"\n\t\"symbol\":\""</span> + <span class="predefines">_Symbol</span> + <span class="string">"\","</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"\n\t\"period\":"</span> + <span class="functions">IntegerToString</span>(<span class="predefines">_Period</span>) + <span class="string">","</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"\n\t\"digits\":"</span> + <span class="functions">IntegerToString</span>(<span class="predefines">_Digits</span>) + <span class="string">","</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"\n\t\"timeBar\":\""</span> + TimeToStr(m_time) + <span class="string">"\","</span> + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

「用文本文件给 EA 喂外部参数」

把 json 库硬接进 MQL 程序,或者自己重写一套解析逻辑,都会带来额外维护负担和编译麻烦。更干净的做法是:外部程序写一份纯文本文件,里面用“关键字=参数值”的格式存放指令,MQL 端用前文提到的 Params 类直接读。 智能交易系统和指标都适合做接收方。典型流程是在 OnTick 里调用 CheckExternalCommand(),它负责扫描 Files/ExtCmd 目录;一旦命中文件,就读取内容、按 Params 解析参数、执行对应动作,随后把文件删掉,避免重复触发。 这种机制对自动化协作很有用:比如外部 Python 信号源算完策略,丢个 cmd.txt 进 ExtCmd,MT5 这边下一 tick 就能吃到参数。外汇和贵金属波动剧烈、杠杆高风险大,外部参数若未经校验直接用于下单,可能瞬间放大亏损。 MQL 市场禁止带 DLL 的程序,根子上是安全——DLL 能碰进程内任何东西。文本文件通道绕开了这点,又保留了外部交互能力。

把交易事件推到安卓手机

想在 MT5 跑 EA 时不被绑在屏幕前,可以用 WirePusher 这类安卓推送服务(免费、无插件)把关键事件发到手机。iPhone 用户需自行找同类方案,原文作者未验证 iOS 端可用性。 接入只需四步:手机装好 WirePusher 并记下主屏显示的 ID;在 MT5 的 服务/设置/智能系统/允许 WebRequest 访问的 URL 里加入 https://wirepusher.com;把脚本里 id 参数的星号换成你的 ID;启动脚本。Cayman EA 会在挂单触发、价格突破交易级别、成交平仓三个时刻调用推送函数。 每种通知类型可分配独立提示音。作者曾给盈利平仓设“ta-da”、亏损平仓设“炸弹”,但频繁亏损时被炸弹音搞到疲惫,说明声音策略本身也会影响执行心态。外汇与贵金属杠杆高,这类提醒只解决“知道”,不解决“做对”,信号到手仍要自行判断。 下面这段 MQL5 是最小可跑的推送示例与函数封装,注意 WebRequest 超时设的 3000 毫秒,返回码非 200 会打印错误并返 false。

MQL5 / C++
class="type">void OnStart() {
    class="type">class="kw">string id = "**********"; class=class="str">"cmt">// 在 WirePusher 里填写您的智能手机 id
    WirePusher("Profit $class="num">1000", "Deal", "Closed", id);
}
class=class="str">"cmt">// ------------------------------------------------------------------------------------------------
class=class="str">"cmt">// 通过 WirePusher 网页服务向智能手机发送通知
class=class="str">"cmt">// 在终端的服务/设置/智能系统/允许 WebRequest 访问的 URL 里加入 https://wirepusher.com
class=class="str">"cmt">// message - 通知文本
class=class="str">"cmt">// title - 通知标题(例如,Attention / Alert / Deal)
class=class="str">"cmt">// type - 通知类型(例如,触发挂单/级别突破/平仓)
class=class="str">"cmt">// id - 来自 WirePusher 安卓应用程序的唯一智能手机 id
class=class="str">"cmt">// ------------------------------------------------------------------------------------------------
class="type">bool WirePusher(class="type">class="kw">string message, class="type">class="kw">string title, class="type">class="kw">string type, class="type">class="kw">string id) {
    class="type">char data[]; class=class="str">"cmt">// HTTP 消息主体数据数组
    class="type">char result[]; class=class="str">"cmt">// Web 服务响应数据数组
    class="type">class="kw">string answer; class=class="str">"cmt">// Web 服务响应头
    class="type">class="kw">string url = "https:class=class="str">"cmt">//wirepusher.com/send?id={id}&title={title}&message={message}&type={type}";
    
    StringReplace(url, "{id}", id);
    StringReplace(url, "{type}", type);
    StringReplace(url, "{title}", title);
    StringReplace(url, "{message}", message);
    
    ResetLastError();
    class="type">int rcode = WebRequest("GET", url, NULL, class="num">3000, data, result, answer);
    if (rcode != class="num">200) {
        PrintFormat("%s / error=%i / url=%s / answer=%s / %s", 
            __FUNCTION__, GetLastError(), url, answer, CharArrayToString(result));
        class="kw">return class="kw">false;
    }
    PrintFormat("%s / %s / %s", __FUNCTION__, title, message);
    class="kw">return true;
}

◍ 记住这一条就够了

把 EA 参数落盘到文本文件,是跨系统最稳的存储办法。文件系统在任何操作系统和终端里都被完整支持,不像全局变量或图形对象那样容易因为命名碰撞或线程竞争出问题。 作者给出的路径范例是 Files\Cayman\Params\128968168864101576\exp_05_Lev607A160E_H4.txt,其中 128968168864101576 是图表 ID,靠这个前缀隔离,别的程序基本摸不到你的参数文件。 下面这段脚本是反例:它不停用随机数覆写终端里所有全局变量,若你真在跑实盘的终端上执行,会把正常 EA 的全局变量全冲掉,概率虽低但后果直接。外汇和贵金属本身波动剧烈、杠杆高危,参数被误改可能放大亏损。 [CODE] 里的代码只适合在闲置测试终端里跑着看热闹,别挂到正式环境。参数这东西,写进文件、加好图表 ID 前缀,比什么都省心。

MQL5 / C++
<span class="keyword">class="type">void</span> <span class="functions">OnStart</span>()
  {
  <span class="keyword">class="kw">while</span>(!<span class="functions">IsStopped</span>())
    <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="functions">GlobalVariablesTotal</span>();i>=<span class="number">class="num">0</span>;--i)
      <span class="functions">GlobalVariableSet</span>(<span class="functions">GlobalVariableName</span>(i),<span class="functions">MathRand</span>());
  }

常见问题

用隐藏的图表对象(比如画一条不可见水平线)把参数编码进对象属性,脚本写了EA读,双方都不落盘。
在EA里用文件或管道把参数字符串写给外部程序,外部程序读入后处理再把结果回写,EA轮询读取即可。
小布可以替你监控参数传递文件或对象状态,异常时直接弹提醒,你不用自己老开着看盘窗。
不一定,EA里写个定时器或按键轮询读文件,检测到内容变了就重新加载,不必手动重启。
EA在事件触发时写个带标记的文件或发本地通知,配合手机端监听工具读走并弹通知,纯本地可行。