English Русский Español Deutsch 日本語 Português
preview
如何将 MetaTrader 5 与 PostgreSQL 连接

如何将 MetaTrader 5 与 PostgreSQL 连接

MetaTrader 5积分 | 20 九月 2023, 09:58
733 0
Jocimar Lopes
Jocimar Lopes

概述

软件开发的格局在过去十年中发生了极大变化。 我们看到云计算的普及,和大量首字母缩略词,如 IASS,PASS 和 SAAS 等等,现在是任何软件项目中必须考虑的关键工具。 对于最终用户和软件开发人员双方来说,事情变得更容易了。

MetaQuotes 团队意识到了这些变化,自 2014 年以来,我们为 MetaTrader 5 提供了一个原生的 WebRequest

变化最大的领域之一是数据库管理。 从实际角度来看,过去很复杂甚至“奇怪”的解决方案不仅可行,而且成为许多用例的首选解决方案。 通过 REST API 访问数据库就是这种情况。

几年前,通过 REST API 访问数据库的建议看起来像是过度设计。 今天,快速搜索“通过 rest API 访问托管数据库”会返回十几个提供商,范围从每月几美元的基本计划,到定制的企业级解决方案。 其中许多提供商提供了慷慨的免费层次,涵盖原型设计、测试、甚至部署小型生产工序。

本文分析了将 Postgres 数据库连接到 MetaTrader 5 的五种标准备选方案,它们的需求,优点和缺点。 此外,我们还将设置一个开发环境,将 Postgres 数据库安装为远程数据库,连接它,并插入和检索 MQL5 脚本或 EA 所需的数据。

这种开发环境设置和相应的过程,可以很容易地与任何 RDBMS 一起复现,因为 REST API 是数据库系统和客户端代码之间的抽象层。


MetaTrader 5 和数据库

MetaTrader 5 已经拥有操控数据库所需的功能,以及通过网络连接到数据库时可能需要的功能。

自 2020 年以来,该平台提供与 SQLite 的原生集成。 您可从代码里调用上面提到的那些数据库函数与之交互。 除此之外,您还可以通过 MetaEditor 中的专用 GUI 与数据库进行交互,从而可以轻松创建、更改数据表、及执行 CRUD 操作,而无需附加软件。

这是最终用户体验的极大改进,也是 MQL5 开发人员武器库的重要补充。

在数十种可用的 RDBMS 当中,其中许多带有开源许可证,SQLite 似乎是 MetaTrader 5 开发人员的明智选择。 尽管它是一个功能齐全的 SQL 数据库,拥有多列索引、触发器、视图、ACID(原子性:Atomicity,或称不可分割性;一致性:Consistency;隔离性:Isolation,又称独立性;持久性:Durability)事务、全文搜索、聚合函数,等等功能,但它是轻量级的、基于文件的、可扩展的,且维护成本为零。 据其网站声称,“似乎有超过一万亿(1-e12)个活跃 SQLite 数据库正在使用当中”。

尽管 SQLite 拥有令人印象深刻的功能,但它的设计仅限于单用户,并且不针对 Web 部署中的并发访问。 MQL5 网站上关于如何将 MetaTrader 5 连接到 MySQL 的大量论坛帖子和文章表明,对于其它用例,需要更强大的解决方案。

本文重点介绍如何使用 Postgres 为这些用例设置开发环境。

为什么选 Postgres

首先,我之所以选择 Postgres,是因为另一个流行的开源替代方案 MySQL 已经在这里有了广泛介绍。

其次,Postgres 是一个成熟的开源项目、多平台、维护优良、文档一致。 它很受欢迎,您可以在网络上找到大量示例代码、指南和教程。 同样,有很多云服务提供商可供满足所有需求和预算。

Postgres 是企业级的,同时亦可由单用户轻松设法在家用机器上单独工作。

当然,我选择 Postgres 数是因为我信任它。 十多年来,在不同的项目中,我都是 Postgres 的快乐用户。

最后但不止于此,我在此分享目前正为我的个人交易环境实现的解决方案。 所以它好似一种“游戏皮肤”。 我在吃我的狗粮。


从 MQL5 与 Postgres 交互的四种方式

