开发Python交易机器人(第三部分):实现基于模型的交易算法(基础篇)
📘

开发Python交易机器人(第三部分):实现基于模型的交易算法(基础篇)

第 1/3 篇

把训练好的模型接进 MT5 实盘

前两步已经把数据打标、模型训练加交叉验证跑完,下一步是用 Python 直连 MT5 下单。MetaTrader 5 官方给的 Python 包把平台里的函数和系统类都暴露出来,等于在本地脚本里直接调终端能力,不必再绕 MQL5 的 EA 壳。 对做价格行为结合 AIGC 的交易者来说,这套接法的价值在于:模型侧用 sklearn 或 torch 跑完,信号可以直接通过同一个进程发到贵金属或外汇账户。外汇与贵金属杠杆高、滑点和跳空频繁,实盘前务必在策略测试器外再用 demo 账户跑一遍连通性。 本小节只交代链路起点——后面才会写具体下单与仓位函数,但你现在就该在终端开好 Python 环境、装好 MetaTrader5 库,否则后续代码粘过来也跑不动。

◍ 用 Python 库把模型信号接进 MT5 下单

把训练好的价格方向模型转成实盘动作,核心就是按标签开仓:模型吐出「涨」就发多头订单,「跌」就发空头订单,两张单都带固定的止损与止盈距离,不靠人工盯盘。 MetaTrader 5 的 Python 库(MetaTrader5 模块)能直接下订单、平仓、改 SL/TP,等于把模型预测和券商执行打通成一条自动链路。之前分析和训练阶段攒下的特征与阈值,在这里变成实时推送的开仓/平仓指令,算法连续性取决于你推送频率和数据延迟。 外汇与贵金属杠杆高,自动算法若标签翻转快,可能在窄幅震荡里连续触发小额止损,实盘前务必在 MT5 策略测试器用历史 tick 跑一遍信号延迟。

「把 MT5 接进 Python 的第一步」

要让小布这类 AIGC 工具直接读 MT5 行情,得先打通本地环境。核心是装齐 Python 依赖,并把终端可执行文件路径指给代码。 依赖建议用 pip 一次性拉齐:numpy、pandas 负责数组与表处理,MetaTrader5 是官方桥接包,scikit-learn 和 xgboost 留给后续训练。路径那行必须换成你机器上 terminal64.exe 的真实位置,RoboForex 这种带券商名的目录只是示例。 外汇与贵金属杠杆高、滑点随机,环境跑通不代表策略能盈利,仅代表数据管道可用。

MQL5 / C++
pip install numpy pandas MetaTrader5 scikit-learn xgboost
terminal_path = "C:/Program Files/RoboForex - MetaTrader class="num">5/Arima/terminal64.exe"

让模型预测直接变成MT5挂单

把训练好的模型接进实盘,核心就是一个 online_trading 函数:传入品种、特征向量和模型对象,它负责连终端、取报价、出信号、下单单。函数先按 terminal_path 指向的 exe 初始化 MT5,连不上就直接退出,不浪费后续轮询。 信号判定很直白:模型输出最后一个值大于 0.5 倾向开多,小于 0.5 倾向开空。每笔固定手数 0.3,多单 SL 放在 ask 下方 150 点、TP 放在 ask 上方 800 点;空单对称处理。点值是 symbol_info.point,所以止损止盈是随品种精度自动换算的。 并发控制靠 MAX_OPEN_TRADES 常量:持仓数没到上限且信号明确才发请求,否则空转等下一 tick。外汇和贵金属杠杆高,SL/TP 虽能限损,但滑点和断连仍可能让实际亏损超出预设,跑之前先在策略测试器验证点值计算。 代码里初始化路径写死为 RoboForex 的 terminal64.exe,换券商得改字符串;attempts 设了 30000 次等待品种信息,按每次 sleep 5 秒算最长可能卡 150000 秒,实盘前建议调小。

