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:
1 parent
c0a02868ac
commit
2993014fa8
5 files changed
+465
-335
No files matched your search
+48
-58
@@ -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:
|
||||
"""
|
||||
|
||||
Reference in new issue
Block a user