距今就我所知,从 MQL5 调用 Postgres 有四种主要方式:

  1. 专用的 MQL5 库驱动程序
  2. 来自 C++ 客户端接口的 .dll
  3. 通过 .NET Npgsql 驱动程序
  4. 一套 REST API

我们来看看每一种的需求、优缺点。 我很确定,来自 MQL5 社区的大量经验丰富的开发人员,将为缺点提供简单的解决方案,并从专业人士角度指出我看不到的缺点。 来自 MQL5 社区的反馈是意料之中的,因为经验不足的开发人员和非开发人员交易者都能从与此相关的讨论中受益。


1. 专用的 MQL5 库驱动程序

该函数库尚不存在。 它需要开发,且需要来自资深 MQL5 开发人员花费大量的辛勤工作时间。 这不会是廉价的。 我们也需要考虑维护成本。 Postgres 是成熟的,但它在任何意义上都不是静默的。 这是一个定期发布新版本的活跃项目,其中一些版本(如果不是很多)将需要更新客户端代码。

例如:现在,在撰写本文时,最后一个 Postgres 版本(15)要求数据库的正规用户必须以“公开模式”授予一些权限。 该要求在以前的版本中并不存在。 大概,有若干个外挂代码库需要维护。

委托为 Postgres 开发专用 MQL5 驱动程序的优势在于,如果共享,它可能对许多 MQL5 用户有用。 而缺点也非常明显:时间/金钱成本。

如果您选择这种方式,从哪里开始:

在此站点上搜索 MySQL 文章将返回一些有用的参考资料。

开源 C++ 客户端库 libpqxx

Postgres 的官方 C 客户端库 libpq


2. 来自 C++ 客户端接口的 .dll

这是一套外部维护的官方 C++ 库,libpqxx,它建立在内部维护的官方 C 库 libpq 之上,随 Postgres 发行版一起提供。

就个人而言,我从未用过它,我只能说它已存在了很长时间,并且似乎维护得很好。 这种方法的缺点是 MQL5 应用商店不允许 DLL。 如果这对您的项目来说不是问题,并且您在家中也经常用到来自 MetaTrader 的 .dll,那么这可能是您的解决方案。

如果您选择这种方式,从哪里开始:

开源 C++ 客户端库 libpqxx


3. 通过 .NET Npgsql 驱动程序

自 2018 年以来,MetaTrader 5 增加了对 .NET 库的原生支持,并带有“智能”函数导入功能。 随着平台构建 1930 的发布,.NET 库无需编写特殊包装器即可使用 — MetaEditor 自行完成。 

