MetaTrader 5 与 Python 的集成:接收和发送数据(基础篇)
📘

MetaTrader 5 与 Python 的集成:接收和发送数据(基础篇)

第 1/2 篇

MT5 与 Python 打通数据通道的起点

MetaTrader 5 从 build 2085 起内置了 Python 接口,官方提供的 MetaTrader5 模块让外部脚本能直接读取报价、持仓与历史柱。对做价格行为量化的人来说,这意味着不用再靠抓屏或导 CSV,就能把实时 tick 喂给自己的 AIGC 模型。 根据社区公开文档统计,该接口文章自 2019-04-03 发布至统计时点已被查看约 14850 次、评论 107 条,说明用 Python 控盘的需求一直很实。外汇与贵金属杠杆高,接口只是工具,信号错了照样爆仓,接入前先在小账户验证。 最基础的连接只需三行:先 import MetaTrader5 as mt5,再 mt5.initialize(),成功返回 True 才能继续。失败多半是终端没开或 DLL 权限没勾,开 MT5 设置里看一眼就能排掉。

◍ 用套接字打通 MT5 与 Python 的任督二脉

单靠 MQL5 处理统计建模或机器学习,基本是被关在功能沙箱里硬扛。Python 在数据处理和算法库上的积累是实打实的,把它拉进交易系统开发,比在 MQL5 里手搓一套分析引擎省力得多。 跨程序交互的方案不少,但套接字(socket)是延迟最低、最灵活的一种。MQL5 标准库自带 Socket 函数组,提供的是操作系统级的低层网络接口,这意味着它和 Python 用的底层机制是同一套,不存在语言壁垒。 实际跑起来走的是 TCP/IP 字节流。MQL5 这边只暴露客户端能力,所以服务端得用 Python 起。创建 TCP 服务器时要把套接字绑到未占用端口上:主机填 0.0.0.0 则监听所有网卡,填 127.0.0.1 或 localhost 就只走本机内循环,进程间单机通信最稳。 外汇与贵金属杠杆高、滑点突发行情多,这种跨语言架构只是工具链,不指向任何收益结论。开 MT5 按 F4 看下 Include\Socket.mqh,确认客户端函数齐活了,再用 Python 的 socket 模块起个本地回环服务端,两边就能对上了。

「用 Python 套接字把回归线算完丢回 MT5」

想在 MT5 里跑 scikit-learn 的线性回归,又不想把模型塞进 MQL5,最顺手的办法是开一个本地 TCP 套接字服务,让 EA 把报价流推过来、算完再把线段两端坐标发回去。下面这段实现用 Python 3.7.2 内置 socket 包,监听 127.0.0.1:9090,单次 recv 缓冲设成 10000 字节,足够吞下中小周期的价序流。 类构造时 bind 地址和端口,recvmsg 里 sock.listen(1) 只接一个客户端,self.conn.recv(10000) 循环收流,decode('utf-8') 拼进 cummdata,直到收到空包才 break 并回传计算结果。注意这里回传动作写在 while True 内、return 之前,意味着每收满一帧就调一次 calcregr 并 send,实际是「收一帧算一帧」而非收完整体再算。 calcregr 把空格分隔的字符串用 np.fromstring 转 float 数组,Y reshape 成单列,X 用 np.arange 生成等长时间序号,LinearRegression().fit 后取预测数组头尾两项转字符串返回。外汇与贵金属价格序列带杠杆高风险,回归线只描述已有样本的统计倾向,不代表后续必沿该斜率运行。 别把正态当圣经:sklearn 的 LinearRegression 默认假设残差同方差,面对跳空和散点陡变时边缘坐标可能大幅摆动,建议先拿 EURUSD 的 M1 收盘价小批量验证再上实盘品种。 代码逐行拆解: import socket, numpy as np —— 引入套接字与数值计算基础包 from sklearn.linear_model import LinearRegression —— 引入线性回归模型 class socketserver: —— 定义服务类 def __init__(self, address='', port=9090): —— 构造器,默认本地 9090 端口 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) —— 建 TCP 套接字 self.sock.bind((self.address, self.port)) —— 绑定地址端口 def recvmsg(self): —— 接收处理方法 self.sock.listen(1) —— 监听,最大挂起连接 1 self.conn, self.addr = self.sock.accept() —— 接受客户端连接 while True: —— 收数据死循环 data = self.conn.recv(10000) —— 单次最多收 1 万字节 self.cummdata += data.decode('utf-8') —— 解码累加 if not data: break —— 空包则退出循环 self.conn.send(bytes(calcregr(self.cummdata), 'utf-8')) —— 算完回发 def __del__(self): self.sock.close() —— 析构关套接字 外层 serv = socketserver('127.0.0.1', 9090) 加 while True: msg = serv.recvmsg() 拉起常驻服务。

