Add strategy-aware paper trade schema and API endpoints
This commit is contained in:
parent
732b01691b
commit
f6156a2cfe
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user