使用 .NET Npgsql 驱动程序所需要做的就是导入 .dll 本身。 您可以在官方发行说明(https://www.mql5.com/zh/forum/285632)中查看一些限制。

如果您选择这种方式,从哪里开始:

用于 .NET 的开源 Postgres 驱动程序


4. 一套 REST API

如果您选择“无需.dll”的路径,这应该是更快、更便宜的方法。 该 API 可以用任何语言编写,您可以在一天、甚至几小时内拥有一个工作原型。

除此之外,一些云提供商免费为 Postgres 提供内置的 REST API。 您所需要的只是为您的 MQL5 代码提供一个良好的开发环境。

通过使用这种方法,您的 MQL5 代码可以将您的 Postgres 响应作为 JSON。

如果您选择这种方式,从哪里开始:

这里! 只需继续阅读,按照以下步骤操作,下载示例代码,然后开始在 Postgres 数据库中存储和查询您的成交和交易。


设置开发环境

无论您选择哪种方法,您都需要在正运行 Postgres 服务器的 Windows 机器中配置开发环境。 俗话说,道路不止一条。 我记得有三条道路,从最复杂、最耗时、到最简单的:

  1. 从源代码编译
  2. docker 容器
  3. 第三方 msi 安装器

所有这些都是在 Windows 上拥有 Postgres 的好方法,但相信我,在 Windows 上从源代码编译应该是您的最后选择,除非您愿意学习软件开发里的间歇性快速恢复能力的理论并实践。

docker 容器是一个非常好的选择,它是一个健壮而灵活的安装,其中您的数据库服务器将在“远程”机器中运行,而不是“本地主机”(见下文)。 毕竟,这很容易。 您只需要安装 Docker,以及两到三个命令行,就可以出发了。

除了“第三方”软件的相对不便之外,第三方 msi 安装器是一个很好的选择,可以避免编译源代码、或来自 docker 安装和容器管理时所冒的风险。

但是,如果有可能针对位于远程机器上的服务器进行开发,则我不建议将数据库服务器或任何类型的服务器的开发环境作为“本地主机”。 这是因为最好在远程环境中而不是在“本地主机”上进行开发、测试和调试服务器,以便尽快解决连接设置和身份验证问题。

进入 WSL。

WSL 是什么

WSL 代表 Windows Subsystem For Linux。

自 2016 年以来,您就可以在 Windows 机器上运行 Linux 发行版作为子系统,您或许尚未接触它。 不用担心! 这里没有隐秘后门。 WSL 由微软公司开发,内置于 Windows 当中。 您只需要启用它,我们将在下面看到。

为什么选 WSL

为什么不简单地在另一台 Windows 机器上安装 Postgres,甚至最终安装虚拟机呢?

因为 Postgres 是一个 Unix 原生系统,在 *nix 系统中创建和开发。 在 Linux 上,您可轻松地安装、更新、以及维护它。 所有官方文档都是针对 Unix 系统。 您可以在 Web 上找到的大多数示例代码、片段和一般帮助都反映出这一事实。

因此,您将可以轻松地在 Linux 系统中进行开发。 WSL 正是为了这个目的而由微软开发的。

安装 WSL

来自微软文档中的先决需求:

“您必须运行 Windows 10 版本 2004 及更高版本(内部版本 19041 及更高版本),或 Windows 11 才能使用以下命令。 如果您使用的是早期版本,请参阅手动安装页面。”

如果系统满足此先决需求,只需以管理员身份打开 Power Shell,然后输入以下命令即可安装/启用 WSL:

wsl –install

Power Shell 里输入 wsl 安装命令

此命令将在 WSL 上安装 Ubuntu,因为它是默认发行版。 

重启 Windows。

这应该是一个直截了当的过程。 如果不对,您应在上面链接的 MS 官方文档中找到一个包含最常见安装问题的部分。

重新启动后,您应该看到类似这样的内容。 继续,创建一个新的 UNIX 用户名和密码。

首次重启后 wsl 安装


现在,您已经安装并运行了 WSL/Ubuntu,我们在上面安装 Postgres。

在 WSL 上,安装 Postgres

输入以下命令。

sudo apt install postgresql postgresql-contrib

此命令将安装 Ubuntu 存储库中可用的 PostgreSQL 软件包的最后一个稳定版本。 它包括服务器,pgsql 客户端,对应的二进制文件,和一些实用程序。 这些就是您开始所需的所有内容。

如果您想安装最新的稳定版 Postgres — 通常与 Ubuntu 仓储库中的最后一个稳定版本不同 — 您可以在软件包管理器的源代码清单中包含 Postgres 的官方仓储库。 您可以在 Postgres 官方文档中找到详细说明。

如果一切正常,输入命令

psql --version

应当返回所安装 Postgres 数据库的版本。


启动服务器

键入此命令来启动服务器。

sudo service postgresql start

默认情况下,新的 Postgres 安装仅接受来自“本地主机”的连接。 我们需要改变一下。

找到 Postgres 配置文件。

sudo -u postgres psql -c "SHOW config_file"

postgres 安装显示配置文件


编辑配置文件,以便接受本地主机之外的连接。更改 listen_addresses 行。

postgres 配置 listen_addresses


找到 pg_hba 配置文件。

sudo -u postgres psql -c "SHOW hba_file"

postgres 配置显示 hba 文件


编辑 pg_hba.conf 文件,从而允许在 IPv4 和 IPv6 上通过密码进行身份验证。

postgres 配置 pg_hba 通过密码认证


现在,以安装时创建的默认 Postgres 用户身份访问 psql 实用程序。 其名为 'postgres'。

sudo -u postgres psql


创建拥有 CREATEDB 权限的数据库正式用户。 到目前为止,安装时只创建了用户 “postgres”。

为 mt5_user 授权架构的所有公开权限。 如果您的 Postgres 版本低于 15,则不需要这样做。

CREATE USER mt5_user PASSWORD '123' CREATEDB;

GRANT ALL ON SCHEMA public TO mt5_user;

postgres 为所创建用户授权所有权限


创建一个数据库 my_remote_db,并授予 mt5_user 对其的所有权限。

GRANT ALL PRIVILEGES ON DATABASE my_remote_db TO mt5_user;

psql 创建数据库并授权所有权限


连接到 Postgres

到目前为止,您应当已在远程机器上运行数据库服务器,其 IP 与 Windows 本地主机不同,并准备好接受经由网络的连接。 我们可以通过套接字或 HTTP 连接。 由于我们将与 REST API 交互,因此在本例中我们将使用后者。

我们看看是否可以在 WSL 主机上按照用户名:mt5_user,密码:123 ,连接到 my_remote_db。

输入此命令以获取 WSL 主机名(IP)。

hostname -I

ubuntu 终端命令主机名


检查 Postgres 服务器状态。 如果处于停机,则启动它。 您可以使用此命令启动、重启、或停止服务器。

sudo service postgresql {status, start, stop}


在 MetaTrader 5 终端上,转到 “工具 > 选项 > 智能系统” 选项卡,并将 WSL 主机 IP 包含在允许的 IP 列表之中。

MT5 终端工具选项菜单

MetaTrader 5 终端分别只接受端口 80 和 443 上的 HTTP 和 HTTPS 连接。 仅限端口 80 和 443。如果您要开发自己的 API,则应考虑此安全特点。 通常,在移动到生产中的真实服务器之前,开发服务器也许会侦听非特权端口,如 3000 或 5000。 因此,为了能够将请求发送到您在上述终端设置中声明的 IP,您需要把开发时的服务器自定义端口流量重定向到 HTTP 请求的端口 80 和/或 HTTPS 请求的 443。

为保持简单起见,您将在附加的 Python 应用的自述文件中找到有关如何在 WSL 上执行此重定向的说明。


启动演示应用

鉴于本文是关于 MQL5 的,因此我不会讨论 API 实现的细节。 取而代之,我做了一个演示应用程序,您可以作为为 Python 软件包下载并安装,以便测试您的 MQL5 代码与 API 的交互。

若要启动演示应用,您只需在 WSL 上安装 Python。 它应该已在那里准备就绪了。

强烈建议您创建一个 Python 虚拟环境('venv')来安装该应用程序。 这将确保您系统的 Python 安装不会混乱。 体验过该应用程序之后,您只需删除虚拟环境即可。

您可以使用此命令创建虚拟环境。

python3 -m venv 'venv'


故此,在您安装演示应用程序之后,为了启动您的 MQL5 开发代码,您将:

  1. 启动 WSL
  2. 启动 Postgres 服务器
  3. 启动演示应用
  4. 从演示应用的输出中获取主机名 IP
  5. 将主机名 IP 添加到终端中允许的地址

WSL 和 Postgres 服务器都可以配置为在 Windows 启动时启动。


从 MQL5 插入数据

我们来尝试插入一些数据。 首先,我们的帐户信息。 在您的 MT5 终端上,创建一个新脚本,并添加以下代码。

//+------------------------------------------------------------------+
//|                                                post_acc_info.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- gathering the data - Account Info
   CJAVal data;
   CJAVal acc_info;
//--- doubles
   data["balance"] =          AccountInfoDouble(ACCOUNT_BALANCE);
   data["credit"] =           AccountInfoDouble(ACCOUNT_CREDIT);
   data["profit"] =           AccountInfoDouble(ACCOUNT_PROFIT);
   data["equity"] =           AccountInfoDouble(ACCOUNT_EQUITY);
   data["margin"] =           AccountInfoDouble(ACCOUNT_MARGIN);
   data["margin_free"] =      AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   data["margin_level"] =     AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   data["margin_so_call"] =   AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   data["margin_so_so"] =     AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
//--- integers
   data["login"] =            AccountInfoInteger(ACCOUNT_LOGIN);
   data["leverage"] =         AccountInfoInteger(ACCOUNT_LEVERAGE);
   data["trade_allowed"] =    AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
   data["ea_allowed"] =       AccountInfoInteger(ACCOUNT_TRADE_EXPERT);
   data["trade_mode"] =       AccountInfoInteger(ACCOUNT_TRADE_MODE);
   data["margin_so_mode"] =   AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
//-- strings
   data["company"] =          AccountInfoString(ACCOUNT_COMPANY);
   data["currency"] =         AccountInfoString(ACCOUNT_CURRENCY);
   data["name"] =             AccountInfoString(ACCOUNT_NAME);
   data["server"] =           AccountInfoString(ACCOUNT_SERVER);
   
//--- fill in the acc_info array with Account Info data
   acc_info["acc_info"].Add(data);
   
//--- WebRequest arguments
   string method = "POST";
   string url = "http://172.22.18.235/accs";
   string headers = "Content-Type: application/json";
   int timeout = 500;
   char post[], result[];
   string result_headers;
   
//--- prepare JSON data to send
   string json = acc_info.Serialize();
   ArrayResize(post, json.Length(), 0);
   StringToCharArray(json, post, 0, StringLen(json), CP_UTF8);
   ResetLastError();
   
//--- send the request
   int res = WebRequest(method, url, headers, timeout, post, result, result_headers);
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);     }
   else
     {
      Print("Starting post...");
      
      if(res == 201)// HTTP result code 201 (created)
        {
         Print("posted accs");
        }
     }
  }

