市场模拟:(第 11 部分):套接字(五)
概述
在上一篇文章,“市场模拟(第 10 部分):套接字(四)”中,我们讨论了 xlwings 及其将 Python 与 Excel 集成的能力。好吧,虽然这一切看起来可能非常遥远,与我们这里在复制/建模系统中的情况无关。在您对集成 Python 和 Excel 的前景感到沮丧之前,让我们看看如何(在某种程度上)使用 xlwings 通过 Excel 控制 MetaTrader 5。我们在这里展示的内容将主要集中在教育目标上。但是,不要以为我们只能做这里涵盖的事情。我们还可以做得更多。但是,您需要了解 xlwings 如何通过 Python 控制 Excel。这是从 Excel 完全控制 MetaTrader 5 的唯一方法,即使不使用 Excel 也可以。这样,您将能够通过 Python 控制 MetaTrader 5。
让我们从基本的部分开始
首先,我们需要做一些非常简单的事情。它应该真的有效。让我们从 xlwings 文档中的一个示例开始,但以不同的方式解释它。然后你就会明白为什么我们可以在 Excel 中运行 Python。
我们将从最简单的开始。在任意编辑器中,让我们编写并保存下图所示的 Python 代码:

请注意图片中的一些细节,例如我们保存文件的位置。但最重要的是文件名,这个名字以后会非常重要。请注意,这个 Python 脚本引用了某些内容。在这种情况下,我们指的是 Excel 电子表格。但具体是哪一个页面呢?别担心,我们一步一步来。在确定是哪个工作表之前,请注意,在第 05 行,我们将向特定单元格写入文本。
但是,要使代码正常工作,必须在 Excel 会话中打开电子表格。这与第 04 行有关。正是这一行建立了联系,让我们知道应该把文本写在哪个工作表中。接下来,我们将执行以下操作:打开 Excel 并添加一个新工作表,使 Excel 界面如下图所示:

现在,请注意。目前,我们可以在 Excel 中选择一个或多个工作表。我们为什么要这样做?原因是我们要创建一个宏,当选择 Sheet1 时,该宏将被触发。这样,要调用宏,我们就不必添加额外的元素。我们只需按下 ALT + F11 打开 Excel VBA,然后输入下图所示的代码:

请注意,该代码属于 Sheet1。一旦它被激活,这段小型 VBA 脚本就会执行。现在,我们来看一下代码的第 03 行。要正确处理这一行,必须添加对 xlwings 的引用。我们在上一篇文章中解释了如何做到这一点。引用 xlwings 后,我们就可以运行 Python 脚本了。但怎么做呢?
如您所见,我们正在导入保存在本主题第一张图片中的文件。为了让 VBA 知道文件的位置,我们需要提供文件的名称以及它所在的目录。但是,由于我们的电子表格保存在同一目录中,因此不需要指定哪个目录。
现在,为了让 VBA(或者更准确地说,xlwings)知道要执行 Python 脚本的哪一部分,我们在分号后指定这一点。然后,我们返回 Excel 并切换到另一个工作表。当我们这样做时,结果将如下图所示:

