feat: expand header stats with total funds and profit-loss ratio
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,8 @@ from typing import Any
|
||||
|
||||
EMBED_STRATEGY_PAGES = frozenset({"strategy", "strategy_trend", "strategy_roll", "strategy_records"})
|
||||
|
||||
_WIN_EPS = 1e-9
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EmbedRenderPlan:
|
||||
@@ -50,6 +52,48 @@ def embed_render_plan(page: str, embed_mode: str | None) -> EmbedRenderPlan:
|
||||
)
|
||||
|
||||
|
||||
def profit_loss_ratio_from_averages(avg_win: float | None, avg_loss: float | None) -> float | None:
|
||||
"""盈亏比 = 平均盈利 / |平均亏损|。"""
|
||||
if avg_win is None or avg_loss is None:
|
||||
return None
|
||||
try:
|
||||
aw = float(avg_win)
|
||||
al = float(avg_loss)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if al == 0:
|
||||
return None
|
||||
return round(aw / abs(al), 2)
|
||||
|
||||
|
||||
def profit_loss_ratio_from_trades(trades: list[dict[str, Any]] | None) -> float | None:
|
||||
wins: list[float] = []
|
||||
losses: list[float] = []
|
||||
for row in trades or []:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
try:
|
||||
pnl = float(row.get("effective_pnl_amount") or row.get("pnl_amount") or 0)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if pnl > _WIN_EPS:
|
||||
wins.append(pnl)
|
||||
elif pnl < -_WIN_EPS:
|
||||
losses.append(pnl)
|
||||
avg_win = sum(wins) / len(wins) if wins else None
|
||||
avg_loss = sum(losses) / len(losses) if losses else None
|
||||
return profit_loss_ratio_from_averages(avg_win, avg_loss)
|
||||
|
||||
|
||||
def total_funds_usdt(funding_usdt: float | None, trading_usdt: float | None) -> float | None:
|
||||
if funding_usdt is None:
|
||||
return None
|
||||
try:
|
||||
return round(float(funding_usdt) + float(trading_usdt or 0), 2)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def trade_records_summary(conn, start_bj: str, end_bj: str, tr_ts: str) -> dict[str, Any]:
|
||||
"""顶栏统计用 COUNT,避免 embed 壳拉 1000 行交易记录。"""
|
||||
from lib.trade.trade_result_lib import sql_effective_pnl_expr
|
||||
@@ -59,7 +103,9 @@ def trade_records_summary(conn, start_bj: str, end_bj: str, tr_ts: str) -> dict[
|
||||
f"""
|
||||
SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(CASE WHEN {pnl_sql} > 0 THEN 1 ELSE 0 END) AS wins
|
||||
SUM(CASE WHEN {pnl_sql} > 0 THEN 1 ELSE 0 END) AS wins,
|
||||
AVG(CASE WHEN {pnl_sql} > 0 THEN {pnl_sql} END) AS avg_win,
|
||||
AVG(CASE WHEN {pnl_sql} < 0 THEN {pnl_sql} END) AS avg_loss
|
||||
FROM trade_records
|
||||
WHERE {tr_ts} >= ? AND {tr_ts} <= ?
|
||||
AND COALESCE(result, '') != '错过'
|
||||
@@ -70,10 +116,13 @@ def trade_records_summary(conn, start_bj: str, end_bj: str, tr_ts: str) -> dict[
|
||||
total = int(row["total"] or 0) if row else 0
|
||||
wins = int(row["wins"] or 0) if row else 0
|
||||
rate = round(wins / total * 100, 2) if total else 0
|
||||
avg_win = float(row["avg_win"]) if row and row["avg_win"] is not None else None
|
||||
avg_loss = float(row["avg_loss"]) if row and row["avg_loss"] is not None else None
|
||||
return {
|
||||
"records": [],
|
||||
"total": total,
|
||||
"rate": rate,
|
||||
"profit_loss_ratio": profit_loss_ratio_from_averages(avg_win, avg_loss),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user