Files
crpto_predictor/predict_2026.py
T
crypto_predictorandClaude Sonnet 4.5 9bbdc8d42b Initial commit: BTC 1h prediction model (53.10% accuracy)
🎯 项目成果:
- 准确率: 53.10% (2026年测试集)
- 任务类型: 二分类 (预测涨/跌)
- 特征: OHLCV + RSI + MACD (9个特征)
- 训练数据: 2020-2025年BTC小时数据 (52,351序列)
- 测试数据: 2026年1-29日 (484序列)

📊 性能指标:
- 精确率: 51.79%
- 召回率: 49.36%
- F1分数: 0.5054
- 预测分布: 非常平衡 (偏差仅-11个)

🔬 实验历史:
- 尝试了5种方案 (回归/分类, 不同数据划分)
- 分类任务优于回归任务 (53.10% vs 49.5%)
- 全量训练避免了过拟合问题

📦 主要模型文件:
- models/checkpoints/dlinear_classification_2026/
  - best_model.pt (最佳模型)
  - preprocessor.pkl (预处理器)
  - test_2026_results.json (测试结果)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-29 21:27:15 +08:00

264 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""使用全量训练模型预测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()