Add strategy-aware paper trade schema and API endpoints

This commit is contained in:
root 2026-03-01 11:55:00 +00:00
parent 732b01691b
commit f6156a2cfe
2 changed files with 71 additions and 5 deletions

View File

@ -355,5 +355,9 @@ def init_schema():
conn.rollback()
# 忽略已存在错误
continue
cur.execute(
"ALTER TABLE paper_trades "
"ADD COLUMN IF NOT EXISTS strategy VARCHAR(32) DEFAULT 'v51_baseline'"
)
conn.commit()
ensure_partitions()

View File

@ -442,7 +442,7 @@ async def get_market_indicators(user: dict = Depends(get_current_user)):
result = {}
for sym in SYMBOLS:
indicators = {}
for ind_type in ["long_short_ratio", "top_trader_position", "open_interest_hist", "coinbase_premium"]:
for ind_type in ["long_short_ratio", "top_trader_position", "open_interest_hist", "coinbase_premium", "funding_rate"]:
row = await async_fetchrow(
"SELECT value, timestamp_ms FROM market_indicators WHERE symbol = $1 AND indicator_type = $2 ORDER BY timestamp_ms DESC LIMIT 1",
sym,
@ -568,8 +568,8 @@ async def paper_summary(user: dict = Depends(get_current_user)):
async def paper_positions(user: dict = Depends(get_current_user)):
"""当前活跃持仓(含实时价格和浮动盈亏)"""
rows = await async_fetch(
"SELECT id, symbol, direction, score, tier, entry_price, entry_ts, "
"tp1_price, tp2_price, sl_price, tp1_hit, status, atr_at_entry "
"SELECT id, symbol, direction, score, tier, strategy, entry_price, entry_ts, "
"tp1_price, tp2_price, sl_price, tp1_hit, status, atr_at_entry, score_factors "
"FROM paper_trades WHERE status IN ('active','tp1_hit') ORDER BY entry_ts DESC"
)
# 从币安API获取实时价格
@ -624,6 +624,7 @@ async def paper_positions(user: dict = Depends(get_current_user)):
async def paper_trades(
symbol: str = "all",
result: str = "all",
strategy: str = "all",
limit: int = 100,
user: dict = Depends(get_current_user),
):
@ -642,11 +643,16 @@ async def paper_trades(
elif result == "loss":
conditions.append("pnl_r <= 0")
if strategy != "all":
conditions.append(f"strategy = ${idx}")
params.append(strategy)
idx += 1
where = " AND ".join(conditions)
params.append(limit)
rows = await async_fetch(
f"SELECT id, symbol, direction, score, tier, entry_price, exit_price, "
f"entry_ts, exit_ts, pnl_r, status, tp1_hit "
f"SELECT id, symbol, direction, score, tier, strategy, entry_price, exit_price, "
f"entry_ts, exit_ts, pnl_r, status, tp1_hit, score_factors "
f"FROM paper_trades WHERE {where} ORDER BY exit_ts DESC LIMIT ${idx}",
*params
)
@ -788,6 +794,62 @@ async def paper_stats(user: dict = Depends(get_current_user)):
}
@app.get("/api/paper/stats-by-strategy")
async def paper_stats_by_strategy(user: dict = Depends(get_current_user)):
"""按策略聚合模拟盘表现"""
rows = await async_fetch(
"SELECT strategy, pnl_r FROM paper_trades WHERE status NOT IN ('active','tp1_hit')"
)
active_rows = await async_fetch(
"SELECT strategy, COUNT(*) AS active_count FROM paper_trades "
"WHERE status IN ('active','tp1_hit') GROUP BY strategy"
)
if not rows and not active_rows:
return {"data": []}
active_map = {r["strategy"] or "v51_baseline": int(r["active_count"]) for r in active_rows}
by_strategy: dict[str, list[float]] = {}
for row in rows:
strategy = row["strategy"] or "v51_baseline"
by_strategy.setdefault(strategy, []).append(float(row["pnl_r"]))
stats = []
for strategy, pnls in by_strategy.items():
total = len(pnls)
wins = [p for p in pnls if p > 0]
losses = [p for p in pnls if p <= 0]
avg_win = sum(wins) / len(wins) if wins else 0
avg_loss = abs(sum(losses) / len(losses)) if losses else 0
stats.append(
{
"strategy": strategy,
"total": total,
"win_rate": round((len(wins) / total) * 100, 1) if total else 0,
"total_pnl": round(sum(pnls), 2),
"avg_win": round(avg_win, 2),
"avg_loss": round(avg_loss, 2),
"active_positions": active_map.get(strategy, 0),
}
)
for strategy, active_count in active_map.items():
if strategy not in by_strategy:
stats.append(
{
"strategy": strategy,
"total": 0,
"win_rate": 0,
"total_pnl": 0,
"avg_win": 0,
"avg_loss": 0,
"active_positions": active_count,
}
)
stats.sort(key=lambda x: x["strategy"])
return {"data": stats}
# ─── 服务器状态监控 ───────────────────────────────────────────────
import shutil, subprocess, psutil