如何将 MetaTrader 5 与 PostgreSQL 连接·综合运用
🗄️

如何将 MetaTrader 5 与 PostgreSQL 连接·综合运用

(3/3)·从原生 WebRequest 到 REST API 抽象层,打通 MQL5 与远程 Postgres 的数据读写闭环

偏理论进阶 第 3/3 篇
不少交易者把 MT5 的 SQLite 原生集成当成万能存储,EA 跑久了文件锁和并发写入直接拖垮回测。远程 Postgres 若只靠裸 WebRequest 拼 SQL,超时和注入漏洞会在实盘暴露。先把抽象层和连接方案理清,再谈部署。

「把远端账户快照落进本地变量」

当 HTTP 返回码为 200 时,说明远端账户接口已正常响应,这时候要把 JSON 反序列化成可操作的字段。下面这段代码演示了如何用 CJAVal 把返回结果拆成 double、long、string、bool 四类本地变量。 double 段覆盖了余额、信用、利润、净值、已用保证金、空闲保证金、保证金水平、爆仓 call/so 阈值——这些是盯盘时最容易被忽略但决定风控边界的数字。 long 与 string 段拿到登录号、杠杆、交易模式、公司名、币种、服务器等静态身份;bool 段则直接暴露 EA 与手动交易是否被 broker 放行。 外汇与贵金属自带高杠杆高风险,拿到 a_margin_level 与 a_margin_so_so 后,应自行核对平台强平线,勿盲信接口返回值。

