"""使用全量训练模型预测2026年BTC价格涨跌""" import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import torch import pickle import numpy as np import pandas as pd from data.fetcher import BinanceDataFetcher from models.dlinear.model import DLinear from training.evaluator import ModelEvaluator def main(): print("="*80) print("使用全量训练模型预测2026年BTC涨跌") print("="*80) # ========== 步骤1: 加载模型和预处理器 ========== print("\n[1/6] 加载训练好的模型...") model_path = './models/checkpoints/dlinear_btc_full_data/best_model.pt' preprocessor_path = './models/checkpoints/dlinear_btc_full_data/preprocessor.pkl' # 加载模型 device = torch.device('mps' if torch.backends.mps.is_available() else 'cpu') checkpoint = torch.load(model_path, map_location=device, weights_only=False) model = DLinear(checkpoint['config']) model.load_state_dict(checkpoint['model_state_dict']) model = model.to(device) model.eval() print(f"✓ 模型加载成功 (设备: {device})") # 加载预处理器 with open(preprocessor_path, 'rb') as f: preprocessor = pickle.load(f) print(f"✓ 预处理器加载成功") # ========== 步骤2: 获取2026年数据 ========== print("\n[2/6] 获取2026年BTC数据...") fetcher = BinanceDataFetcher() try: df_2026 = fetcher.fetch_ohlcv( symbol='BTC', timeframe='1h', start_date='2026-01-01', end_date='2026-12-31', use_cache=False # 不使用缓存,获取最新数据 ) print(f"✓ 已获取 {len(df_2026)} 行2026年数据") print(f"✓ 时间范围:{df_2026['timestamp'].min()} 至 {df_2026['timestamp'].max()}") print(f"✓ 价格范围:${df_2026['close'].iloc[0]:,.2f} → ${df_2026['close'].iloc[-1]:,.2f}") total_return = (df_2026['close'].iloc[-1] - df_2026['close'].iloc[0]) / df_2026['close'].iloc[0] print(f"✓ 2026年收益率:{total_return*100:+.2f}%") except Exception as e: print(f"⚠️ 无法获取2026年完整数据:{e}") print(f"尝试获取2025年12月至今的最新数据...") df_2026 = fetcher.fetch_ohlcv( symbol='BTC', timeframe='1h', start_date='2025-12-01', use_cache=False ) print(f"✓ 已获取 {len(df_2026)} 行最新数据") print(f"✓ 时间范围:{df_2026['timestamp'].min()} 至 {df_2026['timestamp'].max()}") # ========== 步骤3: 数据预处理 ========== print("\n[3/6] 预处理2026年数据...") # 添加技术指标和时间特征 df_processed = preprocessor.add_technical_indicators(df_2026) df_processed = preprocessor.add_time_features(df_processed) df_processed = preprocessor.create_labels(df_processed, task='regression') # 删除NaN行 df_processed = df_processed.dropna().reset_index(drop=True) # 确保包含所有训练时的列 # 如果缺少某些列,用0填充 for col in preprocessor.feature_columns: if col not in df_processed.columns: df_processed[col] = 0 print(f" ⚠️ 添加缺失列:{col}") # 使用训练时的scaler标准化特征 scaled_features = preprocessor.transform(df_processed) # 创建序列 X, y = preprocessor.create_sequences( scaled_features, df_processed['label'].values ) print(f"✓ 预测序列数量:{len(X)}") print(f"✓ 序列形状:{X.shape}") if len(X) == 0: print("❌ 没有足够的数据创建序列,需要至少169小时的数据") return # ========== 步骤4: 模型预测 ========== print("\n[4/6] 使用模型预测未来1小时涨跌...") X_tensor = torch.FloatTensor(X).to(device) with torch.no_grad(): predictions = model(X_tensor).squeeze().cpu().numpy() print(f"✓ 预测完成,共 {len(predictions)} 个预测值") print(f"✓ 预测收益率范围:{predictions.min()*100:.4f}% ~ {predictions.max()*100:.4f}%") print(f"✓ 预测收益率均值:{predictions.mean()*100:.4f}%") # ========== 步骤5: 涨跌判断 ========== print("\n[5/6] 根据预测结果判断涨跌...") # 预测方向:正值=上涨,负值=下跌 predicted_direction = (predictions > 0).astype(int) # 1=涨, 0=跌 actual_direction = (y > 0).astype(int) n_predict_up = (predicted_direction == 1).sum() n_predict_down = (predicted_direction == 0).sum() n_actual_up = (actual_direction == 1).sum() n_actual_down = (actual_direction == 0).sum() print(f"\n📊 预测统计:") print(f" 预测上涨:{n_predict_up} ({n_predict_up/len(predictions)*100:.1f}%)") print(f" 预测下跌:{n_predict_down} ({n_predict_down/len(predictions)*100:.1f}%)") print(f"\n📊 实际统计:") print(f" 实际上涨:{n_actual_up} ({n_actual_up/len(y)*100:.1f}%)") print(f" 实际下跌:{n_actual_down} ({n_actual_down/len(y)*100:.1f}%)") # ========== 步骤6: 评估预测准确率 ========== print("\n[6/6] 评估预测准确率...") evaluator = ModelEvaluator(task='regression') metrics = evaluator.evaluate(y, predictions) print("\n" + "="*80) print("2026年预测结果评估") print("="*80) print(f"\n📊 回归指标:") print(f" RMSE (均方根误差): {metrics.get('rmse', 0)*100:.4f}%") print(f" MAE (平均绝对误差): {metrics.get('mae', 0)*100:.4f}%") print(f" MAPE (平均百分比误差): {metrics.get('mape', 0):.2f}%") print(f" 方向准确率: {metrics.get('directional_accuracy', 0)*100:.2f}%") # 混淆矩阵 from sklearn.metrics import confusion_matrix cm = confusion_matrix(actual_direction, predicted_direction) tn, fp, fn, tp = cm.ravel() print(f"\n📈 混淆矩阵(方向预测):") print(f" 真阳性(预测涨且真涨): {tp} ({tp/len(y)*100:.1f}%)") print(f" 真阴性(预测跌且真跌): {tn} ({tn/len(y)*100:.1f}%)") print(f" 假阳性(预测涨但真跌): {fp} ({fp/len(y)*100:.1f}%)") print(f" 假阴性(预测跌但真涨): {fn} ({fn/len(y)*100:.1f}%)") print(f"\n 精确率(预测涨的准确率): {tp/(tp+fp)*100 if (tp+fp)>0 else 0:.2f}%") print(f" 召回率(实际涨被预测为涨): {tp/(tp+fn)*100 if (tp+fn)>0 else 0:.2f}%") # 回测策略 backtest_metrics = evaluator.backtest_strategy( y, predictions, initial_capital=10000.0, transaction_cost=0.001 ) print(f"\n💰 回测结果(2026年):") print(f" 总收益率: {backtest_metrics.get('total_return', 0)*100:.2f}%") print(f" 最终资金: ${backtest_metrics.get('final_capital', 0):,.2f}") print(f" 夏普比率: {backtest_metrics.get('sharpe_ratio', 0):.4f}") max_dd = backtest_metrics.get('max_drawdown', 0) if not np.isnan(max_dd): print(f" 最大回撤: {max_dd*100:.2f}%") print(f" 胜率: {backtest_metrics.get('win_rate', 0)*100:.2f}%") print(f" 交易次数: {backtest_metrics.get('num_trades', 0):.0f}") # Buy & Hold对比 buy_hold_return = (df_processed['close'].iloc[-1] - df_processed['close'].iloc[168]) / df_processed['close'].iloc[168] print(f"\n📊 策略对比:") print(f" 模型策略收益: {backtest_metrics.get('total_return', 0)*100:.2f}%") print(f" Buy & Hold: {buy_hold_return*100:.2f}%") if backtest_metrics.get('total_return', 0) > buy_hold_return: print(f" ✅ 模型策略跑赢Buy & Hold {(backtest_metrics.get('total_return', 0) - buy_hold_return)*100:.2f}%") else: print(f" ❌ 模型策略跑输Buy & Hold {(buy_hold_return - backtest_metrics.get('total_return', 0))*100:.2f}%") # 性能评估 print("\n" + "="*80) print("🎯 性能评估") print("="*80) dir_acc = metrics.get('directional_accuracy', 0) total_return = backtest_metrics.get('total_return', 0) print(f"\n方向预测能力:") if dir_acc > 0.55: print(f" ✅ 方向准确率 {dir_acc*100:.1f}% > 55%,模型有预测能力") elif dir_acc > 0.50: print(f" ⚠️ 方向准确率 {dir_acc*100:.1f}% 略优于随机") else: print(f" ❌ 方向准确率 {dir_acc*100:.1f}% ≤ 50%,预测能力不足") print(f"\n盈利能力:") if total_return > 0.1: print(f" ✅ 回测收益 {total_return*100:.2f}% > 10%,策略表现优秀") elif total_return > 0: print(f" ⚠️ 回测收益 {total_return*100:.2f}% > 0,策略有微弱盈利") else: print(f" ❌ 回测收益 {total_return*100:.2f}% ≤ 0,策略亏损") print(f"\n风险控制:") sharpe = backtest_metrics.get('sharpe_ratio', 0) if sharpe > 1.0: print(f" ✅ 夏普比率 {sharpe:.2f} > 1.0,风险调整后收益优秀") elif sharpe > 0.5: print(f" ⚠️ 夏普比率 {sharpe:.2f},风险调整后收益一般") else: print(f" ❌ 夏普比率 {sharpe:.2f} < 0.5,风险调整后收益较差") # 详细预测样本展示 print("\n" + "="*80) print("📋 部分预测样本展示(前20个)") print("="*80) print(f"\n{'时间':<20} {'预测收益率':<12} {'实际收益率':<12} {'预测方向':<8} {'实际方向':<8} {'准确':<4}") print("-"*80) for i in range(min(20, len(predictions))): pred_val = predictions[i] actual_val = y[i] pred_dir = "上涨" if pred_val > 0 else "下跌" actual_dir = "上涨" if actual_val > 0 else "下跌" correct = "✓" if (pred_val > 0) == (actual_val > 0) else "✗" timestamp = df_processed['timestamp'].iloc[i + 168] # +168因为lookback print(f"{str(timestamp):<20} {pred_val*100:>10.4f}% {actual_val*100:>10.4f}% {pred_dir:<8} {actual_dir:<8} {correct:<4}") print("\n" + "="*80) print("✓ 2026年预测评估完成!") print("="*80) print("\n💡 总结:") print(f" - 在2026年 {len(predictions)} 个小时的预测中") print(f" - 方向准确率达到 {dir_acc*100:.2f}%") print(f" - 策略回测收益 {total_return*100:.2f}%(Buy & Hold: {buy_hold_return*100:.2f}%)") if dir_acc > 0.52 and total_return > 0: print(f"\n ✅ 模型在2026年表现良好,可以考虑实盘小仓位测试") elif dir_acc > 0.50: print(f"\n ⚠️ 模型预测能力有限,建议谨慎使用") else: print(f"\n ❌ 模型在2026年预测失败,不建议实盘使用") if __name__ == "__main__": main()