很好,您刚刚看到的是 Python 如何在 Excel 中执行任务,从而取代 VBA 的许多功能。我说“很多”,是因为我们仍然需要在 VBA 中执行一些操作,才能让 Python 执行你在脚本中编写的程序。
但这与 MetaTrader 5 有什么关系?请保持冷静,我们即将得到解释。如果你不熟悉 Python,我们刚才做的事情可能看起来微不足道。但是,如果你已经在学习(甚至正在用)Python 编程,那么当你想象这简单的“Hello, world”为我们开启的各种可能性时,你的眼睛可能会闪闪发光。
好吧,如果我们能写,我们也能读。还记得我说过,虽然我们可以使用 RTD 或 DDE 服务器将数据发送到 Excel,但这会浪费时间吗?这些信息只会发送到 Excel,我们无法使用 Excel 中的计算来控制 MetaTrader 5。
由于我们可以使用 Python 直接在 Excel 中写入和读取数据,我们可以开始进一步思考。这样就可以从 Excel 控制 EA 交易,只需要在 Excel 和 MetaTrader 5 之间建立通信桥梁即可。但是如果我们不想使用 Excel,我们也可以使用 Python 来实现同样的功能。
然而,有些交易者甚至都不看图表。他们完全以价格为导向。分析基本指标的最佳方法是什么?没错,亲爱的读者:一个 Excel 表格。但不仅仅是 Excel —— 还需要其他工具配合使用。然而,在这种情况下,我们将专注于基础知识,因为这个想法是展示如何创建其他东西,我们将在另一个时间构建。
规划 Excel 和 MetaTrader 5 之间的连接
要建立 Excel 和 MetaTrader 5 之间的连接,我们需要一种高效且易于实现的方法。实现这一目标的最佳方法之一是使用套接字。这是因为套接字允许你在一台电脑上使用 Excel,在另一台电脑上使用 MetaTrader 5,也可以在同一台电脑上运行这两个程序。如何选择这些设置应由交易者控制。也就是说,如果有人想用一台电脑专门运行 MetaTrader 5,用另一台电脑专门运行 Excel,那么套接字就能实现这一点。即使交易员更喜欢在同一台机器上运行这两个程序,也可以在不更改通信协议的情况下完成。简而言之,套接字是一种非常实用的工具。
好的,但是我们该怎么做呢?这是个人选择的问题。然而,由于我们正在演示如何在 Excel 中使用 Python,而且我们已经知道 Python 脚本可以在 MetaTrader 5 中使用,这开启了许多可能性。主要的一个是创建一个 Python 服务器,我们可以在 MetaTrader 5 和 Excel 中使用。但在那之前,让我们先仔细考虑一下。Python 允许我们直接在电子表格中读取和写入数值。然而,正如我们之前解释的那样,理想情况下应该通过套接字来完成。因此,要运行客户端,我们需要 MetaTrader 5 或 Excel。至此,很明显,如果我们创建一个运行在 MetaTrader 5 上的服务器,我们就需要用 Excel 来编写客户端程序。但是,MQL5 使得创建可以直接与 EA 交易交互的客户端成为可能,而且操作非常简单。
现在,我想你明白我们需要做一些编程。那么,我们就从简单的事情开始吧 —— 不涉及任何复杂或令人困惑的元素。这就是创建回声服务器的想法的由来。它将仅用于测试 Excel 和 MetaTrader 5 之间的连接,因为这是最简单的套接字编程形式。
创建回声服务器
由于这是最简单的服务器代码,所以不要对它抱有过高的期望。这个想法是测试 Excel 如何与用 Python 编写的服务器一起工作。让我们来看看需要做什么。首先,我们将用 Python 创建服务器代码。如下所示:
01. import socket 02. import xlwings as xw 03. 04. def Echo(): 05. wb = xw.Book.caller() 06. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 07. server.bind(("127.0.0.1", 27015)) 08. wb.sheets[0]['A1'].value = "Waiting connection..." 09. server.listen(1) 10. client, addr = server.accept() 11. client.send("Wellcome to Server in Python\n\r".encode()) 12. wb.sheets[0]['A2'].value = str(f"Client connected by {addr}") 13. while True: 14. info = client.recv(512) 15. if not info: 16. break 17. client.send(b"Echo Server:" + info) 18. wb.sheets[0]['A3'].value = "Client disconnected" 19. client.close()
Python 脚本
“但仅此而已吗?”是的,就这些 —— 还有一点要补充。此代码旨在与 Excel 交互,并生成有关服务器上发生的情况的报告。如果你已经熟悉 Python,这段代码可能不会让你太兴奋。但是,如果你不知道发生了什么,让我们来看看这个 Python 脚本的实际功能。
在第 01 行和第 02 行中,我们告诉 Python 我们需要哪些模块。在第 04 行,我们开始构建该过程。这稍后将在 VBA 中使用。但首先,让我们来了解一下 Python 中的这部分。在第 05 行,我们实际上连接到了 Excel。在第 08、12 和 18 行,我们将执行状态放入 Excel 表格中,就像我们在上一个主题中所做的那样。
其余所有代码行都是回显服务器的一部分。一个重要的细节是,此服务器一次只允许一个连接。也就是说,一旦客户端连接,服务器就会锁定到该客户端,并且不会接受其他连接。当客户端关闭连接时,服务器也会关闭。这看起来可能不太实用,但请记住 —— 我们只是在测试。
另一个细节,或许更重要,在第 07 行。请注意那里指定的值。此 IPv4 格式的值表示只接受来自具有指定地址的客户端的连接。在本例中,它指的是本地主机。这意味着客户端和服务器必须位于同一台机器上。无论是物理的还是虚拟的,重要的是两者都在同一条“船”上。
您可以将此值更改为客户端预期访问的其他地址 —— 甚至可以是网站。但是,如果您不知道该地址,或者只是想允许来自任何地址的连接,只需将该值设置为 “0.0.0.0” 即可。在这种情况下,Python(或者更准确地说,套接字)会理解任何地址对于客户端连接都是有效的。请注意:一次只允许一个客户端。
好吧,但如果我们想处理更多的客户端呢?我们该怎么办?在这种情况下,我们需要处理线程中的连接 —— 就像我们在 C++ 代码中所做的那样。然而,在 Python 中,这甚至更简单。但就我们目前的目的而言,这是不必要的。对我们来说,这台服务器接受连接并发回回声就足够了。
现在我们有了 Python 中的服务器代码,我们可以做与上一主题中相同的事情。“也就是说,使用 RunPython 函数来启动服务器,对吗?”正是这样。但是,让我们对上一主题中的 VBA 代码稍作修改。VBA 代码看起来像这样:
1. Private Sub Worksheet_Activate() 2. MsgBox "Call Server..." 3. RunPython "import Server_Echo; Server_Echo.Test()" 4. End Sub
VBA 脚本
这段代码的简洁性令人印象深刻。请注意,在第 03 行,我们只更改了文件名和被调用过程的名称。关键细节在第 02 行。需要添加这行代码,以便您可以查看何时再次尝试调用服务器。我们尝试切换到另一个工作表来激活服务器。第二次尝试启动时,Python 将显示错误警告。但这并不完全算是一个错误 —— 只是代码没有检查服务器是否已经处于活动状态。如果两个客户端试图在同一会话中连接,也会发生类似的情况。我将把这些实验作为家庭作业。之后,您将了解此教育代码的局限性,并能够进行必要的调整。
我是否应该提供已经修复、功能齐全、可直接用作回声服务器的代码?在我看来,那将是无用的。如果你试着自己解决这些问题,那会好得多。尝试创建一个解决方案。这样,套接字的知识和基本概念都会更好地记住。解决这些缺点并不难 —— 我已经给出了一些解决办法。剩下的就是了解正在发生的事情并消除这个问题。
所以,这部分已经准备好了。现在,让我们来看看 MQL5 客户端。
我们使用 MQL5 实现了客户端 —— 回声版本
就像在服务器端一样,我们也可以完全用 Python 实现代码。但我们正在为以后要做的事情做准备。那么,我们暂时抛开 Python,专注于 MQL5 吧。客户端代码如下所示:
01. //+------------------------------------------------------------------+ 02. #property service 03. #property copyright "Daniel Jose" 04. #property description "Echo Server Test Service." 05. #property description "Requires that the Python server is running in Excel." 06. #property version "1.00" 07. //+------------------------------------------------------------------+ 08. input string user00 = "127.0.0.1"; //Address 09. input int user01 = 27015; //Port 10. //+------------------------------------------------------------------+ 11. void OnStart() 12. { 13. char buff[], resp[]; 14. int sock = SocketCreate(), ret; 15. uint len; 16. string szMsg; 17. 18. if (sock == INVALID_HANDLE) 19. { 20. Print("Unable to create socket. Error: ", GetLastError()); 21. return; 22. } 23. if (!SocketConnect(sock, user00, user01, 1000)) 24. { 25. Print("Connection with the address [", user00, "] in port ", user01, " failed. Error code: ", GetLastError()); 26. SocketClose(sock); 27. return; 28. } 29. while (!_StopFlag) 30. { 31. szMsg = TimeToString(TimeLocal(), TIME_DATE | TIME_SECONDS); 32. len = StringToCharArray(szMsg, buff) - 1; 33. Print("To Server: ", szMsg); 34. if (SocketSend(sock, buff, len) != len) 35. { 36. Print("Error code: " , GetLastError()); 37. break; 38. }; 39. szMsg = ""; 40. do 41. { 42. len = SocketIsReadable(sock); 43. ret = SocketRead(sock, resp, len, 1000); 44. if (ret > 0) 45. szMsg += CharArrayToString(resp, 0, ret); 46. }while ((ret <= 0) && (!_StopFlag)); 47. Print("From Server: ", szMsg); 48. Sleep(1000); 49. } 50. SocketSend(sock, buff, 0); 51. SocketClose(sock); 52. } 53. //+------------------------------------------------------------------+
MQL5 服务
在我们讨论这段代码之前,让我们先看看一个需要配置的小细节,以便一切正常工作。是的,我们需要告诉 MetaTrader 5 我们允许打开一个套接字。首先,需要指定接收方地址。如下所示操作:

