🎯 项目成果: - 准确率: 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>
241 lines
9.1 KiB
Python
241 lines
9.1 KiB
Python
"""跨资产迁移学习实验 - 用BTC训练,在ETH上验证和测试"""
|
||
|
||
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("跨资产迁移学习实验")
|
||
print("训练:BTC 2020-2025 | 验证+测试:ETH 2020-2025")
|
||
print("="*80)
|
||
|
||
fetcher = BinanceDataFetcher()
|
||
|
||
# ========== 步骤1: 获取BTC训练数据 ==========
|
||
print("\n[1/10] 获取BTC数据(用于训练)...")
|
||
df_btc = fetcher.fetch_ohlcv(
|
||
symbol='BTC',
|
||
timeframe='1h',
|
||
start_date='2020-01-01',
|
||
end_date='2025-12-31',
|
||
use_cache=True
|
||
)
|
||
print(f"✓ BTC数据: {len(df_btc)} 行")
|
||
print(f" 时间: {df_btc['timestamp'].min()} 至 {df_btc['timestamp'].max()}")
|
||
print(f" 价格: ${df_btc['close'].iloc[0]:,.0f} → ${df_btc['close'].iloc[-1]:,.0f}")
|
||
btc_return = (df_btc['close'].iloc[-1] - df_btc['close'].iloc[0]) / df_btc['close'].iloc[0]
|
||
print(f" 收益率: {btc_return*100:+.1f}%")
|
||
|
||
# ========== 步骤2: 获取ETH验证/测试数据 ==========
|
||
print("\n[2/10] 获取ETH数据(用于验证和测试)...")
|
||
df_eth = fetcher.fetch_ohlcv(
|
||
symbol='ETH',
|
||
timeframe='1h',
|
||
start_date='2020-01-01',
|
||
end_date='2025-12-31',
|
||
use_cache=True
|
||
)
|
||
print(f"✓ ETH数据: {len(df_eth)} 行")
|
||
print(f" 时间: {df_eth['timestamp'].min()} 至 {df_eth['timestamp'].max()}")
|
||
print(f" 价格: ${df_eth['close'].iloc[0]:,.2f} → ${df_eth['close'].iloc[-1]:,.2f}")
|
||
eth_return = (df_eth['close'].iloc[-1] - df_eth['close'].iloc[0]) / df_eth['close'].iloc[0]
|
||
print(f" 收益率: {eth_return*100:+.1f}%")
|
||
|
||
# ========== 步骤3: 预处理BTC数据 ==========
|
||
print("\n[3/10] 预处理BTC数据(回归任务)...")
|
||
preprocessor_btc = CryptoPreprocessor(
|
||
lookback=168,
|
||
forecast_horizon=1,
|
||
price_change_threshold=0.0
|
||
)
|
||
X_btc, y_btc, processed_btc = preprocessor_btc.preprocess_pipeline(
|
||
df_btc, task='regression', fit=True
|
||
)
|
||
print(f"✓ BTC序列: X={X_btc.shape}, y={y_btc.shape}")
|
||
|
||
# ========== 步骤4: 预处理ETH数据 ==========
|
||
print("\n[4/10] 预处理ETH数据(使用BTC的scaler)...")
|
||
# 重要:复用BTC的preprocessor(已fit的scaler)来处理ETH数据
|
||
# 添加技术指标和时间特征
|
||
df_eth_processed = preprocessor_btc.add_technical_indicators(df_eth)
|
||
df_eth_processed = preprocessor_btc.add_time_features(df_eth_processed)
|
||
df_eth_processed = preprocessor_btc.create_labels(df_eth_processed, task='regression')
|
||
|
||
# 删除NaN行
|
||
processed_eth = df_eth_processed.dropna().reset_index(drop=True)
|
||
|
||
# 使用BTC训练的scaler来标准化ETH特征(不重新fit)
|
||
scaled_features = preprocessor_btc.transform(processed_eth)
|
||
|
||
# 创建序列
|
||
X_eth, y_eth = preprocessor_btc.create_sequences(
|
||
scaled_features,
|
||
processed_eth['label'].values
|
||
)
|
||
print(f"✓ ETH序列: X={X_eth.shape}, y={y_eth.shape}")
|
||
|
||
# ========== 步骤5: 划分数据集 ==========
|
||
print("\n[5/10] 划分数据集...")
|
||
|
||
# BTC: 只用前70%作为训练集
|
||
n_btc = len(X_btc)
|
||
train_end = int(n_btc * 0.7)
|
||
X_train = X_btc[:train_end]
|
||
y_train = y_btc[:train_end]
|
||
|
||
print(f" 训练集(BTC): {len(X_train)} 样本")
|
||
print(f" 收益率范围: {y_train.min()*100:.4f}% ~ {y_train.max()*100:.4f}%")
|
||
|
||
# ETH: 70%验证,30%测试
|
||
n_eth = len(X_eth)
|
||
val_end = int(n_eth * 0.7)
|
||
X_val = X_eth[:val_end]
|
||
y_val = y_eth[:val_end]
|
||
X_test = X_eth[val_end:]
|
||
y_test = y_eth[val_end:]
|
||
|
||
print(f" 验证集(ETH): {len(X_val)} 样本")
|
||
print(f" 收益率范围: {y_val.min()*100:.4f}% ~ {y_val.max()*100:.4f}%")
|
||
print(f" 测试集(ETH): {len(X_test)} 样本")
|
||
print(f" 收益率范围: {y_test.min()*100:.4f}% ~ {y_test.max()*100:.4f}%")
|
||
|
||
# 分析时间范围
|
||
lookback = 168
|
||
train_df = processed_btc.iloc[lookback:train_end+lookback]
|
||
val_df = processed_eth.iloc[lookback:val_end+lookback]
|
||
test_df = processed_eth.iloc[val_end+lookback:]
|
||
|
||
print(f"\n 时间范围分析:")
|
||
print(f" 训练(BTC): {train_df['timestamp'].min()} 至 {train_df['timestamp'].max()}")
|
||
print(f" 验证(ETH): {val_df['timestamp'].min()} 至 {val_df['timestamp'].max()}")
|
||
print(f" 测试(ETH): {test_df['timestamp'].min()} 至 {test_df['timestamp'].max()}")
|
||
|
||
# ========== 步骤6: 创建数据加载器 ==========
|
||
print("\n[6/10] 创建数据加载器...")
|
||
train_loader, val_loader, test_loader = create_dataloaders(
|
||
(X_train, y_train),
|
||
(X_val, y_val),
|
||
(X_test, y_test),
|
||
task='regression',
|
||
batch_size=64
|
||
)
|
||
|
||
# ========== 步骤7: 创建模型 ==========
|
||
print("\n[7/10] 创建DLinear回归模型...")
|
||
model_config = DLinearConfig(
|
||
input_dim=X_btc.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():,} 个")
|
||
|
||
# ========== 步骤8: 训练模型 ==========
|
||
print("\n[8/10] 训练模型(BTC训练,ETH验证)...")
|
||
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/cross_asset_btc_to_eth'
|
||
)
|
||
|
||
trainer = Trainer(model, train_config, task='regression')
|
||
history = trainer.fit(train_loader, val_loader)
|
||
|
||
# ========== 步骤9: 在ETH测试集上评估 ==========
|
||
print("\n[9/10] 在ETH测试集上评估...")
|
||
metrics_eth = trainer.evaluate(test_loader)
|
||
|
||
results_path = './models/checkpoints/cross_asset_btc_to_eth/test_metrics.json'
|
||
with open(results_path, 'w') as f:
|
||
json.dump(metrics_eth, f, indent=2)
|
||
print(f"✓ 结果保存至: {results_path}")
|
||
|
||
# ========== 步骤10: 对比分析 ==========
|
||
print("\n[10/10] 性能分析...")
|
||
print("\n" + "="*80)
|
||
print("跨资产迁移学习实验结果")
|
||
print("="*80)
|
||
|
||
print("\n📋 实验设置:")
|
||
print(f" 训练集: BTC 2020-2024 ({len(X_train):,} 样本)")
|
||
print(f" 验证集: ETH 2020-2024 ({len(X_val):,} 样本)")
|
||
print(f" 测试集: ETH 2024-2025 ({len(X_test):,} 样本)")
|
||
|
||
print("\n📊 ETH测试集性能:")
|
||
print(f" RMSE: {metrics_eth.get('rmse', 0)*100:.4f}%")
|
||
print(f" MAE: {metrics_eth.get('mae', 0)*100:.4f}%")
|
||
print(f" 方向准确率: {metrics_eth.get('directional_accuracy', 0)*100:.2f}%")
|
||
|
||
print(f"\n💰 ETH回测结果:")
|
||
print(f" 总收益率: {metrics_eth.get('backtest_total_return', 0)*100:.2f}%")
|
||
print(f" 夏普比率: {metrics_eth.get('backtest_sharpe_ratio', 0):.4f}")
|
||
max_dd = metrics_eth.get('backtest_max_drawdown', 0)
|
||
if not np.isnan(max_dd):
|
||
print(f" 最大回撤: {max_dd*100:.2f}%")
|
||
print(f" 胜率: {metrics_eth.get('backtest_win_rate', 0)*100:.2f}%")
|
||
print(f" 交易次数: {metrics_eth.get('backtest_num_trades', 0):.0f}")
|
||
|
||
# 对比之前的结果
|
||
print("\n" + "="*80)
|
||
print("📊 性能对比")
|
||
print("="*80)
|
||
|
||
print("\n实验1:BTC训练 → BTC测试(之前结果)")
|
||
print(" 方向准确率: 50.67%")
|
||
print(" RMSE: 0.4881%")
|
||
print(" 回测收益: -16.71%")
|
||
|
||
print(f"\n实验2:BTC训练 → ETH测试(当前结果)")
|
||
print(f" 方向准确率: {metrics_eth.get('directional_accuracy', 0)*100:.2f}%")
|
||
print(f" RMSE: {metrics_eth.get('rmse', 0)*100:.4f}%")
|
||
print(f" 回测收益: {metrics_eth.get('backtest_total_return', 0)*100:.2f}%")
|
||
|
||
# 性能评估
|
||
print("\n🎯 迁移学习效果评估:")
|
||
|
||
dir_acc = metrics_eth.get('directional_accuracy', 0)
|
||
if dir_acc > 0.52:
|
||
print(f" ✅ 方向准确率 {dir_acc*100:.1f}% > 52%,跨资产迁移有效")
|
||
elif dir_acc > 0.50:
|
||
print(f" ⚠️ 方向准确率 {dir_acc*100:.1f}% 略优于随机")
|
||
else:
|
||
print(f" ❌ 方向准确率 {dir_acc*100:.1f}% ≤ 50%,迁移失败")
|
||
|
||
total_return = metrics_eth.get('backtest_total_return', 0)
|
||
if total_return > 0:
|
||
print(f" ✅ 回测收益 {total_return*100:.2f}% > 0,策略盈利")
|
||
else:
|
||
print(f" ❌ 回测收益 {total_return*100:.2f}% ≤ 0,策略亏损")
|
||
|
||
print("\n💡 结论:")
|
||
if dir_acc > 0.52 and total_return > 0:
|
||
print(" ✅ 跨资产迁移学习成功!BTC学到的模式可应用于ETH")
|
||
elif dir_acc > 0.50:
|
||
print(" ⚠️ 跨资产迁移部分有效,但收益不佳")
|
||
else:
|
||
print(" ❌ 跨资产迁移失败,BTC和ETH市场动态差异太大")
|
||
|
||
print("\n" + "="*80)
|
||
print("✓ 实验完成!")
|
||
print("="*80)
|
||
|
||
if __name__ == "__main__":
|
||
main()
|