正如您从文件开头看到的那样,我们正在使用助手函数库来序列化/逆序列化我们的 JSON 数据。 它是由 MQL5 社区的一位成员开发的,您可以在他的 GitHub 存储库中找到该函数库。

现在,我们来插入 MetaTrader 5 历史记录中的交易。 创建新脚本,并添加以下代码。

//+------------------------------------------------------------------+
//|                                                   post_deals.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- gathering the data - Deals
   CJAVal data;
   CJAVal deals;
//--- request trade history
   HistorySelect(0, TimeCurrent());
   int deals_total = HistoryDealsTotal();
//--- iterate over all deals to get data
//--- of each deal from its ticket number
   for(int i = 0; i < deals_total; i++)
     {
      //-- integers
      ulong deal_ticket =   HistoryDealGetTicket(i);
      data["ticket"] =     (int) deal_ticket;
      data["order"] =      (int) HistoryDealGetInteger(deal_ticket, DEAL_ORDER);
      data["position"] =   (int) HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID);
      data["time"] =       (int) HistoryDealGetInteger(deal_ticket, DEAL_TIME);
      data["type"] =       (int) HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
      data["entry"] =      (int) HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
      data["magic"] =      (int) HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
      data["reason"] =     (int) HistoryDealGetInteger(deal_ticket, DEAL_REASON);
      //--- strings
      data["symbol"] =     (string) HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
      //--- doubles
      data["volume"] =     (double) HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
      data["price"] =      (double) HistoryDealGetDouble(deal_ticket, DEAL_PRICE);
      data["profit"] =     (double) HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
      data["swap"] =       (double) HistoryDealGetDouble(deal_ticket, DEAL_SWAP);
      data["comission"] =  (double) HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
 //--- fill in the deals array with each deal data
      deals["deals"].Add(data);
     }
 //--- WebRequest arguments
   string method = "POST";
   string url = "http://172.22.18.235/deals";
   string headers = "Content-Type: application/json";
   int timeout = 500;
   char post[], result[];
   string result_headers;
   
 //--- prepare JSON data to send
   string json = deals.Serialize();
   ArrayResize(post, json.Length(), 0);
   StringToCharArray(json, post, 0, StringLen(json), CP_UTF8);
   ResetLastError();