完成这一步后,我们就能看到 MQL5 客户端代码是如何工作的。首先最有可能引起您注意的是第 02 行。实际上,我们可以使用服务来实现套接字。指标是唯一不能实现套接字的应用类型。否则,我们在使用其他应用程序类型时有完全的自由。
您可能会发现此代码与我们在前几篇文章中看到的类似。事实的确如此。所有面向套接字的代码都非常相似。因此,上面讨论的一切仍然有效。唯一的真正区别在于第 50 行。
现在你可能会问自己:为什么会有这一行?要理解这一点,我们需要查看 Python 服务器代码。在那里,您可以看到,在第 15 行中,当服务器收到不正确或空的信息时,它会关闭。第 50 行正是这样做的 —— 尽管说实话,这并不是服务器和客户端通信连接正在关闭的最佳方式。但对于最初的目的和基本的测试,我们现在可以接受它。因此,当客户端连接到服务器时,我们将在 MetaTrader 5 中看到以下内容:

请注意,我们正在实时回应这些信息。这很简单,但有助于理解沟通过程是如何展开的。如果你看下一张将在 Excel 中显示的图片,你就可以理解我们在说什么。

下一步之前需要解决的问题
好的。但这是最简单的基础知识。然而,在我们进入下一阶段并检查完整代码之前,让我们再次记住,这段代码的目的纯粹是教育。我想花点时间反思一下我们目前的状况。
对于那些有经验的人来说,这一切都不会令人惊讶。但我怀疑你可能还没有使用套接字的经验。说实话,我担心在你学习的初期,你可能会认为一切都很完美,只要按照步骤操作就能让套接字正常工作。
然而,现实并不那么乐观。倒也没什么可怕的 —— 套接字没什么可怕的。事实上,套接字是目前最简单的通信工具之一。无论是程序之间的通信、跨不同平台(即操作系统)的通信,还是不同计算机之间的通信(例如,将树莓派与 PC 一起使用),套接字都能简化彼此差异巨大的元素之间的交互。但是,如果不采取某些预防措施,你会遇到严重的问题,或者至少会遇到理解和解决不可避免的障碍的困难。
让我们重新审视一下我们的小型实现,它已经可以正常工作,并实现了 Excel 和 MetaTrader 5 之间的信息交换。与前面的文章不同,在前面的文章中,我们使用在命令行中运行的单个服务器,在这里,您可能会意外地尝试一些我们通常不会故意做的事情:在同一环境中运行两个服务器。也就是说,我们有一个系统控制台,其中一台服务器已经在运行;我们在同一操作系统会话中打开另一个控制台,并尝试在这个新提示符中运行同一台服务器。如果你不知道在这种情况下会发生什么,请随时尝试。你会发现这在某种程度上是不可能的。这揭示了一个问题,那就是许多人 —— 即使是经验丰富的人 —— 也没有完全理解套接字(至少在理论上)。
如果从一个提示符启动迷你聊天服务器,则在任何情况下都无法在同一操作系统会话中从另一个控制台启动同一个服务器。这是事实。
现在请记住,迷你聊天服务器使用端口 27015,而回声服务器(将通过 Excel 运行)也使用相同的端口。因此,理论上讲,这里存在冲突。人们可能会认为操作系统不允许服务器使用相同的协议在同一端口上进行干扰或尝试监听。在本例中,我们使用的是 TCP 协议。但是,我们可以在一台服务器上使用 TCP,在另一台服务器上使用 UDP。在这种情况下,使用同一个端口不会有问题。
但是当我们使用相同的端口、相同的协议和相同的主机时,事情会变得更加复杂。
我想强调这一点,这样那些希望成为套接字高级用户的人就会明白,使用套接字需要采取一定的预防措施。在您了解 Excel 和 MetaTrader 5 之间的通信是如何实际实现的之前,我希望您了解当两个不同的服务器使用相同的资源时会发生什么。在这种情况下:相同的协议、相同的端口和相同的主机。要掌握这一点,首先打开迷你聊天服务器,让它等待连接。
现在在 Excel 中打开回声服务器,并让它等待连接。也就是说,我们会得到类似这样的结果:

