基于MT5与Python交互通信以实现数据交换【免费共享代码】
引子:→用于机器学习或人工智能模型交易
一般对于做机器学习量化投资的人来说,在编程层面需要相互数据的交换。比如我用Python训练一个模型,用于交易时,需要“访问”模型并给模型一定数据,让模型“回答”结果并用于交易;另外,反向也如此。也就是说,如果到交易层面的话,训练模型和交易模型之间的交互通讯是需要考虑的,其基本关系如下图所示:
如上图所示:在模块化编程的情况下,每个部分的交互需要的通讯;不光是访问数据,而是对于实时的数据交换也是一种方式。
MetaTrader5 与 Python的通讯交换简介:
MT5软件做为用C++编写的交易软件,能够起到非常快速的效率;通常来说,AI或者机器学习的模型一般情况下是用Python来进行训练的。这样来说,MT5做为主交易系统,Python做为一个模型插件能够起到交易速度和模型训练的便利性的可选项。
新版的MT5软件开放网络套接字的API接口,这样就给网络通讯提供了可能性,对应Python软件这块功能是可以起到对应的。
MT5的Stocket接口可以参照链接:https://www.mql5.com/zh/docs/network/socketcreate
如果同时都在本地运行程序的情况下,通过本地的TCP/IP实现可以起到非常快的交互速度。建立套接字只需要指定“127.0.0.1”或"localhost"本地换回地址即可,做为一种“内部循环”内联,仅在一台PC上同时实现监听和接收,一个做为Client一个做为Server即可。根据这种思路能够非常清晰的建立这种思路。因此,根据两段交互分两种:
第一种:MT5发送数据→Python接收
第二种:Python发送数据→MT5接收
这样才能实现这种交互
前期工作:
1、关于Python在MT5中的安装可以参考其他内容,这里不再赘述
2、其中有一点要非常注意,要在MT5的Option中开启运行访问的地址,如果不开启会出现“4014”的错误。位置如下:
MT5端代码分析:注意要建立一个EA代码
【我们创建一个简单的智能交易系统,它可以连接到服务器,传递指定数量的近期收盘价,得到反馈的回归线坐标,并将其绘制在图表上。 】
1、套接字开始和结束部分
sinput int lrlenght = 150; // 画趋势线的长度变量 int socket; // 套接字定义 //+------------------------------------------------------------------+ //| 初始化函数 | //+------------------------------------------------------------------+ int OnInit() { socket=SocketCreate(); // 创建套接字 return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 终止化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { SocketClose(socket); // 终止套接字 }
2、OnTick()事件中的内容
2.1、获取当前数据
1 // 数据容器 2 double clpr[]; 3 // CopyClose函数 → 从图表上获取Close数据 4 // 参数: 5 // 1、_Symbol → 当前图表的交易品种名称 6 // 2、PERIOD_CURRENT → 当前图表的周期 7 // 3、0 → 获取数据的起始位置 → 从最右侧开始 8 // 4、lrlenght → 获取数据的结束位置 → 到规定的左边为止 9 // 5、clpr → 获取双精度数据数组 → 获取的数据 10 int copyed = CopyClose(_Symbol,PERIOD_CURRENT,0,lrlenght,clpr);
2.2、发送数据准备
1 // 【发送数据准备】 2 string tosend; 3 // 遍历获取的数据,数据拼接,用" 空格 "链接,并转换为String类型 4 for(int i=0; i<ArraySize(clpr); i++) 5 tosend+=(string)clpr[i]+" ";
2.3、【重点】讲MT5数据发送给Python,同时接收Python数据
MT端口 → 【发送数据】→ *Python* → 【回传数据】 → MT端口
1 // 【发送/接收数据】 2 // MT端口 → 【发送数据】→ *Python* → 【回传数据】 → MT端口 3 string received = socksend(socket, tosend) ? socketreceive(socket, 10) : "";
3、socksend()函数将数据传递给服务器
1 //+------------------------------------------------------------------+ 2 //| 发送数据函数 | 3 //+------------------------------------------------------------------+ 4 // 【函数将数据传递给服务器】 5 bool socksend(int sock,string request) 6 { 7 // 【准备转换后数组】 8 char req[]; 9 // StringToCharArray → 复制一个从Unicode型字符串转换成ANSI,也就是无符字符型数组 10 // 参数: 11 // 1、request → 源字符串【tosend已经由获取的数组数据转一串字符串】 12 // 2、req → 转换后无符字符型数组 13 int len=StringToCharArray(request,req)-1; 14 15 // 【转换后的数组长度为0,不进行发送】 16 if(len<0) 17 return(false); 18 19 // StocketSend → 将数据写入套接字 20 // 1、sock → 套接字 21 // 2、req → 转换后无符字符型数组 → 缓冲区数组 22 // 3、len → 缓冲区大小 23 return(SocketSend(sock,req,len)==len); 24 }
4、socketreceive() 函数监听端口。 一旦收到服务器响应后,该函数将其作为字符串返回
1 //+------------------------------------------------------------------+ 2 //| 接收来自Python数据函数→ socketreceive() 函数监听端口。 | 3 //+------------------------------------------------------------------+ 4 string socketreceive(int sock, int timeout) 5 { 6 // 数据准备 7 // rsp → 获取的无符字符型数组 8 // result → 从Python获取的数据 9 // len → 套接字的字节数 10 // timeout_check → 返回一过去的毫秒数据量 + 设定的超时毫秒数 11 char rsp[]; 12 string result = ""; 13 uint len; 14 uint timeout_check=GetTickCount()+timeout; // GetTickCount()函数返回已过去的毫秒的数量 15 16 // 接收来自Python的数据 17 do 18 { // SocketIsReadable → 获取从套接字的字节数 19 len=SocketIsReadable(sock); 20 if(len) 21 { 22 // SocketRead → 从套接字读取数据 23 // 1、sock → 套接字 24 // 2、rsp → 获取的无符字符型数组 → 缓冲区数组 25 // 3、len → 缓冲区大小 26 // 4、timeout → 超时设置 → 单位:毫秒 27 int rsp_len; 28 rsp_len = SocketRead(sock,rsp,len,timeout); 29 30 // CharArrayToString → 无符字符型数组转换成字符串 31 // 1、rsp → 无符字符型数组 → 缓冲区数组 32 // 2、0 → 数组获取位置 33 // 3、rsp → 需要获取数组的长度 34 if(rsp_len>0) 35 { 36 result+=CharArrayToString(rsp,0,rsp_len); 37 } 38 } 39 } 40 41 // GetTickCount()函数返回已过去的毫秒的数量和未停止继续执行 42 while((GetTickCount()<timeout_check) && !IsStopped()); 43 return result; 44 }
5、对于获取从Python返回来的数据进行处理
→ 这里Python是计算的趋势线 → 返回数据用于在MT5画线
1 //+------------------------------------------------------------------+ 2 //| 返回数据 → 进行处理 | 3 //+------------------------------------------------------------------+ 4 // drawlr() 接收一个字符串,其中写入左、右线坐标,然后将字符串解析为字符串数组, 5 // 并在图表上绘制线性回归线: 6 void drawlr(string points) 7 { 8 // 【由于返回的是字符串,用StringSplit进行拆分】 9 // res → 拆分之后的数据 10 string res[]; 11 // 拆分前数据打印 12 Print("拆分前数据:" + points); 13 StringSplit(points, ' ', res); 14 15 if(ArraySize(res)==2) 16 { 17 // 打印两点数据 18 Print(StringToDouble(res[0])); 19 Print(StringToDouble(res[1])); 20 21 // CopyTime获取时间函数 22 datetime temp[]; 23 CopyTime(Symbol(),Period(),TimeCurrent(),lrlenght,temp); 24 // ObjectCreate → 在制定表子窗口中创建坐标 25 ObjectCreate(0,"regrline",OBJ_TREND,0,TimeCurrent(),NormalizeDouble(StringToDouble(res[0]),_Digits),temp[0],NormalizeDouble(StringToDouble(res[1]),_Digits)); 26 } 27 }
6、小结:在如下流程中可以看到 → 套接字传递/接收数据是通过字符串传递的【实际无符号字符】,需要再两段转换
→ 发送前数据:MT5包装成字符串 → 传递给Python
→ Python返回字符串 → 给MT5
→ 再将字符串转换成数组,供MT5使用:
Python端代码分析:
【首先接收来自MT5传递过来的数据,计算完毕后再传递回MT5】
1、接收函数和发送数据 :def recvmsg()
1 # 【接收MT5传递过来的数据/计算完毕再发送给MT5】 2 def recvmsg(self): 3 # .listen(1) → 建立套接字接听 4 self.sock.listen(1) 5 # .accept() → 服务器接收获得conn和addr地址 6 self.conn, self.addr = self.sock.accept() 7 #print('connected to', self.addr) 8 self.cummdata = '' 9 10 # 【接收MT5传递过来的数据】 11 while True: 12 # .recv() → 规定每次接收数据多少 13 data = self.conn.recv(10000) 14 # .decode() → 规定解码方式 15 # 【接收MT过来的数据】→ self.cummdata → 表示:接受MT5传递过来的一串<class str> 16 self.cummdata+=data.decode("utf-8") 17 if not data: 18 break 19 20 # 【计算完毕再发送给MT5】→ self.cummdata → 表示:最终由转换成<class str> 21 self.conn.send(bytes(calcregr(self.cummdata), "utf-8")) 22 return self.cummdata
2、数据计算:def calcregr()
→ 数据计算部分需要两步数据转换,此时要注意返回的数据要和MT5中定义的一样
1 def calcregr(msg = ''): # 【注意】:要和MT5中定义的一样 2 # 【转换数据】:<class str> → <class numpy.ndarray> 3 chartdata = np.fromstring(msg, dtype=float, sep= ' ') 4 5 # -------------------【数据处理部分】------------------------ 6 Y = np.array(chartdata).reshape(-1,1) 7 X = np.array(np.arange(len(chartdata))).reshape(-1,1) 8 9 lr = LinearRegression() 10 lr.fit(X, Y) 11 Y_pred = lr.predict(X) 12 # -------------------【数据处理部分】------------------------ 13 14 # 【转换数据】:<class numpy.ndarray> → <class str> 15 P = Y_pred.astype(str).item(-1) + ' ' + Y_pred.astype(str).item(0) 16 #print(P) 17 18 return str(P)
演示截图:
1、传递模式
2、数据传递
先启动Python的Server端口;再启动MT5的Client端口,如下图实现数据的相互交互
福利代码
代码:https://note.youdao.com/s/PuVomzy2
B站展示视频:
https://www.bilibili.com/video/BV1BY411z7jg?share_source=copy_web
参考:https://www.mql5.com/zh/articles/5691
本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。