
MetaTrader 5 与 Python 的集成:接收和发送数据
为什么要把 MQL5 与 Python 集成?
全方位的数据处理需要大量工具,并且经常超出单一应用程序的功能沙箱。 专用编程语言正在用于处理和分析数据,统计和机器学习。 Python 是数据处理的主要编程语言之一。 一个非常有效的解决方案是利用语言的力量并包含函数库来开发交易系统。
在两个或更多个程序之间实现交互存在众多不同的解决方案。 套接字是最快速、最灵活的解决方案之一。
网络套接字是计算机网络上进程间通信的端点。 MQL5 标准库包含一组 Socket 函数,这些函数为在互联网上操作提供了一个低层接口。 这是不同编程语言的通用接口,因为它在操作系统级别进行系统调用。
价格之间的数据交换是通过 TCP/IP(传输控制协议/互联网协议)实现的。 因此,进程可以在单个计算机内,以及通过局域网或互联网进行交互。
若要建立连接,必须创建并初始化 TCP 服务器,以便客户端进程连接。 一旦交互进程完成,则连接必须强制关闭。 TCP 交换中的数据是字节流。
创建服务器时,我们需要将套接字与一个或多个主机(IP 地址)的未使用端口相关联。 如果未设置主机列表,或将其指定为 “0.0.0.0”,则套接字将监听所有主机。 如果指定 “127.0.0.1” 或 “localhost”,则只能在 “内部循环” 内连接,即仅在一台计算机内监听。
由于在 MQL5 中只提供客户端,我们利用 Python 创建一个服务器。
利用 Python 创建套接字服务器
本文的目的并非教授 Python 编程的基础知识。 因此,假定读者熟悉这种语言。
我们将使用 3.7.2 版本的内置 socket 软件包。 更详细信息,请阅读相关文档。
我们将编写一个简单的程序,它创建一个套接字服务器,并从客户端(MQL5 程序)接收必要的信息,处理它并发回结果。 这似乎是最有效的交互方法。 假设我们需要使用机器学习函数库,例如 scikit learn,它将计算价格的线性回归并返回坐标,根据这些坐标可以在 MetaTrader 5 终端中绘制一条线。 这是基本的示例。 然而,这种交互也可以用于训练神经网络,用于从终端发送数据(报价),学习并将结果返回给终端。
我们来创建 socketserver.py 程序并导入上述函数库:
import socket, numpy as np
from sklearn.linear_model import LinearRegression
现在我们可以继续创建一个负责处理套接字操作的类:
class socketserver: def __init__(self, address = '', port = 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 = '' def recvmsg(self): self.sock.listen(1) self.conn, self.addr = self.sock.accept() print('connected to', self.addr) self.cummdata = '' while True: data = self.conn.recv(10000) self.cummdata+=data.decode("utf-8") if not data: break self.conn.send(bytes(calcregr(self.cummdata), "utf-8")) return self.cummdata def __del__(self): self.sock.close()
创建类的对象时,构造函数将获取主机名(IP 地址)和端口编号。 然后创建 sock 对象,该对象与地址和端口 sock.bind() 相关联。
recvmsg 方法监听传入连接 sock.listen(1)。 当传入的客户端连接到达时,服务器会接受它 self.sock.accept()。
然后,服务器在无限循环中等待传入的客户端消息,该消息抵达时是为数位流。 由于事先不知道消息长度,服务器会分帧接收该消息,比如一次 1K 字节,直到读取整个消息 self.conn.recv(10000)。 接收到的数据片段将会被转换为字符串 data.decode(“utf-8”),并添加到字符串 summdata 的其余部分。
一旦收到所有数据(如果不是数据 :),服务器就会向客户端发送一个字符串,其中包含计算出的回归线的最右边和最左边的坐标。 该字符串初步转换为字节数组 conn.send(bytes(calcregr(self.cummdata),"utf-8"))。
在末尾,该方法返回从客户端收到的字符串。 它可以用于所接收报价的可视化,亦或其它。
一旦 Python 程序执行完毕,析构函数就会关闭套接字。
请注意,这并非该类唯一可能的实现。 替代方案,您可以分离接收和发送消息的方法,并在不同的时间点以不同的方式使用它。 我只是描述创建连接的基本技术。 您可以实现自己的方案。
我们来更详尽地考察当前实现中的线性回归学习方法:
def calcregr(msg = ''): chartdata = np.fromstring(msg, dtype=float, sep= ' ') Y = np.array(chartdata).reshape(-1,1) X = np.array(np.arange(len(chartdata))).reshape(-1,1) lr = LinearRegression() lr.fit(X, Y) Y_pred = lr.predict(X) type(Y_pred) P = Y_pred.astype(str).item(-1) + ' ' + Y_pred.astype(str).item(0) print(P) return str(P)
接收到的字节流转换为 utf-8 字符串,然后由 calcregr(msg ='') 方法接受。 由于字符串包含若干由空格分隔的价格序列(在客户端中实现),因此它将被转换至 float 类型的 NumPy 数组。 之后价格数组会被转换为一列(数据接收格式为 sclearn)Y = np.array(chartdata).reshape(-1,1)。模型的预测变量是线性时间(一个数值序列; 其大小等于训练样本的长度)X = np.array(np).arange(len(chartdata))).reshape(-1,1)
接下来是训练和模型预测,而回归线的第一个和最后一个值(线段的边缘)被写入 “P” 变量,转换为字符串,并以字节形式传递给客户端。
现在,我们只需要创建类对象,并在循环中调用 recvmsg() 方法:
serv = socketserver('127.0.0.1', 9090) while True: msg = serv.recvmsg()
利用 MQL5 创建套接字客户端
我们创建一个简单的智能交易系统,它可以连接到服务器,传递指定数量的近期收盘价,得到反馈的回归线坐标,并将其绘制在图表上。
socksend() 函数将数据传递给服务器:
bool socksend(int sock,string request) { char req[]; int len=StringToCharArray(request,req)-1; if(len<0) return(false); return(SocketSend(sock,req,len)==len); }
它接收字符串,转换为字节数组,并发送到服务器。
socketreceive() 函数监听端口。 一旦收到服务器响应后,该函数将其作为字符串返回:
string socketreceive(int sock,int timeout) { char rsp[]; string result=""; uint len; uint timeout_check=GetTickCount()+timeout; do { len=SocketIsReadable(sock); if(len) { int rsp_len; rsp_len=SocketRead(sock,rsp,len,timeout); if(rsp_len>0) { result+=CharArrayToString(rsp,0,rsp_len); } } } while((GetTickCount()<timeout_check) && !IsStopped()); return result; }
最后一个函数 drawlr() 接收一个字符串,其中写入左、右线坐标,然后将字符串解析为字符串数组,并在图表上绘制线性回归线:
void drawlr(string points) { string res[]; StringSplit(points,' ',res); if(ArraySize(res)==2) { Print(StringToDouble(res[0])); Print(StringToDouble(res[1])); datetime temp[]; CopyTime(Symbol(),Period(),TimeCurrent(),lrlenght,temp); ObjectCreate(0,"regrline",OBJ_TREND,0,TimeCurrent(),NormalizeDouble(StringToDouble(res[0]),_Digits),temp[0],NormalizeDouble(StringToDouble(res[1]),_Digits)); }
该函数在 OnTick() 处理程序中实现。
void OnTick() { socket=SocketCreate(); if(socket!=INVALID_HANDLE) { if(SocketConnect(socket,"localhost",9090,1000)) { Print("Connected to "," localhost",":",9090); double clpr[]; int copyed = CopyClose(_Symbol,PERIOD_CURRENT,0,lrlenght,clpr); string tosend; for(int i=0;i<ArraySize(clpr);i++) tosend+=(string)clpr[i]+" "; string received = socksend(socket, tosend) ? socketreceive(socket, 10) : ""; drawlr(recieved); } else Print("Connection ","localhost",":",9090," error ",GetLastError()); SocketClose(socket); } else Print("Socket creation error ",GetLastError()); }
测试 MQL5-Python 客户端 - 服务器应用程序
为了运行该应用程序,您需要安装 Python 解释器。 您可以从 官方网站 下载。
然后运行服务器应用程序 socketserver.py。 它创建一个套接字,并监听来自 MQL5 程序 socketclientEA.mq5 的新连接。
连接成功后,连接过程和回归线价格将显示在程序窗口中。 价格会反馈给客户端:
连接活动和回归线价格也会在 MetaTrader 5 终端中显示。 回归线也会在图表上绘制,并在每次新的即时报价来临时进一步更新:
我们已考研究通过套接字连接实现两个程序的直接交互。 与此同时,MetaQuotes 已开发了一个 Python 软件包,允许直接从终端接收数据。 更多有关详细信息,请参阅与 在 MetaTrader 中使用 Python 相关的论坛讨论(俄语版,请酌情使用自动翻译选项)。</ s0>
我们创建一个脚本来演示如何从终端接收报价。
使用 MetaTrader 5 Python API 获取并分析报价
首先,您需要安装 MetaTrader5 Python 模块(Python 讨论摘要在此)。
pip install MetaTrader5
将其导入程序,并初始化与终端的连接:
from MetaTrader5 import * from datetime import date import pandas as pd import matplotlib.pyplot as plt # 初始化 MT5 连接 MT5Initialize() MT5WaitForTerminal() print(MT5TerminalInfo()) print(MT5Version())
之后创建所需品种列表,并从终端连续请求每个货币对的收盘价发至 pandas 数据帧:
# 为需要绘制相关矩阵的货币创建清单 sym = ['EURUSD','GBPUSD','USDJPY','USDCHF','AUDUSD','GBPJPY'] # 将数据复制到数据帧 d = pd.DataFrame() for i in sym: rates = MT5CopyRatesFromPos(i, MT5_TIMEFRAME_M1, 0, 1000) d[i] = [y.close for y in rates]
现在我们可以断开与终端的连接,然后计算相关矩阵,并在屏幕上显示货币对价格变化的百分比:
# 逆初始化 MT5 连接 MT5Shutdown() # 比较百分比变化 rets = d.pct_change() # 计算相关性 corr = rets.corr() # 绘制相关性矩阵 plt.figure(figsize=(10, 10)) plt.imshow(corr, cmap='RdYlGn', interpolation='none', aspect='auto') plt.colorbar() plt.xticks(range(len(corr)), corr.columns, rotation='vertical') plt.yticks(range(len(corr)), corr.columns); plt.suptitle('FOREX Correlations Heat Map', fontsize=15, fontweight='bold') plt.show()
我们可以从上面的热图中看到 GBPUSD 与 GBPJPY 之间的良好相关性。 然后我们可以通过导入 statsmodels 函数库来测试协整:
# 导入 statsmodels 进行协整检验 import statsmodels from statsmodels.tsa.stattools import coint x = d['GBPUSD'] y = d['GBPJPY'] x = (x-min(x))/(max(x)-min(x)) y = (y-min(y))/(max(y)-min(y)) score = coint(x, y) print('t-statistic: ', score[0], ' p-value: ', score[1])
两个货币对之间的关系可以显示为 Z 分值:
# 绘制 z-分值变换 diff_series = (x - y) zscore = (diff_series - diff_series.mean()) / diff_series.std() plt.plot(zscore) plt.axhline(2.0, color='red', linestyle='--') plt.axhline(-2.0, color='green', linestyle='--') plt.show()
利用 Plotly 函数库可视化市场数据
通常需要按照便利的形式可视化报价。 这可以利用 Plotly 函数库来实现,该函数库还允许以交互式 .html 格式保存图表。
我们下载 EURUSD 报价,并将其显示在烛条图表中:
# -*- coding: utf-8 -*- """ 创建于 Thu Mar 14 16:13:03 2019 @author: dmitrievsky """ from MetaTrader5 import * from datetime import datetime import pandas as pd # 初始化 MT5 连接 MT5Initialize() MT5WaitForTerminal() print(MT5TerminalInfo()) print(MT5Version()) # 将数据复制到 pandas 数据帧 stockdata = pd.DataFrame() rates = MT5CopyRatesFromPos("EURUSD", MT5_TIMEFRAME_M1, 0, 5000) # 逆初始化 MT5 连接 MT5Shutdown() stockdata['Open'] = [y.open for y in rates] stockdata['Close'] = [y.close for y in rates] stockdata['High'] = [y.high for y in rates] stockdata['Low'] = [y.low for y in rates] stockdata['Date'] = [y.time for y in rates] import plotly.graph_objs as go from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot trace = go.Ohlc(x=stockdata['Date'], open=stockdata['Open'], high=stockdata['High'], low=stockdata['Low'], close=stockdata['Close']) data = [trace] plot(data)
也可以下载并显示出价和要价历史的任意深度:
# -*- coding: utf-8 -*- """ 创建于 Thu Mar 14 16:13:03 2019 @author: dmitrievsky """ from MetaTrader5 import * from datetime import datetime # 初始化 MT5 连接 MT5Initialize() MT5WaitForTerminal() print(MT5TerminalInfo()) print(MT5Version()) # 将数据复制到列表 rates = MT5CopyTicksFrom("EURUSD", datetime(2019,3,14,13), 1000, MT5_COPY_TICKS_ALL) bid = [x.bid for x in rates] ask = [x.ask for x in rates] time = [x.time for x in rates] # 逆初始化 MT5 连接 MT5Shutdown() import plotly.graph_objs as go from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot data = [go.Scatter(x=time, y=bid), go.Scatter(x=time, y=ask)] plot(data)
结束语
在本文中,我们研究了终端与利用 Python 编写的程序之间实现通信的选项,通过套接字并直接使用 MetaQuotes 的专用库。 不幸的是,MetaTrader 5 中当前实现的客户端套接字不适合在策略测试程序中运行,因此没有执行完整的测试,以及测量解决方案性能。 我们等待套接字功能的进一步更新。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/5691
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.



只是想知道为什么我们需要在 OnTick() 中不断调用 socket=SocketCreate() 和 SocketClose()?
它不能保持套接字打开/激活吗?或者说,是否有可能保持套接字打开/激活?
你好!我也在安装一个插座。我有一个问题--我用 python 编写了一个指标。它可以从 mt5 获取大量数据,而且要多少有多少,但要将数据返回给 mt5 就不那么容易了。在套接字上可以只传输一个字符串,最多可以传输 100 个小字符串,但我需要更多。除了套接字,还有什么其他解决方案? 除了在 Python->MT5 之间进行数据交换的网络/互联网请求,还有什么其他解决方案? 我不想为此使用 MySQL 数据库。我们讨论的是将大约 40 种货币对从 Python 传输到 MT5,其中包含 1000 个重新计算的历史读数,并在 MT5 中进一步构建指标线......我想摆脱 MT5 中的计算,因为 Python 的计算速度更快。因此,我希望看到指标的所有行,而不仅仅是每秒一次通过字符串传输的最后一个条形图。
使用 Python 中的 multiprocessing.shared_memory 和 MQL5 中的 WinAPI 可以直接从内存中读取数据。有人能给我一些有用的建议吗?
TCP/IP Socket - MT5 将接收一个字符串,但无论如何都无法容纳 30 000 个数据....。即使是 jason 格式,一个字符串变量也最多只能容纳 100 个字符串。这样一个 Socket 有什么用呢?
原来,对于大数据来说,Python-->MySQL-->MT5 是一条出路
我也想到了这个主意:
除了在 Python->MT5 之间进行数据交换的网络/互联网请求者之外?我不想调用 MySQL DB。
"调用 SQLite"--MT5 已内置SQLite,Python 显然也有SQLite....。
您可以使用套接字及时报告所需的信息(信号、警报等),并将大数据存入 SQLite。也就是说,在 python 中,您可以将所需的一切输入数据库,然后在 "数据更新 "套接字上吹哨。而在 MQL 中,您可以根据哨声重新读取数据库。
数据库本身可以存储在框架光盘上
"调用 SQLite"--MT5 已内置SQLite,Python 显然也有。
您可以使用套接字及时报告所需的信息(信号、警报等),并将大数据存入 SQLite。也就是说,在 python 中,您可以将所需的一切输入数据库,然后在 "数据更新 "套接字上吹哨。而在 MQL 中,您可以通过哨声重读数据库
数据库本身可以存储在框架光盘上
我明白您的意思了,谢谢您的回答...我想也是
MetaTrader 5 使用自己的编程语言MQL5,它在一个独立进程中运行。这就不允许与操作系统的共享内存直接交互,而其他使用低级 API 的应用程序则可以做到这一点。
壳中壳....20 年来,他们一直无法为 Mac 制作原生版本......
我明白您的意思,谢谢您的回复....我也这么认为
MetaTrader 5 使用自己的编程语言MQL5,它在一个独立进程中运行。这就不允许与操作系统的共享内存直接交互,而其他使用底层 API 的应用程序则可以做到这一点。
壳中壳....20 年来,他们一直无法为 Mac 制作原生版本......
没有人禁止使用 DLL,在那里你可以随心所欲。
你可以直接搜索内存(可能不会更快 :-) )。
或者高效的中间解决方案,比如内存键值数据库。
只是在大多数情况下,刹车和瓶颈都在 Python 一方