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

203 lines
7.5 KiB
Python

"""2026年价格预测脚本:直接预测价格并判断涨跌方向"""
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
import pandas_ta as ta
from sklearn.preprocessing import StandardScaler
from data.fetcher import BinanceDataFetcher
from models.dlinear.model import DLinear
class PricePreprocessor:
"""预测未来价格的预处理器"""
def __init__(self, lookback=168, forecast_horizon=1):
self.lookback = lookback
self.forecast_horizon = forecast_horizon
self.scaler = StandardScaler()
self.price_scaler = StandardScaler()
self.is_fitted = False
def add_features(self, df):
df = df.copy()
df['rsi'] = ta.rsi(df['close'], length=14)
macd = ta.macd(df['close'])
if macd is not None and not macd.empty:
df['macd'] = macd.iloc[:, 0]
df['macd_hist'] = macd.iloc[:, 1]
df['macd_signal'] = macd.iloc[:, 2]
return df
def create_labels(self, df):
df = df.copy()
df['future_price'] = df['close'].shift(-self.forecast_horizon)
df['label'] = df['future_price']
return df[:-self.forecast_horizon]
def preprocess(self, df, fit=True):
df = self.add_features(df)
df = self.create_labels(df)
df = df.dropna().reset_index(drop=True)
feature_cols = ['open', 'high', 'low', 'close', 'volume',
'rsi', 'macd', 'macd_hist', 'macd_signal']
if fit:
scaled_features = self.scaler.fit_transform(df[feature_cols])
scaled_labels = self.price_scaler.fit_transform(df[['label']])
self.is_fitted = True
self.feature_columns = feature_cols
else:
if not self.is_fitted:
raise ValueError("Must fit before transform")
scaled_features = self.scaler.transform(df[feature_cols])
scaled_labels = self.price_scaler.transform(df[['label']])
X, y = [], []
for i in range(len(scaled_features) - self.lookback):
X.append(scaled_features[i:i + self.lookback])
y.append(scaled_labels[i + self.lookback, 0])
return np.array(X), np.array(y), df
def inverse_transform_price(self, scaled_price):
return self.price_scaler.inverse_transform(scaled_price.reshape(-1, 1)).flatten()
def main():
print("="*80)
print("2026年BTC价格预测 - 直接预测未来价格")
print("="*80)
# ========== 加载模型 ==========
print("\n[1/4] 加载模型...")
model_path = './models/checkpoints/dlinear_predict_price/best_model.pt'
preprocessor_path = './models/checkpoints/dlinear_predict_price/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()
with open(preprocessor_path, 'rb') as f:
preprocessor = pickle.load(f)
print(f"✓ 模型加载成功")
# ========== 获取2026年数据 ==========
print("\n[2/4] 获取2026年数据...")
fetcher = BinanceDataFetcher()
try:
df_2026 = fetcher.fetch_ohlcv('BTC', '1h', start_date='2026-01-01',
end_date='2026-12-31', use_cache=False)
except:
df_2026 = fetcher.fetch_ohlcv('BTC', '1h', start_date='2025-12-01', use_cache=False)
print(f"✓ 数据量: {len(df_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}")
# ========== 预处理 ==========
print("\n[3/4] 预处理并预测...")
X, y, processed_df = preprocessor.preprocess(df_2026, fit=False)
if len(X) == 0:
print("❌ 数据不足")
return
# 模型预测
X_tensor = torch.FloatTensor(X).to(device)
with torch.no_grad():
predictions = model(X_tensor).squeeze().cpu().numpy()
# 还原为真实价格
pred_prices = preprocessor.inverse_transform_price(predictions)
true_prices = preprocessor.inverse_transform_price(y)
# 获取当前价格
current_prices = processed_df['close'].iloc[168:168+len(pred_prices)].values
# ========== 方向判断 ==========
print("\n[4/4] 涨跌方向评估...")
pred_direction = (pred_prices > current_prices).astype(int)
true_direction = (true_prices > current_prices).astype(int)
correct = (pred_direction == true_direction).sum()
accuracy = correct / len(predictions)
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(true_direction, pred_direction)
tn, fp, fn, tp = cm.ravel()
print("\n" + "="*80)
print("📊 2026年价格预测结果")
print("="*80)
print(f"\n价格预测误差:")
price_mae = np.abs(pred_prices - true_prices).mean()
price_rmse = np.sqrt(((pred_prices - true_prices) ** 2).mean())
price_mape = np.mean(np.abs((true_prices - pred_prices) / true_prices)) * 100
print(f" MAE: ${price_mae:,.2f}")
print(f" RMSE: ${price_rmse:,.2f}")
print(f" MAPE: {price_mape:.2f}%")
print(f"\n方向预测统计:")
print(f" 预测上涨: {(pred_direction==1).sum()} / 实际上涨: {(true_direction==1).sum()}")
print(f" 预测下跌: {(pred_direction==0).sum()} / 实际下跌: {(true_direction==0).sum()}")
print(f"\n方向准确率:")
print(f" 正确: {correct}/{len(predictions)} ({accuracy*100:.2f}%)")
print(f"\n详细分析:")
print(f" ✓ 预测涨且真涨: {tp} ({tp/len(predictions)*100:.1f}%)")
print(f" ✓ 预测跌且真跌: {tn} ({tn/len(predictions)*100:.1f}%)")
print(f" ✗ 预测涨但真跌: {fp} ({fp/len(predictions)*100:.1f}%)")
print(f" ✗ 预测跌但真涨: {fn} ({fn/len(predictions)*100:.1f}%)")
# 性能评估
print("\n" + "="*80)
print("🎯 性能评估")
print("="*80)
if accuracy > 0.55:
print(f"\n ✅ 方向准确率 {accuracy*100:.2f}% > 55%,模型有预测能力")
elif accuracy > 0.52:
print(f"\n ⚠️ 方向准确率 {accuracy*100:.2f}% > 52%,略优于随机")
elif accuracy >= 0.48:
print(f"\n ⚠️ 方向准确率 {accuracy*100:.2f}% ≈ 50%,接近随机")
else:
print(f"\n ❌ 方向准确率 {accuracy*100:.2f}% < 48%,低于随机")
# 预测样本展示
print("\n" + "="*80)
print("📋 预测样本展示(前30个)")
print("="*80)
print(f"\n{'时间':<20} {'当前价格':<12} {'预测价格':<12} {'实际价格':<12} {'预测':<6} {'实际':<6} {'结果':<4}")
print("-"*90)
for i in range(min(30, len(predictions))):
timestamp = processed_df['timestamp'].iloc[i + 168]
curr = current_prices[i]
pred = pred_prices[i]
true = true_prices[i]
pred_dir = "上涨" if pred > curr else "下跌"
true_dir = "上涨" if true > curr else "下跌"
result = "✓" if pred_direction[i] == true_direction[i] else "✗"
print(f"{str(timestamp):<20} ${curr:>10,.2f} ${pred:>10,.2f} ${true:>10,.2f} "
f"{pred_dir:<6} {true_dir:<6} {result:<4}")
print("\n" + "="*80)
print(f"总结: 在{len(predictions)}次预测中,方向正确{correct}次 ({accuracy*100:.2f}%)")
print("="*80)
if __name__ == "__main__":
main()