现在的问题是:在测试之前,我希望你们考虑一下哪个服务器会接受来自客户端的连接。换句话说,哪一个会开始倾听并回应客户端的需求?请记住,在这两个例子中,我们都使用了非常简单的代码 —— 没有验证客户端是否与服务器匹配,也没有过滤服务器应该期望的请求类型。
我不确定你是否真的理解我想解释的。关键在于,虽然这两个服务器不同,但它们使用相同的资源:相同的端口、相同的协议(TCP)和相同的主机。这正是使用套接字如此具有挑战性的原因。如果我们有一个设计用于满足特定要求的服务器,并且我们在不知不觉中使用相同的资源启动了另一个服务器,我们可能会阻止客户端收到对其请求的正确响应。
因此,至关重要的是要理解我在这里展示的代码是不完整的。它仅用于个人使用和教育目的。真实世界的服务器代码遵循更严格的使用规则,必须遵守稳健的设计原则。
在许多情况下,有些人可能会说解决方案很简单:“只需更改协议或端口 —— 就能解决服务器之间的冲突。”没错。即使像将一台服务器放在一台机器上,另一台服务器放在另一台机器上这样简单的解决方案也能解决问题,即使它们使用相同的端口和协议。
但问题依然存在:在上图中,两台计算机中哪一台会响应客户端的连接请求?要弄清楚这一点,只需知道最后启动的是哪台服务器就足够了。也就是说,如果还没有客户端连接,则第一个尝试连接的客户端将连接到最后一个启动的服务器。一旦该服务器停止,服务器将在开始处理新的客户端请求之前立即启动。
重要提示:如果客户端连接到服务器,而该服务器已关闭,则连接将丢失,客户端将不会自动重新连接到另一台服务器。事情并非如此。客户端需要一个新的连接请求。只有这样,它才能使用该协议连接到现在正在监听该端口的服务器 —— 假设端口、协议和主机与已停止的服务器相同。
总结性思考
虽然今天的文章没有演示如何在 Excel 和 MetaTrader 5 之间实现消息交换,但我还是想借此机会解释一下我认为非常重要的事情。我不想让你 —— 尤其是如果你是套接字新手 —— 认为使用套接字是世界第八大奇迹,我也不想让你在发现它们并不能神奇地解决你所有的问题时感到失望。
套接字是计算世界中最复杂、同时也是最简单的工具之一。如果你了解它们的工作原理,它们会非常有用。但是,如果你把它们当作微不足道的东西,并假设你可以随心所欲地使用它们,而不需要反思或真正的理解,它们会给你带来严重的头痛。
在本系列关于复制/建模服务的文章开头,我们考虑使用套接字构建整个复制/建模系统。从架构角度来看,这在某种程度上会简单得多 —— 我们只需要将报价数据直接发送到 MetaTrader 5 连接到真实交易服务器的端口即可。然而,经过进一步思考,我放弃了这个想法。为什么呢?因为这需要一些技术,我不想解释这些技术的实际应用。但我确实希望你对此进行反思。我们将在下一篇文章中探讨 Excel 如何向 MetaTrader 5 发送数据,以及 MetaTrader 5 如何响应来自 Excel 的请求。
| 文件 | 描述 |
|---|---|
| Experts\Expert Advisor.mq5 | 演示 Chart Trade 与 EA 交易之间的交互(需要 Mouse Study)。 |
| Indicators\Chart Trade.mq5 | 创建一个窗口,用于配置要发送的订单(需要 Mouse Study)。 |
| Indicators\Market Replay.mq5 | 创建与复制/建模服务交互的控件(需要 Mouse Study)。 |
| Indicators\Mouse Study.mq5 | 实现图形控件与用户之间的交互(回放和真实市场交易都需要)。 |
| Servicios\Market Replay.mq5 | 创建并维护市场复制/建模服务(整个系统的核心文件)。 |
| Code VS C++\Server.cpp | 创建并维护一个用 C++ 开发的套接字服务器(迷你聊天版本)。 |
| Python Server.py | 创建并维护用于 MetaTrader 5 和 Excel 之间通信的 Python 套接字。 |
| ScriptsCheckSocket.mq5 | 允许测试与外部套接字的连接。 |
| Indicators\Mini Chat.mq5 | 通过指标实现迷你聊天(需要运行服务器)。 |
| Experts\Mini Chat.mq5 | 在 EA 交易中实现迷你聊天功能(需要运行服务器)。 |
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/12744
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
数据科学和机器学习(第 38 部分):外汇市场中的 AI 迁移学习
新手在交易中的10个基本错误