使用MQL5和Python集成经纪商API与智能交易系统·进阶篇
🔌

使用MQL5和Python集成经纪商API与智能交易系统·进阶篇

(2/3)·手动登portal充钱太慢?这篇用Python脚本把MT5和券商API焊死,余额阈值触发自动入金

含代码示例实战向 第 2/3 篇

不少交易者账户余额跌破警戒线才想起来登经纪商后台手动入金,行情早跑了一段。把补仓动作绑在EA里而不是浏览器里,才能把资金断裂的窗口压到最小。外汇与贵金属杠杆高,断档即意味被动平仓,这条链路值得自己搭一遍。

◍ 用 Python 给 MT5 接上券商 WebSocket 通道

想让 MT5 的 EA 去调券商的账户接口(比如 Deriv 的入金指令),最轻量的办法是写个独立 Python 脚本在后台跑,靠两个 txt 文件跟 MQL5 做中介。脚本命名 deriv_api_handler.py,保存路径必须记牢,后面 EA 里要硬写进去。 初始化阶段先 import 四个库:json 管消息序列化,websocket 负责跟券商 API 握手,time 做必要的 sleep 暂停,os 用来检查指令文件是否存在。API_URL 和 API_TOKEN 是连接 Deriv 服务器的钥匙,文件路径要选 MQL5 的 Common/Files 目录(如 C:/Users/YourComputerName/AppData/Roaming/MetaQuotes/Terminal/Common/Files/),否则两边有权限读不到。 connect_to_deriv() 函数里用 ws.connect(API_URL) 建连,随后发 authorize 消息做身份校验;服务器回的 JSON 若带 error 字段就打印并 return False,用 try-except 包住避免整脚本崩。process_command() 把 MQL5 传来的 JSON 字符串解析成字典,看到键 mt5_deposit 就调专门的 mt5_deposit() 函数,未知命令回错误信息——这种模块化方便以后加新指令类型。 读指令靠检查 mql5_to_python.txt:存在就读内容、去 BOM 和空白、打印调试、然后删文件,保证同一条指令不会被重复处理;没有文件就 return None。回写走 python_to_mql5.txt,用 json.dumps() 把字典转字符串落盘,闭环完成。主循环持续调 read_command,有指令就处理并写回,异常时打印错误干净退出,维持实时响应。外汇/贵金属券商 API 涉及账户资金操作,属于高风险动作,实盘前务必在模拟环境跑通整套文件握手。

MQL5 / C++
class="kw">import json
class="kw">import websocket
class="kw">import time
class="kw">import os
# Configuration
API_URL = "wss:class=class="str">"cmt">//ws.binaryws.com/websockets/v3?app_id= Your app ID"   class="macro">#Replace with your App ID created in Deriv API dashboard
API_TOKEN = "Your API token"   class="macro">#Replace with your actual token
# File paths(Replace YourComputerName with the actual name of your computer)
MQL5_TO_PYTHON = "C:/Users/YourComputerName/AppData/Roaming/MetaQuotes/Terminal/Common/Files/mql5_to_python.txt"
PYTHON_TO_MQL5 = "C:/Users/YourComputerName/AppData/Roaming/MetaQuotes/Terminal/Common/Files/python_to_mql5.txt"
def connect_to_deriv():
    """Connects to Deriv&class="macro">#x27;s WebSocket API."""
    try:
        ws.connect(API_URL)
        ws.send(json.dumps({"authorize": API_TOKEN}))
        response = json.loads(ws.recv())
        print(f"Authorization Response: {response}")
        if response.get("error"):
            print("Authorization failed:", response["error"]["message"])
            class="kw">return False
        class="kw">return True
    except Exception as e:
        print(f"Error during authorization: {e}")
        class="kw">return False
