|
📖 目录 系统架构概述 核心技术选型 搭建步骤 核心代码解析 踩坑全记录 最终工作代码 部署说明 扩展建议 1. 系统架构概述 1.1 整体架构图 text 复制 下载 ┌─────────────────────────────────────────────────────────────────────┐ │ 你的软件(任意位置) │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ mt5_test.html │◄──────►│ mt5_server.py │ │ │ │ (浏览器界面) │ │ (Python 服务器) │ │ │ └─────────────────┘ └────────┬────────┘ │ │ │ │ └──────────────────────────────────────┼────────────────────────────┘ │ TCP Socket(纯文本 JSON) ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ MT5 电脑(任意网络) │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ MT5BridgeEA.mq5 │ │ │ │ ├── OnTimer 每秒轮询(不依赖 OnTick) │ │ │ │ ├── SocketIsReadable 检查缓冲区(不直接 SocketRead) │ │ │ │ ├── 收到指令 → 执行交易 → 返回结果 │ │ │ │ └── 每 30 秒发送心跳保持连接 │ │ │ └─────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ 1.2 通信协议 客户端 协议 格式 端口 网页 (浏览器) WebSocket JSON(紧凑无空格) 9000 MT5 EA TCP Socket JSON + 换行符 9000 2. 核心技术选型 2.1 为什么用 TCP Socket 而不是 WebRequest? 方案 优点 缺点 WebRequest 官方支持,无需额外配置 只能发 HTTP,延迟高,不支持双向通信 TCP Socket 双向通信,延迟低,实时性好 需要允许 DLL 导入,需要自己处理粘包 结论:追求实时交易响应,必须用 TCP Socket。 2.2 为什么用 OnTimer 而不是 OnTick? 方案 触发条件 可靠性 OnTick 有新报价才触发 ❌ 市场安静时休眠,收不到消息 OnTimer 定时触发(每秒) ✅ 不受报价影响,稳定轮询 结论:网络通信必须用 OnTimer,不依赖 OnTick。 2.3 为什么必须用 SocketIsReadable? 方案 行为 结果 直接 SocketRead 缓冲区空时返回 -1,报错 ❌ Socket 通道被污染,后续读取全部失败 SocketIsReadable 检查 有数据才读,无数据跳过 ✅ Socket 通道始终健康 结论:先检查,再读取,这是最关键的修复。 3. 搭建步骤 3.1 环境准备 组件 要求 Python 3.11 或更高版本 MT5 Build 3800+(支持 Socket 函数) 操作系统 Windows(MT5 必须运行在 Windows 上) 网络 任意(无需公网 IP,所有连接都主动出站) 3.2 文件清单 文件 位置 说明 mt5_server.py 任意目录(建议桌面) Python 服务器,负责转发消息 mt5_test.html 任意目录(建议桌面) 网页测试界面,双击打开 MT5BridgeEA.mq5 MQL5/Experts/ MT5 EA,负责执行交易 3.3 启动顺序 text 复制 下载 1. 启动 Python 服务器 └── python mt5_server.py 2. 打开网页测试界面 └── 双击 mt5_test.html → 点击"连接" 3. 加载 EA └── 编译 → 拖到图表上 4. 测试交易 └── 输入品种和手数 → 点击"买入" 4. 核心代码解析 4.1 EA 核心:OnTimer 轮询 cpp 复制 下载 void OnInit() { ConnectToServer(); EventSetTimer(1); // ★ 每秒触发一次 } void OnTick() { // ★ 留空,不依赖 OnTick } void OnTimer() { // 1. 检查连接 if(!g_connected) { ConnectToServer(); return; } // 2. ★ 先检查缓冲区是否有数据 int len = SocketIsReadable(g_socket); if(len > 0) { uchar buffer[]; SocketRead(g_socket, buffer, len, 1000); string msg = CharArrayToString(buffer, 0, len); ProcessMessages(msg); } // 3. 心跳(每30秒) if(TimeCurrent() - lastHeartbeat >= 30) { lastHeartbeat = TimeCurrent(); SocketSendEx(g_socket, "{\"type\":\"ping\"}"); } } 4.2 服务器核心:区分客户端类型 python 复制 下载 def send_message(client, msg): data = json.dumps(msg, separators=(',', ':')) # ★ 紧凑 JSON if client.get('ws'): # 网页 → WebSocket 编码 client['sock'].send(encode_frame(data)) else: # EA → 纯文本 + 换行符 client['sock'].send((data + '\n').encode()) 4.3 JSON 解析核心:处理紧凑格式 cpp 复制 下载 string GetJsonString(string json, string key) { string search = "\"" + key + "\":"; int start = StringFind(json, search); if(start == -1) return ""; start += StringLen(search); // ★ 跳过空格 while(start < StringLen(json) && json[start] == ' ') start++; if(json[start] == '"') { start++; int end = start; while(end < StringLen(json) && json[end] != '"') end++; return StringSubstr(json, start, end - start); } else { int end = start; while(end < StringLen(json) && json[end] != ',' && json[end] != '}') end++; return StringSubstr(json, start, end - start); } } 5. 踩坑全记录 5.1 坑位总览 # 问题 原因 解决方案 1 EA 连不上服务器 SocketCreate 参数错误 用 SocketCreate(0) 2 编译报错 uchar&[] 传了 char[] 而不是 uchar[] 用 uchar buffer[] 3 OnTick 执行几次就停了 品种没有新报价 改用 OnTimer 4 EA 收不到消息 直接 SocketRead 空缓冲区报错 用 SocketIsReadable 先检查 5 JSON 解析失败 Python 发的 JSON 带空格 separators=(',', ':') 6 EA 收到乱码 服务器发了 WebSocket 帧头 区分客户端,EA 用纯文本 7 注册失败 注册消息 JSON 格式错误 检查引号配对 8 StringGetChar 未声明 MQL5 没有此函数 用 StringSubstr 替代 9 ACCOUNT_FREEMARGIN 弃用 新版 MT5 改了常量名 用 ACCOUNT_MARGIN_FREE 5.2 核心经验总结 text 复制 下载 ┌─────────────────────────────────────────────────────────────────┐ │ MT5 Socket 通信 黄金法则 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 用 OnTimer 不要用 OnTick │ │ 理由:OnTick 依赖报价,OnTimer 稳定每秒执行 │ │ │ │ 2. 先 SocketIsReadable 再 SocketRead │ │ 理由:直接读空缓冲区会报错,污染 Socket 通道 │ │ │ │ 3. JSON 必须紧凑无空格 │ │ 理由:EA 的手动解析器不处理多余空格 │ │ │ │ 4. 区分客户端类型 │ │ 理由:网页用 WebSocket,EA 用纯文本 TCP │ │ │ │ 5. 每个环节都加 Print 日志 │ │ 理由:MT5 专家日志是唯一能告诉你真相的东西 │ │ │ └─────────────────────────────────────────────────────────────────┘ 6. 最终工作代码 6.1 文件清单 文件 行数 说明 mt5_server.py ~200 行 Python 服务器 mt5_test.html ~350 行 网页测试界面 MT5BridgeEA.mq5 ~350 行 MT5 EA 6.2 关键配置 EA 输入参数: 参数 值 说明 ServerHost 127.0.0.1 服务器 IP(同机测试) ServerPort 9000 服务器端口 MT5 设置: 设置 状态 位置 允许 EA 交易 ✅ 开启 工具 → 选项 → EA 交易 允许 DLL 导入 ✅ 开启 EA 属性 自动交易 ✅ 绿色 工具栏 (Ctrl+E) 7. 部署说明 7.1 单机测试 text 复制 下载 同一台电脑: MT5 EA → 127.0.0.1:9000 ← Python 服务器 ← 网页 7.2 局域网部署 text 复制 下载 MT5 电脑:192.168.1.100 软件电脑:192.168.1.101 EA 配置:ServerHost = 192.168.1.101 7.3 互联网部署(无需公网 IP) text 复制 下载 MT5 电脑(内网)──┐ ├── 中继服务器(有公网 IP) 软件电脑(内网)──┘ 所有客户端都主动连接中继服务器 💡 用 ngrok tcp 9000 可以快速暴露内网端口到公网。 7.4 防火墙配置 场景 配置 同机测试 无需配置 局域网 Windows 防火墙允许 9000 端口入站 互联网 路由器做端口映射,或使用内网穿透 8. 扩展建议 8.1 功能扩展 止盈止损设置 挂单(限价单、止损单) 批量平仓 多品种交易 图表显示持仓 历史记录查询 8.2 安全增强 AES 加密通信 API 密钥认证 IP 白名单 操作日志审计 8.3 生产部署 Python 服务器做成 Windows 服务(自动启动) 日志轮转(防止日志文件过大) 异常监控和告警 EA 版本自动更新 📌 快速启动命令 bash 复制 下载 # 1. 启动服务器 cd C:\Users\Administrator python mt5_server.py # 2. 打开网页(双击 mt5_test.html) # 3. 加载 EA(在 MT5 中编译并拖到图表) # 4. 测试交易 # 品种:XAUUSD.demo 手数:0.01 点击"买入" ✅ 验证成功的标志 MT5 专家日志: text 复制 下载 ✅ 已连接! 📤 已注册 ✅ 注册确认 ⏰ OnTimer 执行中... 第 10 次 📬 缓冲区有 87 字节可读 📨 收到: {"type":"command","action":"buy","symbol":"XAUUSD.demo","volume":0.01} 📊 执行: buy XAUUSD.demo 0.01 ✅ 交易成功!订单号: 144844889 🎉 恭喜!你现在拥有了一套完整的 MT5 远程交易系统! |
外汇高手分享