🎯 项目成果: - 准确率: 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>
234 lines
9.7 KiB
Python
234 lines
9.7 KiB
Python
"""方案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()
|