量子计算与交易:价格预测的新方法·进阶篇
◍ 回测里怎么算方向命中率
上面这段是模型评估函数的核心循环,用在 EURUSD 的 H1 周期上,默认取 100 根 K 线做样本外推演。它不碰实盘,只把历史切段,用前段训练出的特征去猜后一根收盘价是涨是跌。
循环里每次把 df 截到 iloc[:-(test_periods-i)],相当于不断往前挪窗口;用 up_probability > 0.5 判为多头信号,否则判为空头,再和真实收盘价比较得到 0/1 的 actual_movement。
最后用 sklearn 的 accuracy_score 和 precision_score 收口,这两个值就是你能直接抄进 MT5 外部脚本验证的硬指标。外汇与贵金属属高风险品类,回测命中率高只代表样本内概率倾向,不代表未来实盘必验。
别把 0.5 阈值当铁律:在滑点大的 XAUUSD 上,阈值微调到 0.55 可能让 precision 波动 3~5 个百分点,建议自己跑一遍看分布。
def evaluate_model(symbol="EURUSD", timeframe=mt5.TIMEFRAME_H1, test_periods=class="num">100): """Evaluation of model accuracy""" predictor = MarketPredictor(symbol, timeframe) predictions = [] actual_movements = [] # Get historical data df = predictor.data_loader.get_historical_data(test_periods + class="num">50) for i in range(test_periods): try: temp_df = df.iloc[:-(test_periods-i)] predictor_temp = MarketPredictor(symbol, timeframe) features_temp = predictor_temp.prepare_features(temp_df) # Get data for forecasting latest_features = features_temp.iloc[-predictor_temp.window_size:].values current_price = temp_df[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1] # Make a forecast with the transfer of all necessary parameters prediction = predictor_temp.quantum_predictor.predict( market_data=latest_features, current_price=current_price, features=features_temp.iloc[-predictor_temp.window_size:] ) predicted_movement = class="num">1 if prediction[&class="macro">#x27;up_probability&class="macro">#x27;] > class="num">0.5 else class="num">0 predictions.append(predicted_movement) actual_price_next = df[&class="macro">#x27;close&class="macro">#x27;].iloc[-(test_periods-i)] actual_price_current = df[&class="macro">#x27;close&class="macro">#x27;].iloc[-(test_periods-i)-class="num">1] actual_movement = class="num">1 if actual_price_next > actual_price_current else class="num">0 actual_movements.append(actual_movement) except Exception as e: print(f"Error in evaluation: {e}") class="kw">continue if len(predictions) > class="num">0: metrics = { &class="macro">#x27;accuracy&class="macro">#x27;: accuracy_score(actual_movements, predictions), &class="macro">#x27;precision&class="macro">#x27;: precision_score(actual_movements, predictions),
「把预测模型接到 MT5 实盘回测」
这段 Python 脚本演示了如何把训练好的方向预测模型直接挂到 MT5 终端做验证。核心动作就两个:先用 evaluate_model 跑 100 根 H1 K 线算分类指标,再实时拉最新窗口算下一根涨跌概率。 若历史样本不足以计算,代码会兜底返回 accuracy / precision / recall / f1 全为 0 的字典,避免脚本在样本为空时抛异常。实际跑下来,EURUSD H1 用 100 周期测试,四个指标打印出来都是带两位小数的百分比,方便你直观比对模型是不是在瞎猜。 主程序里 mt5.initialize() 失败会直接 shutdown 并退出,成功才进 try 块。注意 predictor.predict() 已经修好了参数传递,能正确吃进最新特征;输出里 predicted_price 保留 5 位小数,up_probability 和 down_probability 相加不一定严格为 1,confidence 是模型自己的置信度字段,外汇品种波动大,这类概率仅作参考,实盘仍属高风险。 想复现的话,开 MT5 连上终端,把 symbol 改成你盯的贵金属或交叉盘,test_periods 调到 200 以上,看 recall 是否随样本拉长而塌方,比盯着准确率更有用。
&class="macro">#x27;recall&class="macro">#x27;: recall_score(actual_movements, predictions), &class="macro">#x27;f1&class="macro">#x27;: f1_score(actual_movements, predictions) } else: metrics = { &class="macro">#x27;accuracy&class="macro">#x27;: class="num">0, &class="macro">#x27;precision&class="macro">#x27;: class="num">0, &class="macro">#x27;recall&class="macro">#x27;: class="num">0, &class="macro">#x27;f1&class="macro">#x27;: class="num">0 } class="kw">return metrics if __name__ == "__main__": if not mt5.initialize(): print("MetaTrader5 initialization failed") mt5.shutdown() else: try: symbol = "EURUSD" timeframe = mt5.TIMEFRAME_H1 print("\nTest the model...") metrics = evaluate_model(symbol, timeframe, test_periods=class="num">100) print("\nModel quality metrics:") print(f"Accuracy: {metrics[&class="macro">#x27;accuracy&class="macro">#x27;]:.class="num">2%}") print(f"Precision: {metrics[&class="macro">#x27;precision&class="macro">#x27;]:.class="num">2%}") print(f"Recall: {metrics[&class="macro">#x27;recall&class="macro">#x27;]:.class="num">2%}") print(f"F1-score: {metrics[&class="macro">#x27;f1&class="macro">#x27;]:.class="num">2%}") print("\nCurrent forecast:") predictor = MarketPredictor(symbol, timeframe) df = predictor.data_loader.get_historical_data(predictor.window_size + class="num">50) features = predictor.prepare_features(df) latest_features = features.iloc[-predictor.window_size:].values current_price = df[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1] prediction = predictor.predict() # Now this method passes all parameters correctly print(f"Predicted price: {prediction[&class="macro">#x27;predicted_price&class="macro">#x27;]:.5f}") print(f"Growth probability: {prediction[&class="macro">#x27;up_probability&class="macro">#x27;]:.class="num">2%}") print(f"Fall probability: {prediction[&class="macro">#x27;down_probability&class="macro">#x27;]:.class="num">2%}") print(f"Forecast confidence: {prediction[&class="macro">#x27;confidence&class="macro">#x27;]:.class="num">2%}")
把预测字典打进终端
这段 Python 片段承接前面的 MT5 连接与指标计算,把模型返回的 prediction 字典逐行打印出来,方便在终端直接核对数值。current_price 保留 5 位小数,符合外汇小数点后 4~5 位的报价习惯;rsi 取 2 位,sma 与 ema 同样 5 位,和行情窗口里的浮点精度对齐。 无论预测成功与否,finally 块里的 mt5.shutdown() 都会执行,避免 Python 进程退出后 MT5 终端还挂着无效连接。实盘或回测脚本里漏掉这一步,可能让 MT5 后台残留多个会话,拖慢终端响应。 开 MT5 跑通前面那套逻辑后,把这几行粘到脚本末尾,就能在控制台看到类似 Current price: 1.08452 / RSI: 32.17 的输出,据此判断信号字典是否真的拿到了实时字段。外汇与贵金属杠杆高,终端打印仅作辅助核对,不代表任何方向确定性。
print(f"Current price: {prediction[&class="macro">#x27;current_price&class="macro">#x27;]:.5f}") print(f"RSI: {prediction[&class="macro">#x27;rsi&class="macro">#x27;]:.2f}") print(f"SMA: {prediction[&class="macro">#x27;sma&class="macro">#x27;]:.5f}") print(f"EMA: {prediction[&class="macro">#x27;ema&class="macro">#x27;]:.5f}") finally: mt5.shutdown()
◍ 保守信号系统的绩效与当前盘口读数
在 EURUSD 高波动样本里,这套系统的分类准确率为 55.00%,仅略高于随机基准,但精确率达到 63.64%——也就是说它发出的信号里近三分之二事后被验证是对的。召回率只有 14.58%,说明它极度挑剔,只在置信度足够时才出手,宁可放过也不乱发。 F1 分数 23.73% 是精确率与召回率折中的结果,数值偏低但符合其保守定位:用少而准替代多而杂。对实盘而言,这种选择性比高覆盖更有用,因为外汇和贵金属杠杆高,错信号的成本远大于沉默的成本。 当前盘口:EURUSD 现价 1.02903,模型给的预测价 1.02905,上涨概率 38.95%、下跌概率 61.05%,但置信度标到 99.98%。下跌概率占优却仍判适度上涨,倾向解读为下行趋势里的短线修正,而非反转。 技术侧 RSI 49.13 贴近中性,SMA 1.02904、EMA 1.02909,价格夹在两条均线附近,趋势读数为中性。把概率分布和技术指标放一起看,系统给出的是「低频率、高把握、重修正」的参考,而不是方向宣言。
「把行情先归一化再喂给量子比特」
量子计算和机器学习搭在一起,确实给行情建模多开了一扇窗。我们在 MT5 外接 Python 的实验中,先用 sklearn 的 MinMaxScaler 把市场价格序列压到 [0,1],再把这个归一化结果编码成量子线路里的旋转角度。 这一步归一化不能省:量子门对输入幅值极度敏感,未标准化的价差直接进线路会让测量概率失真,回测里信号会大面积漂移。 系统把机器学习的四项指标——准确率、精确率、召回率、F1——和量子测量输出绑在一起。实测准确率落在 60% 左右,能抓到一些经典均线或动量模型漏掉的结构,但下单频次很低,风格像一个很挑入场点的老交易员。外汇与贵金属杠杆高,这种低胜率高频次的试探本身就有爆仓风险。 后续可以接自动编码器或 transformer 做预处理,让量子线路当经典模型的过滤器,或者反过来。二者协同不是噱头,是下一步算法交易值得在 MT5 + Python 环境里自己跑一遍的方向。
第一个量子分类器的实测翻车点
把量子电路和经典机器学习拼在一起做混合分类器,思路上并不新鲜:用8量子比特的 ry 门把每段价格历史转成旋转角,再用 cx 门纠缠,试图让模型吃到非线性关系。同时叠了一组二元指标——方向、动量、成交量、MA 收敛、波动率、RSI、布林带,每个都出0/1信号再和量子特征合并。 真跑起来并不好看。欧元兑美元 H1 上整体准确率只有 42.13%,比随机扔硬币还低;精确率 39.71%、召回率 27%、F1 仅 32.14%,说明预测错得相当离谱。外汇和贵金属这类高杠杆品种,这种弱模型直接上实盘风险极高,先当逆向避坑样本看。 特征贡献拆开更刺眼:量子特征只占 27.63%,二元特征贡献率却算出 121.95%(超出100%意味着共线或权重抵消,也暴露出融合方式有问题)。要么量子编码没抓到有效模式,要么特征拼接层本身就在互相打架。 代码里 MT5DataLoader 负责拉 H1 的 EURUSD 千根 bar,BinaryPatternGenerator 用 shift、rolling 直接算方向/动量/成交量/快慢均线收敛的二元信号——开 MT5 把这段 py 跑通,你就能复现那组 42% 的惨淡基线。下一步值得试的是砍掉 cx 纠缠、换编码门,或者干脆把量子部分权重压到更低再看贡献率漂移。
class="kw">import numpy as np class="kw">import MetaTrader5 as mt5 class="kw">import pandas as pd from class="type">class="kw">datetime class="kw">import class="type">class="kw">datetime, timedelta from qiskit class="kw">import QuantumCircuit, transpile, QuantumRegister, ClassicalRegister from qiskit_aer class="kw">import AerSimulator from sklearn.preprocessing class="kw">import MinMaxScaler, StandardScaler from sklearn.metrics class="kw">import accuracy_score, precision_score, recall_score, f1_score from sklearn.model_selection class="kw">import train_test_split from catboost class="kw">import CatBoostClassifier class="kw">import warnings warnings.filterwarnings(&class="macro">#x27;ignore&class="macro">#x27;) class MT5DataLoader: def __init__(self, symbol="EURUSD", timeframe=mt5.TIMEFRAME_H1): if not mt5.initialize(): raise Exception("MetaTrader5 initialization failed") self.symbol = symbol self.timeframe = timeframe def get_historical_data(self, lookback_bars=class="num">1000): current_time = class="type">class="kw">datetime.now() rates = mt5.copy_rates_from(self.symbol, self.timeframe, current_time, lookback_bars) if rates is None: raise Exception(f"Failed to get data for {self.symbol}") df = pd.DataFrame(rates) df[&class="macro">#x27;time&class="macro">#x27;] = pd.to_datetime(df[&class="macro">#x27;time&class="macro">#x27;], unit=&class="macro">#x27;s&class="macro">#x27;) class="kw">return df class BinaryPatternGenerator: def __init__(self, df, lookback=class="num">10): self.df = df self.lookback = lookback def direction_encoding(self): class="kw">return (self.df[&class="macro">#x27;close&class="macro">#x27;] > self.df[&class="macro">#x27;close&class="macro">#x27;].shift(class="num">1)).astype(class="type">int) def momentum_encoding(self, threshold=class="num">0.0001): returns = self.df[&class="macro">#x27;close&class="macro">#x27;].pct_change() class="kw">return (returns.abs() > threshold).astype(class="type">int) def volume_encoding(self): class="kw">return (self.df[&class="macro">#x27;tick_volume&class="macro">#x27;] > self.df[&class="macro">#x27;tick_volume&class="macro">#x27;].rolling(self.lookback).mean()).astype(class="type">int) def convergence_encoding(self): ma_fast = self.df[&class="macro">#x27;close&class="macro">#x27;].rolling(class="num">5).mean() ma_slow = self.df[&class="macro">#x27;close&class="macro">#x27;].rolling(class="num">20).mean() class="kw">return (ma_fast > ma_slow).astype(class="type">int)