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>
This commit is contained in:
crypto_predictorandClaude Sonnet 4.5 committed 2026-01-29 21:27:15 +08:00
commit 9bbdc8d42b
82 files changed
+8742

No files matched your search

+263
View File
@@ -0,0 +1,263 @@
"""使用全量训练模型预测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()