//--- send the request
   int res = WebRequest(method, url, headers, timeout, post, result, result_headers);
   
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);     }
   else
     {
      Print("Starting post...");
      
      if(res == 201)// HTTP result code 201 (created)
        {
         Print("posted deals");
        }
     }
  }


从 MQL5 查询数据

现在,我们查询最近插入的数据。 在 MetaTrader 5 终端上,创建一个新脚本,并添加以下代码。

//+------------------------------------------------------------------+
//|                                                 get_endpoint.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- choose the testing endpoint
   string endpoint = "accs"; // or "deals"
//--- WebRequest arguments
   string method = "GET";
   string url = "http://172.22.18.235/" + endpoint;
   string cookie = NULL, headers;
   int timeout = 500;
   char post[], result[];
   ResetLastError();
//--- send the request
   int res = WebRequest(method, url, cookie, NULL, timeout, post, 0, result, headers);
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);
     }
   else
     {
      Print("Starting get...");
      if(res == 200)// HTTP result code 200 (OK)
        {
         PrintFormat("Server headers: %s", headers);
         ResetLastError();
         // save the returned JSON in a file
         string terminal_data_path = TerminalInfoString(TERMINAL_DATA_PATH);
         string subfolder = "TutoPostgres";
         string filename = endpoint + "_fromserver.json";
         int filehandle = FileOpen(subfolder + "\\" + filename, FILE_WRITE | FILE_BIN);
         if(filehandle != INVALID_HANDLE)
           {
            FileWriteArray(filehandle, result, 0, ArraySize(result));
            FileClose(filehandle);
            Print(filename + " created at " + terminal_data_path + "\\" + subfolder);
           }
         else
            Print("File open failed with error ", GetLastError());
        }
      else
         PrintFormat("Request to '%s' failed with error code %d", url, res);
     }
  }