def process_command(command):
    """Processes a command from MQL5."""
    try:
        command_data = json.loads(command)  # Parse the JSON command
        if "mt5_deposit" in command_data:
            class="kw">return mt5_deposit(

MT5 与 Python 桥接的命令收发细节

这段桥接逻辑里,MQL5 侧把指令写进文件,Python 侧用 read_command() 轮询并删除源文件,避免重复执行。实测中若文件带 BOM(\ufeff),json.loads 会直接抛错,所以代码里专门做了 startswith('\ufeff') 的剥离。 mt5_deposit() 通过 WebSocket 向服务端推送 mt5_deposit:1 及金额、来源账户、目标账户字段,再用 ws.recv() 阻塞等回执。任何异常都被捕获成 {'error': ...} 字典返回,不会让守护进程崩掉。 write_response() 把 Python 的回执 json.dumps 后写回 PYTHON_TO_MQL5,MQL5 EA 下次读文件就能拿到结果。外汇与贵金属跨账户调拨属高风险操作,桥接失败可能导致指令丢失,建议先在模拟账户验证文件读写延迟(通常 10~50ms 级)。

MQL5 / C++
def mt5_deposit(amount, from_binary, to_mt5):
    """Performs a deposit operation to the MT5 account."""
    try:
        ws.send(json.dumps({
            "mt5_deposit": class="num">1,
            "amount": amount,
            "from_binary": from_binary,
            "to_mt5": to_mt5
        }))
        response = ws.recv()
        class="kw">return json.loads(response)
    except Exception as e:
        class="kw">return {"error": f"Error during deposit operation: {e}"}

def read_command():
    """Reads a command from the MQL5 file and deletes the file after reading."""
    print(f"Checking for command file at: {MQL5_TO_PYTHON}")
    if os.path.exists(MQL5_TO_PYTHON):
        print(f"Command file found: {MQL5_TO_PYTHON}")
        with open(MQL5_TO_PYTHON, "r", encoding="utf-class="num">8") as file:
            command = file.read().strip()
        print(f"Raw Command read: {repr(command)}")
        # Strip potential BOM and whitespace
        if command.startswith("\ufeff"):
            command = command[class="num">1:]
        print(f"Processed Command: {repr(command)}")
        os.remove(MQL5_TO_PYTHON)  # Remove file after reading
        class="kw">return command
    print(f"Command file not found at: {MQL5_TO_PYTHON}")
    class="kw">return None

def write_response(response):
    """Writes a response to the MQL5 file."""
    with open(PYTHON_TO_MQL5, "w", encoding="utf-class="num">8") as file:
        file.write(json.dumps(response))

「命令轮询的死循环与单次退出」

这段逻辑把主流程包在一个 while True 里,本意是持续监听外部命令文件,但实际只在首次读到或读不到时就 break,等于把“死循环”退化成“跑一次就退”。 read_command() 负责从磁盘或管道取指令;拿到非空字符串就打印 Processing command、交给 process_command 处理、再把回写结果落到 write_response,随后打印 Response written 并 break。若返回空(文件缺失),直接打印 No command file found 并退出。 except 块捕获任意异常,打印 Error in main loop 后同样 break——三种分支殊途同归:循环最多迭代一次。若你想做真正的多指令队列,得把 break 换成 sleep + continue,否则 EA 初始化即停。外汇与贵金属行情瞬息,这类空转脚本可能错过入场窗口,属高风险用法。

MQL5 / C++
    class="kw">while True:
        try:
            command = read_command()
            if command:
                print(f"Processing command: {command}")
                response = process_command(command)
                print(f"Response: {response}")
                write_response(response)
                print("Response written. Exiting loop.")
                break  # Exit the loop after processing one command
            else:
                print("No command file found. Exiting.")
                break  # Exit the loop if the command file is not found
        except Exception as e:
            print(f"Error in main loop: {e}")
            break  # Exit the loop on unexpected error

◍ 让EA自己盯着余额去调Python入金

做资金管理的EA不一定非得下单,这一节的重点是把券商API接进MT5里,让程序在余额跌破线时自动喊外部脚本去入金。核心思路是用shell32的ShellExecuteW从EA内部拉起deriv_api_handler.py,MQL5只负责判余额和写指令文件,真正的转账动作交给Python侧。 为了方便测试,作者把检查逻辑全塞进了OnInit(),而不是常规的OnTimer()。这样EA一加载就跑一次API验证,不用等周期触发;但真要长周期监控账户,还是得挪到OnTimer()里做定时轮询,否则每次重启终端才查一次。 输入参数里有两个硬数字值得注意:balance_threshold和top_up_amount默认都是100000.0,也就是余额低于10万才触发、单次补10万。from_binary和to_mt5分别是券商二进制账户ID和MT5账户ID,留了CR0000000和MTRReplace做占位,实盘前必须换成真实账号。 通信靠一个mql5_to_python.txt的JSON指令文件,EA用FileOpen写进TerminalInfoString(TERMINAL_COMMONDATA_PATH)下的Files目录,Python跑完再回写一个响应文件,EA读到了“成功”才继续。外汇和贵金属账户涉及真实资金调拨,这种自动入金链路任何环节失败都可能卡死流程,建议在模拟环境先跑通再碰实盘。 下面这段是EA头部和OnInit入口的原文,逐行看清楚导入和判断怎么落的: #property copyright "Copyright 2024, Clemence Benjamin" // 设EA元数据版权信息 #property link "[MQL5官方文档] // 设作者链接 #property version "1.01" // 设EA版本号 #property description "Deposit and Withdraw funds between broker and trading account within the EA" // 设EA功能描述 #import "shell32.dll" // 导入shell32动态库 int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); // 声明Windows API函数,用于执行外部程序 #import // 结束导入段 input double balance_threshold = 100000.0; // 定义余额阈值输入项,默认10万 input double top_up_amount = 100000.0; // 定义补仓金额输入项,默认10万 input string from_binary = "CR0000000"; // 定义出金源账户ID输入项,占位 input string to_mt5 = "MTRReplace"; // 定义入金目标MT5账户ID输入项,占位 input string python_script_path = "C:\\Users\\YourComputerName\\PathTo\\deriv_api_handler.py"; // 定义Python脚本绝对路径输入项 input string python_exe = "python"; // 定义Python可执行命令输入项,依赖PATH环境 int OnInit() // EA初始化入口函数 { // 函数体开始 double current_balance = AccountInfoDouble(ACCOUNT_BALANCE); // 取当前账户余额 Print("Current Account Balance: ", current_balance); // 打印余额到日志 if(current_balance < balance_threshold) // 判断余额是否低于阈值 { // 条件成立进入入金分支 Print("Balance is below the threshold. Attempting a deposit..."); // 打印触发提示 string command_file = "mql5_to_python.txt"; // 定义指令文件名 string command_path = TerminalInfoString(TERMINAL_COMMONDATA_PATH) + "\\Files\\" + command_file; // 拼出公共文件目录全路径

int handle = FileOpen(command_file, FILE_WRITEFILE_COMMONFILE_TXTFILE_ANSI); // 以写/公共/文本/ANSI模式打开指令文件
MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Fund Manager EA.mq5  |
class=class="str">"cmt">//|                     Copyright class="num">2024, Clemence Benjamin |
class=class="str">"cmt">//|     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2024, Clemence Benjamin"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.01"
class="macro">#class="kw">property description "Deposit and Withdraw funds between broker and trading account within the EA"
class="macro">#class="kw">import "shell32.dll"
class="type">int ShellExecuteW(class="type">int hwnd, class="type">class="kw">string lpOperation, class="type">class="kw">string lpFile, class="type">class="kw">string lpParameters, class="type">class="kw">string lpDirectory, class="type">int nShowCmd);
class="macro">#class="kw">import

input class="type">class="kw">double balance_threshold = class="num">100000.0;   class=class="str">"cmt">// Threshold balance for top-up
input class="type">class="kw">double top_up_amount = class="num">100000.0;       class=class="str">"cmt">// Amount to top-up if balance is below threshold
input class="type">class="kw">string from_binary = "CR0000000";      class=class="str">"cmt">// Binary account ID to withdraw funds from. Replace zero with real one
input class="type">class="kw">string to_mt5 = "MTRReplace";      class=class="str">"cmt">// MT5 account ID to deposit funds into. Replace with your MT5 acc from Deriv Broker
input class="type">class="kw">string python_script_path = "C:\Users\YourComputerName\PathTo\deriv_api_handler.py"; class=class="str">"cmt">// Python script path
input class="type">class="kw">string python_exe = "python";          class=class="str">"cmt">// Python executable command(ensure Python is in PATH)
class="type">int OnInit()
  {
   class="type">class="kw">double current_balance = AccountInfoDouble(ACCOUNT_BALANCE);
   Print("Current Account Balance: ", current_balance);
   if(current_balance < balance_threshold)
     {
      Print("Balance is below the threshold. Attempting a deposit...");
class="type">class="kw">string command_file = "mql5_to_python.txt";
class="type">class="kw">string command_path = TerminalInfoString(TERMINAL_COMMONDATA_PATH) + "\Files\" + command_file;
class="type">int handle = FileOpen(command_file, FILE_WRITE | FILE_COMMON | FILE_TXT | FILE_ANSI);

MT5 与 Python 的入金指令握手

EA 在判定余额低于阈值后,会把一笔入金请求写成 JSON 字符串丢进共享文件,再唤起本地 Python 脚本去执行跨账户调拨。下面这段就是实际落盘与回读响应的核心片段。 string deposit_command = StringFormat("{\"mt5_deposit\": 1, \"amount\": %.2f, \"from_binary\": \"%s\", \"req_id\": 1, \"to_mt5\": \"%s\"}", top_up_amount, from_binary, to_mt5); FileWrite(handle, deposit_command); FileClose(handle); Print("Deposit command written to file: ", command_path); 这一组先把格式化后的指令写进公共目录文件,路径由 command_path 指定,写毕立即关闭句柄,避免 Python 侧读锁。 int result = ShellExecuteW(0, "open", python_exe, python_script_path, NULL, 1); if(result <= 32) { Print("Failed to launch Python script. Error code: ", result); return(INIT_FAILED); } else { Print("Python script launched successfully."); } ShellExecuteW 返回值 ≤32 代表系统级拉起失败,常见为 2(文件缺失)或 31(无关联程序);此时直接 INIT_FAILED 终止初始化。 string response_file = "python_to_mql5.txt"; if(FileIsExist(response_file, FILE_COMMON)) {

handle = FileOpen(response_file, FILE_READFILE_COMMONFILE_TXTFILE_ANSI);

string response = FileReadString(handle); FileClose(handle); Print("Response from Python: ", response); if(StringFind(response, "\"status\":\"success\"") >= 0) { Print("Deposit was successful."); } else { Print("Deposit failed. Response: ", response); } } else { Print("Response file not found. Ensure the Python script is running and processing the command."); } 回读阶段只认 FILE_COMMON 下的 python_to_mql5.txt,用 StringFind 粗匹配 success 状态;若文件根本没生成,多半是 Python 侧没起来或没写完。 外汇与贵金属账户涉及跨平台资金调度,属高风险操作,任何自动入金逻辑都可能在网络或权限异常时失效,实盘前务必在模拟环境跑通文件握手。

MQL5 / C++
class="type">class="kw">string deposit_command = StringFormat("{\"mt5_deposit\": class="num">1, \"amount\": %.2f, \"from_binary\": \"%s\", \"req_id\": class="num">1, \"to_mt5\": \"%s\"}", top_up_amount, from_binary, to_mt5);
FileWrite(handle, deposit_command);
FileClose(handle);
Print("Deposit command written to file: ", command_path);
class="type">int result = ShellExecuteW(class="num">0, "open", python_exe, python_script_path, NULL, class="num">1);
if(result <= class="num">32)
  {
   Print("Failed to launch Python script. Error code: ", result);
   class="kw">return(INIT_FAILED);
  }
else
  {
   Print("Python script launched successfully.");
  }
class="type">class="kw">string response_file = "python_to_mql5.txt";
if(FileIsExist(response_file, FILE_COMMON))
  {
   handle = FileOpen(response_file, FILE_READ | FILE_COMMON | FILE_TXT | FILE_ANSI);
   class="type">class="kw">string response = FileReadString(handle);
   FileClose(handle);
   Print("Response from Python: ", response);
   if(StringFind(response, "\"status\":\"success\"") >= class="num">0)
     {
      Print("Deposit was successful.");
     }
   else
     {
      Print("Deposit failed. Response: ", response);
     }
  }
else
  {
   Print("Response file not found. Ensure the Python script is running and processing the command.");
  }
else
  {
   Print("Balance is above the threshold. No deposit attempt needed.");
  }
class="kw">return(INIT_SUCCEEDED);
class="type">void OnDeinit(const class="type">int reason)
  {
   Print("Expert deinitialized.");
  }
把桥接状态交给小布盯盘
MT5与券商API的WebSocket心跳、Python桥是否掉线,这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到,你只管调阈值逻辑。

常见问题

MQL5的WebRequest受限较多且难维持长连,Python侧用WebSocket库做常驻桥更稳,EA只负责发指令和收结果。
通常密钥在开发者后台生成并关联具体账号或应用,调用时要带进参数做鉴权,换账号需重新申请。
没有统一值,倾向按你单笔最大风险敞口的1.5到2倍留缓冲,兼顾滑点和高波动时段。
小布目前提供桥接健康度与掉线告警的看板,脚本运行仍需在本地或服务器自备环境,它不托管你的交易进程。
正常实现里应有去重锁和回执确认,断线重连后先拉账户状态再决策,避免重复下单。