从头开始开发智能交易系统(第 16 部分):访问 web 上的数据(II)·综合运用
◍ 用终端全局变量搭一条内部信道
MT5 里两个独立程序(比如服务 + EA)想互通数据,不必走文件或网络,直接用终端全局变量就能做一条内部信道。两端约定同一个变量名,一端写、一端读,本质就是最简化的客户机-服务器模型。 服务端先检查变量是否存在:用 GlobalVariableCheck 判断,没有就靠 GlobalVariableTemp 建一个临时变量,再用 GlobalVariableSet 写入数值。下面这段服务代码每秒把计数器加 1 并写进名为 InnerChannel 的全局变量,扮演服务器角色。 EA 这边不干别的,靠 OnTimer 每秒触发一次:GlobalVariableCheck 确认变量在,就 GlobalVariableGet 读出来 Print 到终端。实测把 EA 挂上图表后,终端日志会每秒吐出一个递增双精度数,证明信道已通。 别把正态当圣经:全局变量是临时性的,服务停了再重启若没重建,EA 读到的可能是上次残留值;代码里每次循环都先 Check 再 Temp,就是防这个坑。外汇贵金属行情瞬变,这类内部通信延迟通常 <1 秒,但高频场景仍要自测抖动。
class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define def_GlobalNameChannel "InnerChannel" class=class="str">"cmt">//+------------------------------------------------------------------+ @class="kw">property service @class="kw">property copyright "Daniel Jose" @class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Inner Channel.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class="type">class="kw">double count = class="num">0; while (!IsStopped()) { if (!GlobalVariableCheck(def_GlobalNameChannel)) GlobalVariableTemp(def_GlobalNameChannel); GlobalVariableSet(def_GlobalNameChannel, count); count += class="num">1.0; Sleep(class="num">1000); } } class=class="str">"cmt">//+------------------------------------------------------------------+ @class="kw">property copyright "Daniel Jose" @class="kw">property description "Testing internal channel\nvia terminal global variable" @class="kw">property version "class="num">1.03" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Inner Channel.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { EventSetTimer(class="num">1); class="kw">return INIT_SUCCEEDED; } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { EventKillTimer(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTimer() { class="type">class="kw">double value; if (GlobalVariableCheck(def_GlobalNameChannel)) { GlobalVariableGet(def_GlobalNameChannel, value); Print(value); } } class=class="str">"cmt">//+------------------------------------------------------------------+ @class="kw">property service @class="kw">property copyright "Daniel Jose"
用 MT5 抓外网报价写进全局变量
这段脚本演示了如何让 MT5 脱离图表直接去外网扒一个指数数值,再塞进全局变量给 EA 或指标共用。它依赖一个自定义的 Inner Channel.mqh,里面应当定义了 def_GlobalNameChannel 这类宏,否则编译都过不了。 OnStart 里是个不死循环:只要终端没点停止,就先确认全局变量在不在,不在就建一个临时变量;随后调 GetDataURL 去 tradingeconomics.com/stocks 抓页面,目标串是 INDU:IND(道指),超时设 100 毫秒,这种短超时在实盘网络下大概率会失败,仅适合本地低延迟测。 抓回来的字符串转成 double 写进全局变量,然后 Sleep(1000) 歇一秒再跑。注意外汇和贵金属受杠杆与跳空影响属高风险,这类外部依赖若断网,GlobalVariableSet 会写进上一次的脏值,下游策略可能误判。 GetDataURL 是核心:先用 WebRequest 发 GET,失败返 "Bad";接着拿 "<!doctype html>" 做头部校验(偏移 2),再拿 "INDU:IND" 做锚点校验(偏移 172783),任一不对就返错。最后从偏移 173474 起跳过空格,一直读到 0x0D(回车)为止,把中间字符拼成数值字符串返回。想验证的话,把 iPos/iInfo 那两个Magic Number 按你抓的页面源码重新数一遍即可。
class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Inner Channel.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class="type">class="kw">string szRet; while (!IsStopped()) { if (!GlobalVariableCheck(def_GlobalNameChannel)) GlobalVariableTemp(def_GlobalNameChannel); szRet = GetDataURL("https:class=class="str">"cmt">//tradingeconomics.com/stocks", class="num">100, "<!doctype html>", class="num">2, "INDU:IND", class="num">172783, class="num">173474, 0x0D); GlobalVariableSet(def_GlobalNameChannel, StringToDouble(szRet)); Sleep(class="num">1000); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string GetDataURL(class="kw">const class="type">class="kw">string url, class="kw">const class="type">int timeout, class="kw">const class="type">class="kw">string szTest, class="type">int iTest, class="kw">const class="type">class="kw">string szFind, class="type">int iPos, class="type">int iInfo, class="type">char cLimit) { class="type">class="kw">string headers, szInfo = ""; class="type">char post[], charResultPage[]; class="type">int counter; if (WebRequest("GET", url, NULL, NULL, timeout, post, class="num">0, charResultPage, headers) == -class="num">1) class="kw">return "Bad"; for (class="type">int c0 = class="num">0, c1 = StringLen(szTest); c0 < c1; c0++) if (szTest[c0] != charResultPage[iTest + c0]) class="kw">return "Failed"; for (class="type">int c0 = class="num">0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0]) class="kw">return "Error"; for (counter = class="num">0; charResultPage[counter + iInfo] == 0x20; counter++); for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]); class="kw">return szInfo; } class=class="str">"cmt">//+------------------------------------------------------------------+
「记住这一条就够了」
MT5 的通信信道在前两篇里只露了冰山一角,本篇演示的 Script 与 EA 配套文件合计仅 5.42 KB(3.03+2.39),说明轻量封装就能把外部行情接进终端。 真要落地,先想清楚你打算绕开哪类平台未覆盖的数据——有交易者曾在评论里提到用这类通道引加密货币报价进策略,外汇和贵金属本身杠杆高、跳空频繁,接外部源更要防延迟错位。 路径没有标准答案,但每条路的成本和边界得自己跑一遍才数得清;下一篇若往个性化走,这套信道大概率还是底层的管子。