feat: upgrade to multi-model ensemble system

- Updated DB schemas: added 'strategy' to trades, new 'hourly_model_predictions' wide table
- Refactored backend: api/models.py, api/prediction_service.py to support wide format
- Updated trader: auto_trade_polymarket.py now logs strategy and saves to new table
- Updated frontend: index.html and app.js to display multi-model accuracy and trade strategy
This commit is contained in:
crypto_predictor committed 2026-02-11 22:13:35 +08:00
1 parent c0a02868ac
commit 2993014fa8
5 files changed
+465 -335

No files matched your search

+48 -58
View File
@@ -131,48 +131,37 @@ class PredictionService:
def ensure_predictions_for_date(self, date: str) -> None:
"""
确保指定日期有预测数据,如果没有则生成
确保指定日期有预测数据,如果没有则生成 (V2)
Args:
date: 日期 (YYYY-MM-DD)
"""
# 使用锁避免并发冲突
with PredictionService._prediction_lock:
# 检查是否正在处理该日期
if date in self._processing_dates:
return
# 标记正在处理
self._processing_dates.add(date)
try:
# 检查是否有缺失的小时 (现在只检查 DLinear 和 TTM-R2 作为代表,或者扩展到 N-BEATS)
# 为了保持兼容性,还是主要检查这两个,或者可以增加 N-BEATS
models_to_check = ["DLinear", "TTM-R2", "N-BEATS"]
# 检查是否有缺失的小时 (只检查宽表是否有记录)
missing_hours = PredictionModel.get_missing_hours(date)
for model_name in models_to_check:
# 数据库中模型名字对应
db_model_name = model_name
missing_hours = PredictionModel.get_missing_hours(date, db_model_name)
if not missing_hours:
continue
print(f"为 {date} 生成 {model_name} 预测 (缺失 {len(missing_hours)} 小时)...")
# 获取足够的历史数据
target_date = datetime.strptime(date, '%Y-%m-%d')
df = self._get_historical_data(target_date)
if df is None or len(df) < 200:
print(f" 警告: 数据不足,无法为 {date} 生成预测")
continue
# 为每个缺失的小时生成预测
for hour in missing_hours:
self._generate_prediction_for_hour(df, date, hour, model_name)
if not missing_hours:
return
print(f"为 {date} 生成预测 (缺失 {len(missing_hours)} 小时)...")
# 获取足够的历史数据
target_date = datetime.strptime(date, '%Y-%m-%d')
df = self._get_historical_data(target_date)
if df is None or len(df) < 200:
print(f" 警告: 数据不足,无法为 {date} 生成预测")
return
# 为每个缺失的小时生成预测
for hour in missing_hours:
self._generate_prediction_for_hour(df, date, hour)
finally:
# 移除处理标记
with PredictionService._prediction_lock:
self._processing_dates.discard(date)
@@ -194,10 +183,9 @@ class PredictionService:
print(f"获取历史数据失败: {e}")
return None
def _generate_prediction_for_hour(self, df: pd.DataFrame, date: str,
hour: int, model_name: str) -> None:
def _generate_prediction_for_hour(self, df: pd.DataFrame, date: str, hour: int) -> None:
"""
为指定小时生成预测
为指定小时生成预测 (V2)
"""
try:
# 1. 构建目标时间 (美东时间 -> UTC)
@@ -234,45 +222,47 @@ class PredictionService:
if len(df_input) < 200:
return
# 6. 调用模型预测
prediction = None
probability = 0.5
# 6. 调用多模型预测
preds = self.inference_manager.predict_all(df_input)
if model_name == "DLinear":
prediction, probability = self.inference_manager.predict_dlinear(df_input)
elif model_name == "TTM-R2":
prediction, probability = self.inference_manager.predict_ttm(df_input)
elif model_name == "N-BEATS":
prediction, probability = self.inference_manager.predict_nbeats(df_input)
if prediction is None:
return
# 7. 判断实际涨跌
actual_result = 1 if target_close > prev_close else 0
# 8. 判断预测是否正确
is_correct = 1 if prediction == actual_result else 0
# 9. 保存预测结果
# 8. 组装数据 (宽表格式)
prediction_data = {
"timestamp": target_time_utc.isoformat(),
"timestamp_et": target_time_et.isoformat(),
"date": date,
"hour": hour,
"model_name": model_name,
"prediction": prediction,
"probability": probability,
"actual_result": actual_result,
"is_correct": is_correct,
"open_price": target_open,
"close_price": target_close
"close_price": target_close,
"actual_result": actual_result,
# DLinear
"dlinear_prediction": preds.get('dlinear', {}).get('prediction'),
"dlinear_prob": preds.get('dlinear', {}).get('probability'),
"dlinear_correct": 1 if preds.get('dlinear', {}).get('prediction') == actual_result else 0,
# N-BEATS
"nbeats_prediction": preds.get('nbeats', {}).get('prediction'),
"nbeats_prob": preds.get('nbeats', {}).get('probability'),
"nbeats_correct": 1 if preds.get('nbeats', {}).get('prediction') == actual_result else 0,
# TTM-R2
"ttm_prediction": preds.get('ttm', {}).get('prediction'),
"ttm_prob": preds.get('ttm', {}).get('probability'),
"ttm_correct": 1 if preds.get('ttm', {}).get('prediction') == actual_result else 0,
# Ensemble
"ensemble_prediction": preds.get('ensemble', {}).get('prediction'),
"ensemble_votes": preds.get('ensemble', {}).get('votes'),
"ensemble_correct": 1 if preds.get('ensemble', {}).get('prediction') == actual_result else 0,
}
PredictionModel.save_prediction(prediction_data)
PredictionModel.save_hourly_prediction(prediction_data)
except Exception as e:
print(f"生成预测失败 ({date} {hour}:00 {model_name}): {e}")
print(f"生成预测失败 ({date} {hour}:00): {e}")
def ensure_predictions_for_month(self, month: str) -> None:
"""