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()
|
conn.rollback()
|
||||||
# 忽略已存在错误
|
# 忽略已存在错误
|
||||||
continue
|
continue
|
||||||
|
cur.execute(
|
||||||
|
"ALTER TABLE paper_trades "
|
||||||
|
"ADD COLUMN IF NOT EXISTS strategy VARCHAR(32) DEFAULT 'v51_baseline'"
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
ensure_partitions()
|
ensure_partitions()
|
||||||
|
|||||||
@ -442,7 +442,7 @@ async def get_market_indicators(user: dict = Depends(get_current_user)):
|
|||||||
result = {}
|
result = {}
|
||||||
for sym in SYMBOLS:
|
for sym in SYMBOLS:
|
||||||
indicators = {}
|
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(
|
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",
|
"SELECT value, timestamp_ms FROM market_indicators WHERE symbol = $1 AND indicator_type = $2 ORDER BY timestamp_ms DESC LIMIT 1",
|
||||||
sym,
|
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)):
|
async def paper_positions(user: dict = Depends(get_current_user)):
|
||||||
"""当前活跃持仓(含实时价格和浮动盈亏)"""
|
"""当前活跃持仓(含实时价格和浮动盈亏)"""
|
||||||
rows = await async_fetch(
|
rows = await async_fetch(
|
||||||
"SELECT id, symbol, direction, score, tier, entry_price, entry_ts, "
|
"SELECT id, symbol, direction, score, tier, strategy, entry_price, entry_ts, "
|
||||||
"tp1_price, tp2_price, sl_price, tp1_hit, status, atr_at_entry "
|
"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"
|
"FROM paper_trades WHERE status IN ('active','tp1_hit') ORDER BY entry_ts DESC"
|
||||||
)
|
)
|
||||||
# 从币安API获取实时价格
|
# 从币安API获取实时价格
|
||||||
@ -624,6 +624,7 @@ async def paper_positions(user: dict = Depends(get_current_user)):
|
|||||||
async def paper_trades(
|
async def paper_trades(
|
||||||
symbol: str = "all",
|
symbol: str = "all",
|
||||||
result: str = "all",
|
result: str = "all",
|
||||||
|
strategy: str = "all",
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
user: dict = Depends(get_current_user),
|
user: dict = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
@ -642,11 +643,16 @@ async def paper_trades(
|
|||||||
elif result == "loss":
|
elif result == "loss":
|
||||||
conditions.append("pnl_r <= 0")
|
conditions.append("pnl_r <= 0")
|
||||||
|
|
||||||
|
if strategy != "all":
|
||||||
|
conditions.append(f"strategy = ${idx}")
|
||||||
|
params.append(strategy)
|
||||||
|
idx += 1
|
||||||
|
|
||||||
where = " AND ".join(conditions)
|
where = " AND ".join(conditions)
|
||||||
params.append(limit)
|
params.append(limit)
|
||||||
rows = await async_fetch(
|
rows = await async_fetch(
|
||||||
f"SELECT id, symbol, direction, score, tier, entry_price, exit_price, "
|
f"SELECT id, symbol, direction, score, tier, strategy, entry_price, exit_price, "
|
||||||
f"entry_ts, exit_ts, pnl_r, status, tp1_hit "
|
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}",
|
f"FROM paper_trades WHERE {where} ORDER BY exit_ts DESC LIMIT ${idx}",
|
||||||
*params
|
*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
|
import shutil, subprocess, psutil
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user