量子计算与交易:价格预测的新方法·进阶篇
📘

量子计算与交易:价格预测的新方法·进阶篇

第 2/3 篇

◍ 回测里怎么算方向命中率

上面这段是模型评估函数的核心循环,用在 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 个百分点,建议自己跑一遍看分布。

MQL5 / C++
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 是否随样本拉长而塌方,比盯着准确率更有用。

MQL5 / C++
    &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 的输出,据此判断信号字典是否真的拿到了实时字段。外汇与贵金属杠杆高,终端打印仅作辅助核对,不代表任何方向确定性。

MQL5 / C++
    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 纠缠、换编码门,或者干脆把量子部分权重压到更低再看贡献率漂移。

MQL5 / C++
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)

常见问题

用样本外数据逐根K线比对预测方向与实际收盘方向,只统计信号触发后的N根内胜负,别把训练集命中当绩效。
先确认行情已归一化再喂入,且回测滑点按实盘品种设置;否则量子分类器在实测里容易大幅偏离纸面结果。
小布可把你的预测字典接入对应品种页,自动标注信号与实时盘口读数,省去手动比对回测与实盘的重复劳动。
常见翻车点是未对行情做归一化直接喂比特,导致边界失真;保守信号系统虽降频但能明显拉回胜率。
当前读数显示信号频率低、方向倾向弱,属高风险环境,仅可作小仓位概率参考,勿当确定性结论。