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

243 lines
10 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.
"""使用2020-2025年数据训练DLinear模型 - 包含完整牛熊周期"""
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("DLinear模型训练 - 使用2020-2025年完整数据(5年,包含牛熊周期)")
print("="*80)
# 步骤1: 获取2020-2025年完整数据
print("\n[1/8] 从Binance获取BTC 2020-2025年完整数据...")
fetcher = BinanceDataFetcher()
# 获取5年数据
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"\n✓ 价格变化分析:")
print(f" 起始价格: ${df['close'].iloc[0]:,.2f} (2020年)")
print(f" 结束价格: ${df['close'].iloc[-1]:,.2f} (2025年)")
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: 数据预处理(预测未来1小时)
print("\n[2/8] 数据预处理(预测未来1小时的涨跌)...")
preprocessor = CryptoPreprocessor(
lookback=168, # 输入:过去168小时(7天)
forecast_horizon=1, # 预测:未来1小时
price_change_threshold=0.0
)
X, y, processed_df = preprocessor.preprocess_pipeline(df, task='classification', fit=True)
print(f"✓ 已创建序列:X形状 = {X.shape}, y形状 = {y.shape}")
print(f"✓ 特征数量:{X.shape[-1]}")
# 计算类别分布
n_down = (y == 0).sum()
n_up = (y == 1).sum()
print(f"✓ 标签分布:下跌(0) = {n_down} ({n_down/len(y)*100:.1f}%), 上涨(1) = {n_up} ({n_up/len(y)*100:.1f}%)")
# 计算类别权重
pos_weight = n_down / n_up if n_up > 0 else 1.0
print(f"✓ 计算类别权重:pos_weight = {pos_weight:.2f}")
# 步骤3: 划分数据集(时间序列分割)
print("\n[3/8] 划分训练/验证/测试集 (70/15/15)...")
train_data, val_data, test_data = split_data(X, y, train_split=0.7, val_split=0.15)
print(f" 训练集:{len(train_data[0])} 样本")
print(f" 下跌: {(train_data[1]==0).sum()}, 上涨: {(train_data[1]==1).sum()}")
print(f" 验证集:{len(val_data[0])} 样本")
print(f" 下跌: {(val_data[1]==0).sum()}, 上涨: {(val_data[1]==1).sum()}")
print(f" 测试集:{len(test_data[0])} 样本")
print(f" 下跌: {(test_data[1]==0).sum()}, 上涨: {(test_data[1]==1).sum()}")
# 分析各数据集对应的时间范围
lookback = 168
train_end_idx = int(len(processed_df) * 0.7) - lookback
val_end_idx = int(len(processed_df) * 0.85) - lookback
train_df = processed_df.iloc[lookback:train_end_idx+lookback]
val_df = processed_df.iloc[train_end_idx+lookback:val_end_idx+lookback]
test_df = processed_df.iloc[val_end_idx+lookback:]
print(f"\n 训练集时间范围: {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}%)")
print(f" 测试集时间范围: {test_df['timestamp'].min()} 至 {test_df['timestamp'].max()}")
test_return = (test_df['close'].iloc[-1] - test_df['close'].iloc[0]) / test_df['close'].iloc[0]
print(f" 价格: ${test_df['close'].iloc[0]:,.0f} → ${test_df['close'].iloc[-1]:,.0f} ({test_return*100:+.1f}%)")
# 步骤4: 创建数据加载器
print("\n[4/8] 创建PyTorch数据加载器...")
train_loader, val_loader, test_loader = create_dataloaders(
train_data, val_data, test_data,
task='classification',
batch_size=64
)
# 步骤5: 创建模型
print("\n[5/8] 创建DLinear模型...")
model_config = DLinearConfig(
input_dim=X.shape[-1],
lookback=168,
task='classification',
hidden_dim=512,
dropout=0.1,
moving_avg_kernel=25
)
model = DLinear(model_config)
print(f"✓ 模型已创建,可训练参数:{model.count_parameters():,} 个")
# 步骤6: 训练模型
print("\n[6/8] 开始训练模型(数据量大,预计需要更长时间)...")
train_config = TrainingConfig(
batch_size=64,
max_epochs=50,
learning_rate=0.001,
early_stopping_patience=10,
lr_scheduler='plateau',
save_best=True,
checkpoint_dir='./models/checkpoints/dlinear_btc_2020_2025'
)
trainer = Trainer(
model,
train_config,
task='classification',
pos_weight=pos_weight
)
print(f"✓ 训练数据规模:{len(train_data[0]):,} 样本(是之前的 {len(train_data[0])/5990:.1f}x)")
history = trainer.fit(train_loader, val_loader)
# 步骤7: 评估模型
print("\n[7/8] 在测试集上评估模型...")
metrics = trainer.evaluate(test_loader)
# 保存评估结果
results_path = './models/checkpoints/dlinear_btc_2020_2025/test_metrics.json'
with open(results_path, 'w') as f:
json.dump(metrics, f, indent=2)
print(f"\n✓ 评估结果已保存至:{results_path}")
# 步骤8: 对比分析
print("\n[8/8] 性能对比分析...")
print("\n" + "="*80)
print("训练完成 - 最终测试结果(使用2020-2025年数据)")
print("="*80)
print(f"\n📊 分类指标:")
print(f" 准确率: {metrics.get('accuracy', 0):.4f} ({metrics.get('accuracy', 0)*100:.2f}%)")
print(f" F1分数: {metrics.get('f1', 0):.4f}")
print(f" 精确率: {metrics.get('precision', 0):.4f}")
print(f" 召回率: {metrics.get('recall', 0):.4f}")
if 'true_positives' in metrics:
tp = metrics['true_positives']
tn = metrics['true_negatives']
fp = metrics['false_positives']
fn = metrics['false_negatives']
print(f"\n📈 混淆矩阵:")
print(f" 真阳性(预测涨且真涨): {tp}")
print(f" 真阴性(预测跌且真跌): {tn}")
print(f" 假阳性(预测涨但真跌): {fp}")
print(f" 假阴性(预测跌但真涨): {fn}")
total = tp + tn + fp + fn
if total > 0:
print(f"\n 预测分布:")
print(f" 预测上涨: {tp + fp} ({(tp+fp)/total*100:.1f}%)")
print(f" 预测下跌: {tn + fn} ({(tn+fn)/total*100:.1f}%)")
print(f"\n💰 回测结果:")
print(f" 总收益率: {metrics.get('backtest_total_return', 0)*100:.2f}%")
print(f" 最终资金: ${metrics.get('backtest_final_capital', 0):,.2f}")
print(f" 夏普比率: {metrics.get('backtest_sharpe_ratio', 0):.4f}")
max_dd = metrics.get('backtest_max_drawdown', 0)
if not np.isnan(max_dd):
print(f" 最大回撤: {max_dd*100:.2f}%")
else:
print(f" 最大回撤: N/A")
print(f" 胜率: {metrics.get('backtest_win_rate', 0)*100:.2f}%")
print(f" 交易次数: {metrics.get('backtest_num_trades', 0)}")
# 性能评估
print(f"\n🎯 性能评估:")
acc = metrics.get('accuracy', 0)
if acc > 0.55:
print(f" ✅ 准确率 {acc*100:.1f}% > 55%,模型有一定预测能力")
elif acc > 0.50:
print(f" ⚠️ 准确率 {acc*100:.1f}% 略优于随机(50%),预测能力较弱")
else:
print(f" ❌ 准确率 {acc*100:.1f}% ≤ 50%,模型预测能力不足")
total_return = metrics.get('backtest_total_return', 0)
if total_return > 0.05:
print(f" ✅ 回测收益 {total_return*100:.2f}% > 5%,策略有较好盈利潜力")
elif total_return > 0:
print(f" ⚠️ 回测收益 {total_return*100:.2f}% > 0,策略有微弱盈利")
else:
print(f" ❌ 回测收益 {total_return*100:.2f}% ≤ 0,策略亏损")
# 对比之前的结果
print(f"\n📊 与之前结果对比:")
print(f" 使用1年数据(2025年):")
print(f" - 训练集: 2025.02-10 (牛市+16%)")
print(f" - 测试集: 2025.12-2026.01 (熊市-2%)")
print(f" - 准确率: 50.7%")
print(f" - 预测上涨: 65.8% (偏向上涨)")
print(f" - 问题: 训练测试分布不一致")
print(f"\n 使用5年数据(2020-2025年):")
print(f" - 训练集: 包含牛市(2020-2021)和熊市(2022)")
print(f" - 测试集: 2024-2025数据")
print(f" - 准确率: {acc*100:.1f}%")
if total > 0:
print(f" - 预测上涨: {(tp+fp)/total*100:.1f}%")
print(f" - 改进: 见过完整市场周期,泛化更好")
print("\n" + "="*80)
print("✓ 训练完成!")
print("="*80)
if __name__ == "__main__":
main()