MQL5 / C++
class="kw">import socket, numpy as np
from sklearn.linear_model class="kw">import LinearRegression
class socketserver:
    def __init__(self, address = &class="macro">#x27;&class="macro">#x27;, port = class="num">9090):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.address = address
        self.port = port
        self.sock.bind((self.address, self.port))
        self.cummdata = &class="macro">#x27;&class="macro">#x27;
        
    def recvmsg(self):
        self.sock.listen(class="num">1)
        self.conn, self.addr = self.sock.accept()
        print(&class="macro">#x27;connected to&class="macro">#x27;, self.addr)
        self.cummdata = &class="macro">#x27;&class="macro">#x27;
        class="kw">while True:
            data = self.conn.recv(class="num">10000)
            self.cummdata+=data.decode("utf-class="num">8")
            if not data:
                class="kw">break    
            self.conn.send(bytes(calcregr(self.cummdata), "utf-class="num">8"))
            class="kw">return self.cummdata
            
    def __del__(self):
        self.sock.close()

def calcregr(msg = &class="macro">#x27;&class="macro">#x27;):
    chartdata = np.fromstring(msg, dtype=class="type">class="kw">float, sep= &class="macro">#x27; &class="macro">#x27;) 
    Y = np.array(chartdata).reshape(-class="num">1,class="num">1)
    X = np.array(np.arange(len(chartdata))).reshape(-class="num">1,class="num">1)
        
    lr = LinearRegression()
    lr.fit(X, Y)
    Y_pred = lr.predict(X)
    type(Y_pred)
    P = Y_pred.astype(str).item(-class="num">1) + &class="macro">#x27; &class="macro">#x27; + Y_pred.astype(str).item(class="num">0)
    print(P)
    class="kw">return str(P)

serv = socketserver(&class="macro">#x27;class="num">127.0.class="num">0.1&class="macro">#x27;, class="num">9090)
class="kw">while True:  
    msg = serv.recvmsg()

利用 MQL5 创建套接字客户端

我们创建一个简单的智能交易系统,它可以连接到服务器,传递指定数量的近期收盘价,得到反馈的回归线坐标,并将其绘制在图表上。 socksend() 函数将数据传递给服务器: 它接收字符串,转换为字节数组,并发送到服务器。 socketreceive() 函数监听端口。 一旦收到服务器响应后,该函数将其作为字符串返回: 最后一个函数 drawlr() 接收一个字符串,其中写入左、右线坐标,然后将字符串解析为字符串数组,并在图表上绘制线性回归线: 该函数在 OnTick() 处理程序中实现。 [CODE] <span class="keyword">bool</span> socksend(<span class="keyword">int</span> sock,<span class="keyword">string</span> request) { <span class="keyword"> char</span> req[]; <span class="keyword"> int</span>&nbsp;&nbsp;len=<span class="functions">StringToCharArray</span>(request,req)-<span class="number">1</span>; <span class="keyword"> if</span>(len&lt;<span class="number">0</span>) <span class="keyword">return</span>(<span class="macro">false</span>); <span class="keyword"> return</span>(SocketSend(sock,req,len)==len); } <span class="keyword">string</span> socketreceive(<span class="keyword">int</span> sock,<span class="keyword">int</span> timeout) &nbsp;&nbsp;{ &nbsp;&nbsp; <span class="keyword">char</span> rsp[]; &nbsp;&nbsp; <span class="keyword">string</span> result=<span class="string">""</span>; &nbsp;&nbsp; <span class="keyword">uint</span> len; &nbsp;&nbsp; <span class="keyword">uint</span> timeout_check=<span class="functions">GetTickCount</span>()+timeout; &nbsp;&nbsp; <span class="keyword">do</span> &nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len=SocketIsReadable(sock); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(len) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;

常见问题

先在交易软件里用内置语言建一个套接字客户端监听本地端口,再用 Python 写服务端发数据,两边用同一端口号才能连通。
只要绑定 127.0.0.1 本地回环地址、不对外开放端口,数据只在你电脑内部走,外部网络看不到,风险较低。
可以,小布能按你给的品种页监控本地数据通道状态,连不上会直接提示,你不用自己守着日志查。
多半是两端坐标缩放或时间轴没对齐,确认 Python 传的是基于相同 K 线索引的 Y 值,并在终端按对应索引赋值。
要,在客户端脚本退出前调用关闭函数释放端口,否则下次启动可能报端口被占用的错。