MQL5 / C++
if(res == class="num">200)class=class="str">"cmt">// HTTP result code class="num">200 (OK)
  {
   CJAVal data;
   data.Deserialize(result);
   class=class="str">"cmt">//--- doubles
   class="type">class="kw">double a_balance =        data[class="num">0]["a_balance"].ToDbl();
   class="type">class="kw">double a_credit =         data[class="num">0]["a_credit"].ToDbl();
   class="type">class="kw">double a_profit =         data[class="num">0]["a_profit"].ToDbl();
   class="type">class="kw">double a_equity =         data[class="num">0]["a_equity"].ToDbl();
   class="type">class="kw">double a_margin =         data[class="num">0]["a_margin"].ToDbl();
   class="type">class="kw">double a_margin_free =    data[class="num">0]["a_margin_free"].ToDbl();
   class="type">class="kw">double a_margin_level =   data[class="num">0]["a_margin_level"].ToDbl();
   class="type">class="kw">double a_margin_so_call = data[class="num">0]["a_margin_so_call"].ToDbl();
   class="type">class="kw">double a_margin_so_so =   data[class="num">0]["a_margin_so_so"].ToDbl();
   class=class="str">"cmt">//--- longs
   class="type">long a_login =          data[class="num">0]["a_login"].ToInt();
   class="type">long a_leverage =       data[class="num">0]["a_leverage"].ToInt();
   class="type">long a_trade_mode =     data[class="num">0]["a_trade_mode"].ToInt();
   class="type">long a_margin_so_mode = data[class="num">0]["a_margin_so_mode"].ToInt();
   class="type">long a_id =             data[class="num">0]["a_id"].ToInt(); class=class="str">"cmt">//--- database generated ID
   class=class="str">"cmt">//--- strings
   class="type">class="kw">string a_company =  data[class="num">0]["a_company"].ToStr();
   class="type">class="kw">string a_currency = data[class="num">0]["a_currency"].ToStr();
   class="type">class="kw">string a_name =     data[class="num">0]["a_name"].ToStr();
   class="type">class="kw">string a_server =   data[class="num">0]["a_server"].ToStr();
   class=class="str">"cmt">//--- booleans
   class="type">bool a_ea_allowed =     data[class="num">0]["a_ea_allowed"].ToBool();
   class="type">bool a_trade_allowed =  data[class="num">0]["a_trade_allowed"].ToBool();
逐行拆解:第1行判断 res==200 才进解析分支;CJAVal 的 Deserialize 吃下 result 字符串。double 块里 ToDbl() 把 JSON 数值转成本地浮点,a_margin_level 若为 0 可能代表接口未返回该字段。long 块 ToInt() 处理整数身份与模式,a_id 是数据库自增主键一般仅作记录。string 块 ToStr() 取文本,bool 块 ToBool() 拿权限开关——a_trade_allowed 为 false 时任何下单都会被拒。 开 MT5 把这段贴进 EA 的 OnTimer,配合 WebRequest 轮询,就能把账户核心状态频率可控地拉到本地做告警。

MQL5 / C++
if(res == class="num">200)class=class="str">"cmt">// HTTP result code class="num">200 (OK)
  {
   CJAVal data;
   data.Deserialize(result);
   class=class="str">"cmt">//--- doubles
   class="type">class="kw">double a_balance =        data[class="num">0]["a_balance"].ToDbl();
   class="type">class="kw">double a_credit =         data[class="num">0]["a_credit"].ToDbl();
   class="type">class="kw">double a_profit =         data[class="num">0]["a_profit"].ToDbl();
   class="type">class="kw">double a_equity =         data[class="num">0]["a_equity"].ToDbl();
   class="type">class="kw">double a_margin =         data[class="num">0]["a_margin"].ToDbl();
   class="type">class="kw">double a_margin_free =    data[class="num">0]["a_margin_free"].ToDbl();
   class="type">class="kw">double a_margin_level =   data[class="num">0]["a_margin_level"].ToDbl();
   class="type">class="kw">double a_margin_so_call = data[class="num">0]["a_margin_so_call"].ToDbl();
   class="type">class="kw">double a_margin_so_so =   data[class="num">0]["a_margin_so_so"].ToDbl();
   class=class="str">"cmt">//--- longs
   class="type">long a_login =          data[class="num">0]["a_login"].ToInt();
   class="type">long a_leverage =       data[class="num">0]["a_leverage"].ToInt();
   class="type">long a_trade_mode =     data[class="num">0]["a_trade_mode"].ToInt();
   class="type">long a_margin_so_mode = data[class="num">0]["a_margin_so_mode"].ToInt();
   class="type">long a_id =             data[class="num">0]["a_id"].ToInt(); class=class="str">"cmt">//--- database generated ID
   class=class="str">"cmt">//--- strings
   class="type">class="kw">string a_company =  data[class="num">0]["a_company"].ToStr();
   class="type">class="kw">string a_currency = data[class="num">0]["a_currency"].ToStr();
   class="type">class="kw">string a_name =     data[class="num">0]["a_name"].ToStr();
   class="type">class="kw">string a_server =   data[class="num">0]["a_server"].ToStr();
   class=class="str">"cmt">//--- booleans
   class="type">bool a_ea_allowed =     data[class="num">0]["a_ea_allowed"].ToBool();
   class="type">bool a_trade_allowed =  data[class="num">0]["a_trade_allowed"].ToBool();

◍ 把账户状态字段一口气打出来

这段脚本的核心动作,是把当前 MT5 账户的关键字段通过 printf 直接输出到专家日志,方便你在不打开终端账户窗口的情况下快速核对。 被注释掉的 Server headers 行说明原始请求里还能拿到 HTTP 头,但本段只聚焦账户结构本身,没有去解析响应头。 double 类字段覆盖了 Balance、Credit、Profit、Equity、Margin、Margin Free、Margin Level、Margin Call Level、Margin Stop Out Level,共 9 项数值,分别对应账户权益与强平阈值链。 long 类包括 Login、Leverage、Trade Mode、Margin Stop Out Mode、Database ID,字符串类有 Company、Currency、Platform Name、Server,布尔类用 %d 输出 Expert Advisor Allowed 与 Trade Allowed。 若请求返回非 200,else 分支会用 PrintFormat 把 URL 和错误码打到日志,你可以据此判断是 broker 接口拒绝还是网络层问题。外汇与贵金属杠杆波动大,输出 Margin Level 后建议手动比对终端显示值,高风险品种下数值偏差可能直接触发强平。

MQL5 / C++
   class=class="str">"cmt">//printf("Server headers: %s", headers);
   class=class="str">"cmt">//--- 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);
   class=class="str">"cmt">//--- 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);
   class=class="str">"cmt">//--- strings
   printf("Company: %s", a_company);
   printf("Currency: %s", a_currency);
   printf("Platform Name: %s", a_name);
   printf("Server: %s", a_server);
   class=class="str">"cmt">//--- booleans
   printf("Expert Advisor Allowed: %d", a_ea_allowed);
   printf("Trade Allowed: %d", a_trade_allowed);
   Print("Done!");
   }
   else
      PrintFormat("Request to &class="macro">#x27;%s&class="macro">#x27; failed with error code %d", url, res);
   }
}

收束

前面两节把 MQL5 通过 REST API 对接 Postgres 的链路和 WSL 开发环境都搭清楚了,核心结论是用轻量 REST 替代专用驱动或 dll,省下的不只是授权费,还有 Windows 下编译排错的精力。 附件里两个 zip 共约 18 KB(5.99 KB 基础文件 + 12.38 KB 演示应用),解压后直接在 WSL 跑起本地 Postgres 就能复现 demo 的请求回路。 外汇与贵金属品种叠加这类外部存储分析,天然伴随连接中断、云端账单失控的高风险,上线前先在单机容器里压一遍并发再说。

把环境校验交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到 WebRequest 白名单与远程库延迟的实时提示,你专注决策即可。

常见问题

SQLite 基于文件、单写者锁,EA 多实例或高频插数易阻塞;Postgres 为服务端 RDBMS,支持多连接与事务隔离,更适合跨终端共享行情与信号数据。
概率上会多一跳 HTTP 开销,但托管商边缘节点通常将 RTT 控制在几十毫秒内;相比裸 WebRequest 直拼 SQL,抽象层换来了免驱升级与跨 RDBMS 复用。
可以,小布盯盘的 AIGC 模块支持配置外部库读取接口,把已落库的订单流按品种聚合后渲染到对应页面,省去手动导 CSV。
倾向选带免费层的托管 REST Postgres,免去自建容器与备份;正式生产再评估私有部署,完整权衡见前篇方案对比。