通过将终端节点从 “accs” 更改为 “deals”,您可以查询刚刚插入的成交。 检查您的 <MT5 终端路径>\Files\TutoPostgres。 如果一切顺利,那里应该至少有两个文件:accs_fromserver.json 和 deals_fromserver.json。 


在智能系统中消化 JSON 数据

为了消化服务器返回的 JSON 数据,您需要对其进行逆序列化。 上面提到的助手函数库可以做到这一点。

如果您查看了使用上述示例代码查询数据库后保存的 JSON 文件,您可能会看到如下所示的 JSON 字符串:

[
  {
    "a_balance": "10005.93",
    "a_company": "MetaQuotes Software Corp.",
    "a_credit": "0.0",
    "a_currency": "USD",
    "a_ea_allowed": true,
    "a_equity": "10005.93",
    "a_id": 3,
    "a_leverage": 100,
    "a_login": 66744794,
    "a_margin": "0.0",
    "a_margin_free": "10005.93",
    "a_margin_level": "0.0",
    "a_margin_so_call": "50.0",
    "a_margin_so_mode": "0",
    "a_margin_so_so": "30.0",
    "a_name": "MetaTrader 5 Desktop Demo",
    "a_profit": "0.0",
    "a_server": "MetaQuotes-Demo",
    "a_trade_allowed": true,
    "a_trade_mode": "0"
  },
  {
(...)

我们将采用此密钥访问逆序列化的数据。 数组索引中的 “0”(零)表示正在访问返回的第一个帐户。 如果您有多个账户,此端点(“accs”)将返回所有账户,您可以通过此索引遍历访问数组的每个账户。

//+------------------------------------------------------------------+
//|                                                 consume_json.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- choose the testing endpoint
   string endpoint = "accs"; // or "deals"
//--- WebRequest arguments
   string method = "GET";
   string url = "http://172.22.18.235/" + endpoint;
   string cookie = NULL, headers;
   int timeout = 500;
   char post[], result[];
   ResetLastError();
//--- send the request
   int res = WebRequest(method, url, cookie, NULL, timeout, post, 0, result, headers);
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);
     }
   else
     {
      Print("Starting get...");
      if(res == 200)// HTTP result code 200 (OK)
        {
         CJAVal data;
         data.Deserialize(result);
         //--- doubles
         double a_balance =         data[0]["a_balance"].ToDbl();
         double a_credit =          data[0]["a_credit"].ToDbl();
         double a_profit =          data[0]["a_profit"].ToDbl();
         double a_equity =          data[0]["a_equity"].ToDbl();
         double a_margin =          data[0]["a_margin"].ToDbl();
         double a_margin_free =     data[0]["a_margin_free"].ToDbl();
         double a_margin_level =    data[0]["a_margin_level"].ToDbl();
         double a_margin_so_call =  data[0]["a_margin_so_call"].ToDbl();
         double a_margin_so_so =    data[0]["a_margin_so_so"].ToDbl();
         //--- longs
         long a_login =             data[0]["a_login"].ToInt();
         long a_leverage =          data[0]["a_leverage"].ToInt();
         long a_trade_mode =        data[0]["a_trade_mode"].ToInt();
         long a_margin_so_mode =    data[0]["a_margin_so_mode"].ToInt();
         long a_id =                data[0]["a_id"].ToInt(); //--- database generated ID
         //--- strings
         string a_company =         data[0]["a_company"].ToStr();
         string a_currency =        data[0]["a_currency"].ToStr();
         string a_name =            data[0]["a_name"].ToStr();
         string a_server =          data[0]["a_server"].ToStr();
         //--- booleans
         bool a_ea_allowed =        data[0]["a_ea_allowed"].ToBool();
         bool a_trade_allowed =     data[0]["a_trade_allowed"].ToBool();
         //printf("Server headers: %s", headers);
         //--- doubles
         printf("Balance: %d", a_balance);
         printf("Credit: %d", a_credit);
         printf("Profit: %d", a_profit);
         printf("Equity: %d", a_equity);
         printf("Margin: %d", a_margin);
         printf("Margin Free: %d", a_margin_free);
         printf("Margin Level: %d", a_margin_level);
         printf("Margin Call Level: %d", a_margin_so_call);
         printf("Margin Stop Out Level: %d", a_margin_so_so);
         //--- longs
         printf("Login: %d", a_login);
         printf("Leverage: %d", a_leverage);
         printf("Trade Mode: %d", a_trade_mode);
         printf("Margin Stop Out Mode: %d", a_margin_so_mode);
         printf("Database ID: %d", a_id);
         //--- strings
         printf("Company: %s", a_company);
         printf("Currency: %s", a_currency);
         printf("Platform Name: %s", a_name);
         printf("Server: %s", a_server);
         //--- booleans
         printf("Expert Advisor Allowed: %d", a_ea_allowed);
         printf("Trade Allowed: %d", a_trade_allowed);
         Print("Done!");
        }
      else
         PrintFormat("Request to '%s' failed with error code %d", url, res);
     }
  }


