diff --git a/manual_trading_hub/hub.py b/manual_trading_hub/hub.py index feb8509..a2b82b9 100644 --- a/manual_trading_hub/hub.py +++ b/manual_trading_hub/hub.py @@ -67,6 +67,7 @@ from settings_store import ( enabled_exchanges, env_force_disabled_ids, load_settings, + normalize_display_prefs, save_settings, ) from hub_web_auth import ( @@ -676,8 +677,13 @@ def api_get_settings(): return load_settings() +class SettingsDisplayBody(BaseModel): + show_account_pnl: bool = True + + class SettingsBody(BaseModel): exchanges: list[dict] = Field(default_factory=list) + display: SettingsDisplayBody | None = None @app.post("/api/settings") @@ -691,7 +697,11 @@ def api_save_settings(body: SettingsBody): row["enabled"] = False row.pop("env_disabled", None) to_save.append(row) - save_settings({"version": 1, "exchanges": to_save}) + existing = load_settings() + display = normalize_display_prefs(existing.get("display")) + if body.display is not None: + display = normalize_display_prefs(body.display.model_dump()) + save_settings({"version": 1, "exchanges": to_save, "display": display}) return {"ok": True, "settings": load_settings()} diff --git a/manual_trading_hub/settings_store.py b/manual_trading_hub/settings_store.py index e34434b..a4a4e26 100644 --- a/manual_trading_hub/settings_store.py +++ b/manual_trading_hub/settings_store.py @@ -9,6 +9,10 @@ from pathlib import Path DIR = Path(__file__).resolve().parent SETTINGS_PATH = DIR / "hub_settings.json" +DEFAULT_DISPLAY = { + "show_account_pnl": True, +} + DEFAULT_EXCHANGES = [ { "id": "0", @@ -65,8 +69,20 @@ def env_force_disabled_ids() -> set[str]: return _ids_from_csv(raw) +def normalize_display_prefs(raw: dict | None) -> dict: + out = dict(DEFAULT_DISPLAY) + if isinstance(raw, dict): + if "show_account_pnl" in raw: + out["show_account_pnl"] = bool(raw.get("show_account_pnl")) + return out + + def load_settings() -> dict: - data = {"exchanges": [dict(x) for x in DEFAULT_EXCHANGES], "version": 1} + data = { + "exchanges": [dict(x) for x in DEFAULT_EXCHANGES], + "version": 1, + "display": dict(DEFAULT_DISPLAY), + } if SETTINGS_PATH.is_file(): try: loaded = json.loads(SETTINGS_PATH.read_text(encoding="utf-8")) @@ -74,6 +90,7 @@ def load_settings() -> dict: data = loaded except Exception: pass + data["display"] = normalize_display_prefs(data.get("display")) force_off = env_force_disabled_ids() for ex in data.get("exchanges") or []: if str(ex.get("id")) in force_off: diff --git a/manual_trading_hub/static/app.css b/manual_trading_hub/static/app.css index f2c6594..78bcbcf 100644 --- a/manual_trading_hub/static/app.css +++ b/manual_trading_hub/static/app.css @@ -2164,6 +2164,28 @@ button.btn-sm { text-transform: none; } +.settings-display-panel { + margin-bottom: 16px; + padding: 14px 16px; +} + +.settings-display-title { + margin: 0 0 10px; + font-size: 0.95rem; + color: var(--text); +} + +.settings-display-chk { + font-size: 0.88rem; +} + +.settings-display-hint { + margin: 8px 0 0; + font-size: 0.78rem; + color: var(--muted); + line-height: 1.45; +} + .settings-card { background: var(--panel); border: 1px solid var(--border); diff --git a/manual_trading_hub/static/app.js b/manual_trading_hub/static/app.js index d31240b..ef192e1 100644 --- a/manual_trading_hub/static/app.js +++ b/manual_trading_hub/static/app.js @@ -2,6 +2,25 @@ const toast = document.getElementById("toast"); let settingsCache = null; let authState = { required: false, logged_in: true }; + + function showAccountPnlPref() { + const d = settingsCache && settingsCache.display; + if (!d || d.show_account_pnl === undefined) return true; + return !!d.show_account_pnl; + } + + function syncDisplayPrefsUI(data) { + const cb = document.getElementById("pref-show-account-pnl"); + if (!cb) return; + const show = data && data.display ? data.display.show_account_pnl : true; + cb.checked = show !== false; + } + + function positionTableHeadHtml(compact) { + const pnlTh = showAccountPnlPref() ? "浮盈" : ""; + const cls = compact ? " data-table data-table-positions" : ""; + return `${pnlTh}`; + } let tpslPending = null; let lastMonitorRows = []; let expandedExchangeId = sessionStorage.getItem("hub_expanded_ex") || ""; @@ -2218,7 +2237,11 @@
止盈${formatTpCellValue(tp, tpMonitored, symbol, tickMap)}
盈亏比${rr != null ? fmt(rr, 2) + ":1" : "—"}
张数${fmt(pos.contracts, 4)}
-
浮盈亏${pnlText}
+ ${ + showAccountPnlPref() + ? `
浮盈亏${pnlText}
` + : "" + } `; + const pnlTd = showAccountPnlPref() + ? `` + : ""; return ` - + ${pnlTd} `; } @@ -2354,7 +2380,7 @@ ); return `
-
合约方向开仓价标记价张数操作
${fmt(x.unrealized_pnl, 2)}
${symBeBadge} ${renderDirectionHtml(x.side)} ${fmtEntryPrice(x, tickMap)} ${fmtMarkPrice(x, tickMap)} ${fmt(x.contracts, 4)}${fmt(x.unrealized_pnl, 2)}${actionCell}
+ ${positionTableHeadHtml(false)} ${rowHtml}
合约方向开仓价标记价张数浮盈操作
@@ -2442,13 +2468,14 @@ ) .join(""); return `
- + ${positionTableHeadHtml(true)} ${rows}
合约方向开仓价标记价张数浮盈操作
`; } function renderAccountStatRow(row, ag) { + if (!showAccountPnlPref()) return ""; const upnl = ag.total_unrealized_pnl; return `
资金账户
${fmt(row.funding_usdt, 2)} U
@@ -2763,7 +2790,11 @@ ${esc(row.name)}
-
${fmt(upnl, 2)} U
+ ${ + showAccountPnlPref() + ? `
${fmt(upnl, 2)} U
` + : "" + }
${esc(posLine)}
${strategyStats}
UPD ${esc(tsShort)}
@@ -2977,6 +3008,7 @@ function loadSettingsUI() { loadSettingsMetaLine(); loadSettings().then((data) => { + syncDisplayPrefsUI(data); renderSettingsList(data); }); } @@ -3010,8 +3042,12 @@ function collectSettingsFromUI() { const rows = [...document.querySelectorAll("#settings-list .settings-card")]; + const pnlCb = document.getElementById("pref-show-account-pnl"); return { version: 1, + display: { + show_account_pnl: pnlCb ? !!pnlCb.checked : true, + }, exchanges: rows.map((card) => { const caps = []; if (card.querySelector(".cap-key").checked) caps.push("key"); @@ -3045,11 +3081,13 @@ showToast("设置已保存(已写入 hub_settings.json)"); if (j.settings) { settingsCache = j.settings; + syncDisplayPrefsUI(j.settings); renderSettingsList(j.settings); loadSettingsMetaLine(); } else { await loadSettingsUI(); } + if (lastMonitorRows.length) renderMonitorGrid(lastMonitorRows); } else showToast("保存失败", true); } catch (e) { showToast(String(e), true); @@ -3526,9 +3564,13 @@ initAuth().then((ok) => { if (!ok) return; initShellNav(); - setActiveNav(); - if (currentPage() === "settings") { - loadSettings().catch(() => {}); - } + loadSettings() + .then((data) => { + syncDisplayPrefsUI(data); + }) + .catch(() => {}) + .finally(() => { + setActiveNav(); + }); }); })(); diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html index 857469e..665f838 100644 --- a/manual_trading_hub/static/index.html +++ b/manual_trading_hub/static/index.html @@ -466,6 +466,14 @@

+
+

监控区显示

+ +

关闭后监控区不显示上述数值;保存至 hub_settings.json,换浏览器同样生效。

+
diff --git a/strategy_templates/key_monitor_panel.html b/strategy_templates/key_monitor_panel.html index 6996ae8..8a723e0 100644 --- a/strategy_templates/key_monitor_panel.html +++ b/strategy_templates/key_monitor_panel.html @@ -9,14 +9,19 @@ .key-panel-scroll.panel-scroll.pos-list{ display:block; flex:1 1 auto; - min-height:200px; - max-height:none; + min-height:0; overflow-x:hidden; overflow-y:auto; padding-bottom:6px; -webkit-overflow-scrolling:touch; scrollbar-gutter:stable; } +.key-monitor-panel-scroll{min-height:200px} +.key-history-panel-scroll{ + flex:0 0 auto; + max-height:calc(8 * 42px + 7 * 8px); + min-height:calc(3 * 42px + 2 * 8px); +} .key-panel-scroll.panel-scroll.pos-list .key-row-collapse{flex-shrink:0} .key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar{width:8px} .key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar-thumb{background:#3a4660;border-radius:4px} @@ -25,16 +30,26 @@ .key-row-collapse:not([open]){overflow:hidden} .key-row-collapse[open]{overflow:visible} .key-row-collapse+.key-row-collapse{margin-top:8px} -.key-row-collapse-summary{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:10px 12px;cursor:pointer;list-style:none;font-size:.8rem;color:#c5cde0;line-height:1.45} +.key-row-collapse-summary{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px;cursor:pointer;list-style:none;font-size:.8rem;color:#c5cde0;line-height:1.45} .key-row-collapse-summary::-webkit-details-marker{display:none} -.key-row-collapse-summary::before{content:"▸";flex:0 0 auto;color:#6d7a99;margin-top:1px;transition:transform .15s ease} +.key-row-collapse-summary::before{content:"▸";flex:0 0 auto;color:#6d7a99;transition:transform .15s ease} .key-row-collapse[open]>.key-row-collapse-summary::before{transform:rotate(90deg)} -.key-row-summary-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px} -.key-row-summary-title{display:flex;align-items:center;gap:6px;flex-wrap:wrap} +.key-row-summary-main{flex:1;min-width:0;display:flex;align-items:center;justify-content:space-between;gap:10px} +.key-row-summary-title{display:flex;align-items:center;gap:6px;flex:0 1 auto;flex-wrap:wrap;min-width:0} .key-row-summary-title strong{font-size:.88rem;color:#fff} .key-row-summary-line{color:#9aa8c4;font-size:.76rem;word-break:break-word} -.key-row-summary-live{color:#8fc8ff;font-size:.74rem} +.key-row-summary-live{ + flex:1 1 auto; + min-width:0; + color:#8fc8ff; + font-size:.72rem; + text-align:right; + white-space:nowrap; + overflow:hidden; + text-overflow:ellipsis; +} .key-row-summary-live.key-row-summary-pending{color:#4cd97f;font-weight:600} +.key-history-panel-scroll .key-row-summary-main{justify-content:flex-start} .key-row-summary-actions{flex:0 0 auto;display:flex;gap:6px;align-items:center} .key-row-collapse-body{padding:0 12px 16px;border-top:1px solid #232b3d} .key-row-collapse-body .pos-meta{margin-top:10px;margin-bottom:10px} @@ -150,7 +165,7 @@ {% include 'key_monitor_rule_tips.html' %}
-
+
{% for k in key %}
@@ -200,7 +215,7 @@

关键位历史

失效或已结案的关键位 · 点击展开详情
-
+
{% for h in key_history %}