MQL5 / C++
def online_trading(symbol, features, model):
    terminal_path = "C:/Program Files/RoboForex - MetaTrader class="num">5/Arima/terminal64.exe"
    if not mt5.initialize(path=terminal_path):
        print("Error: Failed to connect to MetaTrader class="num">5 terminal")
        class="kw">return
    open_trades = class="num">0
    e = None
    attempts = class="num">30000
    while True:
        symbol_info = mt5.symbol_info(symbol)
        if symbol_info is not None:
            class="kw">break
        else:
            print("Error: Instrument not found. Attempt {} of {}".format(_ + class="num">1, attempts))
            time.sleep(class="num">5)
    while True:
        price_bid = mt5.symbol_info_tick(symbol).bid
        price_ask = mt5.symbol_info_tick(symbol).ask
        signal = model.predict(features)
        positions_total = mt5.positions_total()
        for _ in range(attempts):
            if positions_total < MAX_OPEN_TRADES and signal[-class="num">1] > class="num">0.5:
                request = {
                    "action": mt5.TRADE_ACTION_DEAL,
                    "symbol": symbol,
                    "volume": class="num">0.3,
                    "type": mt5.ORDER_TYPE_BUY,
                    "price": price_ask,
                    "sl": price_ask - class="num">150 * symbol_info.point,
                    "tp": price_ask + class="num">800 * symbol_info.point,
                    "deviation": class="num">20,
                    "magic": class="num">123456,
                    "comment": "Test deal",
                    "type_time": mt5.ORDER_TIME_GTC,
                    "type_filling": mt5.ORDER_FILLING_FOK,
                }
            elif positions_total < MAX_OPEN_TRADES and signal[-class="num">1] < class="num">0.5:
                request = {
                    "action": mt5.TRADE_ACTION_DEAL,
                    "symbol": symbol,
                    "volume": class="num">0.3,
                    "type": mt5.ORDER_TYPE_SELL,
                    "price": price_bid,

◍ 下单请求结构与失败重试的实盘细节

这段逻辑把止损、止盈直接挂在 bid 上加点数:sl 取 price_bid + 150 * point,tp 取 price_bid - 800 * point,意味着默认按空单思路摆盘;点差容差 deviation 设 20,magic 编号 123456 便于后续在 MT5 终端里筛单。 订单填充方式写死 ORDER_FILLING_FOK(全部成交或废单),时效 ORDER_TIME_GTC(挂到手动取消),这种组合在流动性薄的贵金属时段可能直接被拒,需根据品种调成 IOC 或 RETURN。 发单后若 retcode 不是 TRADE_RETCODE_DONE,会 print 错误码并 sleep(3) 做下一次尝试;外层循环间隔 sleep(4000) 毫秒约 4 秒轮一次。外汇与贵金属杠杆高,重发逻辑若不加全局次数锁,遇到券商拒绝可能刷出连单。 process_symbol 里串了取数、增广、标注、GMM 聚 4 类、特征工程选 10 维、再训 XGBoost,generate_new_features 固定 random_seed=1 生成 100 个候选特征,回测可复现但容易过拟合窄样本。

MQL5 / C++
"sl": price_bid + class="num">150 * symbol_info.point,
"tp": price_bid - class="num">800 * symbol_info.point,
"deviation": class="num">20,
"magic": class="num">123456,
"comment": "Test deal",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_FOK,
}
else:
print("No signal to open a position")
class="kw">return None
result = mt5.order_send(request)
if result.retcode == mt5.TRADE_RETCODE_DONE:
if signal[-class="num">1] < class="num">0.5:
print("Buy position opened")
open_trades += class="num">1
elif signal[-class="num">1] > class="num">0.5:
print("Sell position opened")
open_trades += class="num">1
class="kw">return result.order
else:
print("Error: Trade request not executed, retcode={}. Attempt {}/{}".format(result.retcode, _ + class="num">1, attempts))
time.sleep(class="num">3)
time.sleep(class="num">4000)
def process_symbol(symbol):
try:
# Retrieve data for the specified symbol
raw_data = retrieve_data(symbol)
if raw_data is None:
print("No data found for symbol {}".format(symbol))
class="kw">return None
# Augment data
augmented_data = augment_data(raw_data)
# Markup data
marked_data = markup_data(augmented_data.copy(), &class="macro">#x27;close&class="macro">#x27;, &class="macro">#x27;label&class="macro">#x27;)
# Label data
labeled_data = label_data(marked_data, symbol)
# Generate new features
labeled_data_generate = generate_new_features(labeled_data, num_features=class="num">100, random_seed=class="num">1)
# Cluster features by GMM
labeled_data_clustered = cluster_features_by_gmm(labeled_data_generate, n_components=class="num">4)
# Feature engineering
labeled_data_engineered = feature_engineering(labeled_data_clustered, n_features_to_select=class="num">10)
# Train XGBoost classifier
train_data = labeled_data_engineered[labeled_data_engineered.index <= FORWARD]

「让算法在终端里跑起来」

真正进入实盘前,先在 MT5 中调用 process_symbol 并传入具体品种,例如 "XAUUSD",这一步把控制权重交给在线交易逻辑。 在 process_symbol 内部会继续调用 online_trading,把所需参数透传进去,随后进入一个常驻循环:只要不被外部中断,它就会持续监听与处理该品种的信号。外汇与贵金属波动剧烈、杠杆风险极高,常驻逻辑必须能随时手动停掉。 循环尾部固定 sleep 6 秒,目的是给终端喘息,避免高频轮询把 MetaTrader 5 资源吃满。若执行途中触发异常,算法会打印错误并直接中止,而不是带着脏状态继续跑。 这套启动方式没有隐藏收益假设,能否平稳运行只取决于你传入的品种流动性和终端负载,开 MT5 挂个模拟盘跑十分钟就能验证 6 秒节拍是否适合你的机器。

常见问题

用 Python 连接交易终端,把模型预测结果转成标准下单请求结构发出,注意处理返回码与失败重试。
先装好终端的 Python 包并确认端口通信正常,再用脚本读取账户与品种信息做联通测试。
小布可监控品种页的算法信号与挂单状态,异常时提示你检查请求结构或重试逻辑。
先核对品种最小变动价位与手数限制,再打印返回码,针对超时或拒绝做有限次退避重试。
重点看信号延迟、成交滑点及重试频率,外汇贵金属波动大,需常校验模型与实盘一致性。