SQLite 作为 Postgres 镜像

还可以利用现有 MQL5 基础设施的杠杆效力,方法就是通过将远程数据用作本地 SQLite 数据库。 若要实现此功能,我们需要同步数据库。 这种同步几乎是实时的,只有几秒钟的延迟。 但它可以提高性能,避免网络延迟,允许通过标准 MetaEditor GUI 访问数据,并在您的 MQL5 代码中使用 MQL5 数据库函数。

如果您认为此功能很实用,请让我得知。 我很乐意撰写一个详细的教程,其中包含远程 Postgres 和本地 SQLite 数据库之间同步的示例代码。


结束语

在这些说明中,我们重温了一些当前可用的将 MQL5 代码实例与 Postgres 数据库连接的方法。 我们选择了 REST API 作为可行且快速的替代方案,取代更昂贵的专用驱动程序开发或使用 .dll。 此外,我们开发了一个基本的演示应用,作为如何在 Windows 的 Linux 子系统上为 MQL5/Postgres 设置开发环境的示例。

现在您可以开始开发了! 选择一个好的云端提供商,并撬动 Postgres 分析、自动化、Web 可扩展性、和机器学习扩展的所有功能来增强您的交易。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/12308

MQL5 中的范畴论 (第 6 部分):单态回拉和满态外推 MQL5 中的范畴论 (第 6 部分):单态回拉和满态外推
范畴论是数学的一个多样化和不断扩展的分支,直到最近才在 MQL5 社区中得到一些报道。 这些系列文章旨在探索和验证一些概念和公理,其总体目标是建立一个开放的函数库,提供洞察力,同时也希望进一步在交易者的策略开发中运用这个非凡的领域。
MetaTrader 中的多机器人:从单图表中启动多个机器人 MetaTrader 中的多机器人:从单图表中启动多个机器人
在本文中,我将研究一个简单的模板,用来创建通用的 MetaTrader 机器人,该机器人可以在多个图表上使用,同时仅附加到一个图表,无需在每个单独的图表上为每个机器人实例进行配置。
神经网络实验(第 5 部分):常规化传输到神经网络的输入参数 神经网络实验(第 5 部分):常规化传输到神经网络的输入参数
神经网络是交易者工具包中的终极工具。 我们来检查一下这个假设是否成立。 在交易中运用神经网络,MetaTrader 5 是最接近自给自足的媒介。 为此提供了一个简单的解释。
如何在 MQL5 中集成 ONNX 模型的示例 如何在 MQL5 中集成 ONNX 模型的示例
ONNX(开放神经网络交换)是一种表现神经网络的开放格式。 在本文中,我们将展示如何在一个智能交易系统中同时使用两个 ONNX 模型。