Show average profit and loss in option stats panel.

Expose avg_win and avg_loss from stats API and update chart labels and values to match the average-based P/L ratio.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-11 09:29:26 +08:00
parent bf2d668d3c
commit 68733eb4f9
4 changed files with 17 additions and 13 deletions
+8 -8
View File
@@ -800,12 +800,12 @@
}
if (closedEl) closedEl.textContent = String(d.total_closed || 0);
if (profitEl) {
profitEl.textContent = d.total_profit != null && d.total_profit > 0
? fmt(d.total_profit, 2) + " USDC" : (d.total_closed ? "0 USDC" : "—");
profitEl.textContent = d.avg_win != null && d.avg_win > 0
? fmt(d.avg_win, 2) + " USDC" : (d.win_count ? "0 USDC" : "—");
}
if (lossEl) {
lossEl.textContent = d.total_loss != null && d.total_loss > 0
? fmt(d.total_loss, 2) + " USDC" : (d.total_closed ? "0 USDC" : "—");
lossEl.textContent = d.avg_loss != null && d.avg_loss > 0
? fmt(d.avg_loss, 2) + " USDC" : (d.loss_count ? "0 USDC" : "—");
}
if (avgHoldEl) avgHoldEl.textContent = fmtDuration(d.avg_hold_sec);
if (winHoldEl) winHoldEl.textContent = fmtDuration(d.avg_win_hold_sec);
@@ -868,8 +868,8 @@
if (ring) ring.style.setProperty("--win-pct", String(winRate));
if (ringLabel) ringLabel.textContent = d.total_closed ? winRate.toFixed(0) + "%" : "0%";
const profit = Math.max(0, Number(d.total_profit) || 0);
const loss = Math.max(0, Number(d.total_loss) || 0);
const profit = Math.max(0, Number(d.avg_win) || 0);
const loss = Math.max(0, Number(d.avg_loss) || 0);
const pnlTotal = profit + loss;
if (pnlTotal > 0) {
setBarFill(profitBar, (profit / pnlTotal) * 100);
@@ -879,8 +879,8 @@
} else {
setBarFill(profitBar, 0);
setBarFill(lossBar, 0);
if (profitBarLabel) profitBarLabel.textContent = d.total_closed ? "0 USDC" : "—";
if (lossBarLabel) lossBarLabel.textContent = d.total_closed ? "0 USDC" : "—";
if (profitBarLabel) profitBarLabel.textContent = d.win_count ? "0 USDC" : "—";
if (lossBarLabel) lossBarLabel.textContent = d.loss_count ? "0 USDC" : "—";
}
const winHold = Number(d.avg_win_hold_sec) || 0;
+2
View File
@@ -93,6 +93,8 @@ def compute_options_stats(get_db) -> dict[str, Any]:
"loss_count": len(losses),
"win_rate": win_rate,
"profit_loss_ratio": profit_loss_ratio_from_averages(avg_win, avg_loss),
"avg_win": round(avg_win, 4) if avg_win is not None else None,
"avg_loss": round(abs(avg_loss), 4) if avg_loss is not None else None,
"total_profit": round(sum(wins), 4) if wins else 0.0,
"total_loss": round(abs(sum(losses)), 4) if losses else 0.0,
"avg_hold_sec": _avg_seconds(all_holds),
+5 -5
View File
@@ -115,14 +115,14 @@
</div>
<div class="opt-stats-chart opt-stats-chart--pnl">
<div class="opt-stats-bar-row">
<span class="k">盈利</span>
<span class="k">平均盈利</span>
<div class="opt-stats-bar-track">
<div class="opt-stats-bar-fill opt-stats-bar-fill--profit" id="opt-stats-bar-profit"></div>
</div>
<span class="v pos-pnl-profit" id="opt-stats-bar-profit-label"></span>
</div>
<div class="opt-stats-bar-row">
<span class="k">亏损</span>
<span class="k">平均亏损</span>
<div class="opt-stats-bar-track">
<div class="opt-stats-bar-fill opt-stats-bar-fill--loss" id="opt-stats-bar-loss"></div>
</div>
@@ -161,11 +161,11 @@
<span class="v" id="opt-stats-closed"></span>
</div>
<div class="options-stat-item">
<span class="k">盈利</span>
<span class="k">平均盈利</span>
<span class="v pos-pnl-profit" id="opt-stats-profit"></span>
</div>
<div class="options-stat-item">
<span class="k">亏损</span>
<span class="k">平均亏损</span>
<span class="v pos-pnl-loss" id="opt-stats-loss"></span>
</div>
<div class="options-stat-item">
@@ -212,4 +212,4 @@
</div>
</div>
<script src="/static/options_expiry_countdown.js?v=1"></script>
<script src="/static/options_panel.js?v=25"></script>
<script src="/static/options_panel.js?v=26"></script>
+2
View File
@@ -61,6 +61,8 @@ class OptionsStatsLibTests(TestCase):
self.assertEqual(out["win_count"], 1)
self.assertEqual(out["loss_count"], 1)
self.assertEqual(out["win_rate"], 50.0)
self.assertAlmostEqual(out["avg_win"], 1.2, places=4)
self.assertAlmostEqual(out["avg_loss"], 1.0, places=4)
self.assertAlmostEqual(out["avg_win_hold_sec"], 3600.0, delta=5.0)
self.assertAlmostEqual(out["avg_loss_hold_sec"], 3 * 3600.0, delta=5.0)
self.assertEqual(out["open_count"], 1)