Files
crpto_predictor/train_full_data.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

234 lines
9.7 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.
"""方案A:使用2020-2025年BTC全部数据训练 - 准备实盘使用"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from data.fetcher import BinanceDataFetcher
from data.preprocessor import CryptoPreprocessor, split_data
from data.dataset import create_dataloaders
from models.dlinear.model import DLinear, DLinearConfig
from training.trainer import Trainer
from training.config import TrainingConfig
import json
import numpy as np
def main():
print("="*80)
print("方案A:BTC全量数据训练 (2020-2025)")
print("训练集:95% | 验证集:5%(仅用于early stopping)| 测试集:无")
print("="*80)
# ========== 步骤1: 获取全部BTC数据 ==========
print("\n[1/7] 获取BTC 2020-2025年全部数据...")
fetcher = BinanceDataFetcher()
df = fetcher.fetch_ohlcv(
symbol='BTC',
timeframe='1h',
start_date='2020-01-01',
end_date='2025-12-31',
use_cache=True
)
print(f"✓ 已获取 {len(df):,} 行数据(约{len(df)/8760:.1f}年)")
print(f"✓ 时间范围:{df['timestamp'].min()} 至 {df['timestamp'].max()}")
print(f"✓ 价格范围:${df['close'].iloc[0]:,.2f} → ${df['close'].iloc[-1]:,.2f}")
total_return = (df['close'].iloc[-1] - df['close'].iloc[0]) / df['close'].iloc[0]
print(f"✓ 总收益率:{total_return*100:+.2f}%")
# 市场周期分析
df['year'] = df['timestamp'].dt.year
print(f"\n✓ 市场周期覆盖:")
for year in sorted(df['year'].unique()):
year_df = df[df['year'] == year]
if len(year_df) > 100:
year_return = (year_df['close'].iloc[-1] - year_df['close'].iloc[0]) / year_df['close'].iloc[0]
trend = "🚀 牛市" if year_return > 0.3 else ("🔴 熊市" if year_return < -0.3 else "📊 震荡")
print(f" {year}年: {year_return*100:+7.2f}% {trend}")
# ========== 步骤2: 数据预处理 ==========
print("\n[2/7] 数据预处理(回归任务)...")
preprocessor = CryptoPreprocessor(
lookback=168, # 输入:过去7天
forecast_horizon=1, # 预测:未来1小时
price_change_threshold=0.0
)
X, y, processed_df = preprocessor.preprocess_pipeline(df, task='regression', fit=True)
print(f"✓ 序列数量:{len(X):,}")
print(f"✓ 序列形状:{X.shape}")
print(f"✓ 特征数量:{X.shape[-1]}")
print(f"\n✓ 标签统计(收益率):")
print(f" 最小值: {y.min()*100:.4f}%")
print(f" 最大值: {y.max()*100:.4f}%")
print(f" 均值: {y.mean()*100:.4f}%")
print(f" 标准差: {y.std()*100:.4f}%")
print(f" 中位数: {np.median(y)*100:.4f}%")
n_positive = (y > 0).sum()
n_negative = (y <= 0).sum()
print(f"\n✓ 方向分布:")
print(f" 上涨: {n_positive:,} ({n_positive/len(y)*100:.1f}%)")
print(f" 下跌: {n_negative:,} ({n_negative/len(y)*100:.1f}%)")
# ========== 步骤3: 划分数据集(95% 训练 + 5% 验证)==========
print("\n[3/7] 划分数据集(95%训练 + 5%验证)...")
print("⚠️ 注意:此次训练使用几乎全部数据,无独立测试集")
print(" 验证集仅用于early stopping防止过拟合")
n = len(X)
train_end = int(n * 0.95)
X_train = X[:train_end]
y_train = y[:train_end]
X_val = X[train_end:]
y_val = y[train_end:]
print(f"\n 训练集:{len(X_train):,} 样本 ({len(X_train)/n*100:.1f}%)")
print(f" 收益率范围: {y_train.min()*100:.4f}% ~ {y_train.max()*100:.4f}%")
print(f" 验证集:{len(X_val):,} 样本 ({len(X_val)/n*100:.1f}%)")
print(f" 收益率范围: {y_val.min()*100:.4f}% ~ {y_val.max()*100:.4f}%")
# 时间范围分析
lookback = 168
train_df = processed_df.iloc[lookback:train_end+lookback]
val_df = processed_df.iloc[train_end+lookback:]
print(f"\n 时间范围:")
print(f" 训练集: {train_df['timestamp'].min()} 至 {train_df['timestamp'].max()}")
train_return = (train_df['close'].iloc[-1] - train_df['close'].iloc[0]) / train_df['close'].iloc[0]
print(f" ${train_df['close'].iloc[0]:,.0f} → ${train_df['close'].iloc[-1]:,.0f} ({train_return*100:+.1f}%)")
print(f" 验证集: {val_df['timestamp'].min()} 至 {val_df['timestamp'].max()}")
val_return = (val_df['close'].iloc[-1] - val_df['close'].iloc[0]) / val_df['close'].iloc[0]
print(f" ${val_df['close'].iloc[0]:,.0f} → ${val_df['close'].iloc[-1]:,.0f} ({val_return*100:+.1f}%)")
# ========== 步骤4: 创建数据加载器 ==========
print("\n[4/7] 创建数据加载器...")
train_loader, val_loader, _ = create_dataloaders(
(X_train, y_train),
(X_val, y_val),
(X_val, y_val), # 测试集用验证集替代(不使用)
task='regression',
batch_size=64
)
# ========== 步骤5: 创建模型 ==========
print("\n[5/7] 创建DLinear回归模型...")
model_config = DLinearConfig(
input_dim=X.shape[-1],
lookback=168,
task='regression',
hidden_dim=512,
dropout=0.1,
moving_avg_kernel=25
)
model = DLinear(model_config)
print(f"✓ 模型参数:{model.count_parameters():,} 个")
print(f"✓ 任务类型:回归(预测未来1小时收益率)")
print(f"✓ 损失函数:MSELoss")
# ========== 步骤6: 训练模型 ==========
print("\n[6/7] 开始全量数据训练...")
print(f"✓ 训练样本:{len(X_train):,} (使用了{len(X_train)/len(X)*100:.1f}%的数据)")
print(f"✓ 对比之前:之前训练集36,645样本,现在{len(X_train):,}样本(+{(len(X_train)-36645)/36645*100:.1f}%)")
train_config = TrainingConfig(
batch_size=64,
max_epochs=50,
learning_rate=0.001,
early_stopping_patience=15, # 增加patience,允许更充分训练
lr_scheduler='plateau',
save_best=True,
checkpoint_dir='./models/checkpoints/dlinear_btc_full_data'
)
trainer = Trainer(model, train_config, task='regression')
history = trainer.fit(train_loader, val_loader)
# ========== 步骤7: 保存最终模型 ==========
print("\n[7/7] 保存最终模型...")
# 在验证集上快速评估
print("\n在验证集上进行sanity check...")
val_metrics = trainer.evaluate(val_loader)
results_path = './models/checkpoints/dlinear_btc_full_data/final_metrics.json'
with open(results_path, 'w') as f:
json.dump(val_metrics, f, indent=2)
print(f"✓ 结果保存至:{results_path}")
# 同时保存预处理器(实盘使用时需要)
import pickle
preprocessor_path = './models/checkpoints/dlinear_btc_full_data/preprocessor.pkl'
with open(preprocessor_path, 'wb') as f:
pickle.dump(preprocessor, f)
print(f"✓ 预处理器保存至:{preprocessor_path}")
# ========== 最终报告 ==========
print("\n" + "="*80)
print("全量数据训练完成 - 最终报告")
print("="*80)
print(f"\n📊 训练概况:")
print(f" 数据时间范围:2020-01-01 至 2025-12-31 (6年)")
print(f" 训练样本数量:{len(X_train):,} (95%)")
print(f" 验证样本数量:{len(X_val):,} (5%)")
print(f" 特征数量:{X.shape[-1]}")
print(f" 模型参数:{model.count_parameters():,}")
print(f"\n📈 验证集性能(仅供参考,非独立测试):")
print(f" RMSE: {val_metrics.get('rmse', 0)*100:.4f}%")
print(f" MAE: {val_metrics.get('mae', 0)*100:.4f}%")
print(f" 方向准确率: {val_metrics.get('directional_accuracy', 0)*100:.2f}%")
print(f"\n💰 验证集回测(仅供参考):")
print(f" 总收益率: {val_metrics.get('backtest_total_return', 0)*100:.2f}%")
print(f" 夏普比率: {val_metrics.get('backtest_sharpe_ratio', 0):.4f}")
max_dd = val_metrics.get('backtest_max_drawdown', 0)
if not np.isnan(max_dd):
print(f" 最大回撤: {max_dd*100:.2f}%")
print(f" 胜率: {val_metrics.get('backtest_win_rate', 0)*100:.2f}%")
print("\n" + "="*80)
print("🎯 全量训练 vs 之前训练对比")
print("="*80)
print("\n之前训练(70/15/15分割):")
print(" 训练样本: 36,645")
print(" 时间范围: 2020-01 至 2024-03")
print(" 测试集方向准确率: 50.67%")
print(" 测试集回测收益: -16.71%")
print(f"\n当前全量训练(95/5分割):")
print(f" 训练样本: {len(X_train):,} (+{(len(X_train)-36645)/36645*100:.1f}%)")
print(f" 时间范围: 2020-01 至 {train_df['timestamp'].max().strftime('%Y-%m')}")
print(f" 验证集方向准确率: {val_metrics.get('directional_accuracy', 0)*100:.2f}%")
print(f" 验证集回测收益: {val_metrics.get('backtest_total_return', 0)*100:.2f}%")
print("\n" + "="*80)
print("⚠️ 重要说明")
print("="*80)
print("\n1. 此模型使用了95%的历史数据训练")
print("2. 验证集仅占5%,性能指标仅供参考,不代表真实泛化能力")
print("3. 模型已针对2020-2025年市场环境充分优化")
print("4. 适用场景:")
print(" ✅ 用于2026年未来数据的预测")
print(" ✅ 用于实盘交易(假设未来市场特征与历史相似)")
print(" ❌ 不适合评估在未见过数据上的泛化性能")
print("\n💡 下一步建议:")
print(" 1. 使用此模型预测2026年1月的BTC价格")
print(" 2. 在实盘中小仓位测试策略")
print(" 3. 定期(每月/季度)用新数据重新训练模型")
print(" 4. 监控实盘性能,如果偏差大说明市场环境变化")
print("\n" + "="*80)
print("✓ 训练完成!模型已保存到 ./models/checkpoints/dlinear_btc_full_data/")
print("="*80)
if __name__ == "__main__":
main()