从头开始开发智能交易系统(第 16 部分):访问 web 上的数据(II)·综合运用
📘

从头开始开发智能交易系统(第 16 部分):访问 web 上的数据(II)·综合运用

第 3/3 篇

◍ 用终端全局变量搭一条内部信道

MT5 里两个独立程序(比如服务 + EA)想互通数据,不必走文件或网络,直接用终端全局变量就能做一条内部信道。两端约定同一个变量名,一端写、一端读,本质就是最简化的客户机-服务器模型。 服务端先检查变量是否存在:用 GlobalVariableCheck 判断,没有就靠 GlobalVariableTemp 建一个临时变量,再用 GlobalVariableSet 写入数值。下面这段服务代码每秒把计数器加 1 并写进名为 InnerChannel 的全局变量,扮演服务器角色。 EA 这边不干别的,靠 OnTimer 每秒触发一次:GlobalVariableCheck 确认变量在,就 GlobalVariableGet 读出来 Print 到终端。实测把 EA 挂上图表后,终端日志会每秒吐出一个递增双精度数,证明信道已通。 别把正态当圣经:全局变量是临时性的,服务停了再重启若没重建,EA 读到的可能是上次残留值;代码里每次循环都先 Check 再 Temp,就是防这个坑。外汇贵金属行情瞬变,这类内部通信延迟通常 <1 秒,但高频场景仍要自测抖动。

MQL5 / C++
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 按你抓的页面源码重新数一遍即可。

MQL5 / C++
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),说明轻量封装就能把外部行情接进终端。 真要落地,先想清楚你打算绕开哪类平台未覆盖的数据——有交易者曾在评论里提到用这类通道引加密货币报价进策略,外汇和贵金属本身杠杆高、跳空频繁,接外部源更要防延迟错位。 路径没有标准答案,但每条路的成本和边界得自己跑一遍才数得清;下一篇若往个性化走,这套信道大概率还是底层的管子。

常见问题

用终端全局变量即可,它是同终端内所有程序共享的内存区,一个 EA 写、另一个 EA 读就能打通,无需文件或网络。
在终端里用 WebRequest 拉取外网数据,解析后写入全局变量,策略 EA 直接读该变量就能拿到外部报价。
可以,小布能持续监控全局变量与外部数据通道状态,异常时直接提醒你,不用自己写轮询代码。
默认关机或重启终端会清除,若需持久化要显式保存或改用文件,别假设它一直活着。
会,没有锁机制,建议约定单一写者或加时间戳校验,避免互相覆盖导致信号错误。