From b0a463a22cb13cdfcebd404bda61b6da4dbaa507 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 2 Mar 2026 10:55:52 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=9B=98=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=20(live=5Fconfig)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DB: - live_config表(key/value/label/updated_at) - 默认7项: 1R金额/初始本金/风险比例/最大持仓/杠杆/策略/环境 API: - GET /api/live/config — 读取全部配置 - PUT /api/live/config — 批量更新配置 前端: - L1.5配置面板(L1止血和L2账户之间) - 标题栏醒目显示 1R = $X.XX - 7列网格: 图标+标签+值 - 编辑模式: 点编辑→改值→保存/取消 - 各项格式化: $前缀/%后缀/x后缀 --- backend/main.py | 26 ++++++++++++ frontend/app/live/page.tsx | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/backend/main.py b/backend/main.py index 91240b5..0cad1ac 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1887,3 +1887,29 @@ async def live_events( *params ) return {"count": len(rows), "data": rows} + + +# ============ Live Config (实盘配置) ============ + +@app.get("/api/live/config") +async def live_config_get(user: dict = Depends(get_current_user)): + """获取实盘配置""" + rows = await async_fetch("SELECT key, value, label, updated_at FROM live_config ORDER BY key") + config = {} + for r in rows: + config[r["key"]] = {"value": r["value"], "label": r["label"], "updated_at": str(r["updated_at"])} + return config + + +@app.put("/api/live/config") +async def live_config_update(request: Request, user: dict = Depends(get_current_user)): + """更新实盘配置""" + body = await request.json() + updated = [] + for key, value in body.items(): + await async_execute( + "UPDATE live_config SET value = $1, updated_at = NOW() WHERE key = $2", + str(value), key + ) + updated.append(key) + return {"updated": updated} diff --git a/frontend/app/live/page.tsx b/frontend/app/live/page.tsx index 8e8061f..fa3fcd0 100644 --- a/frontend/app/live/page.tsx +++ b/frontend/app/live/page.tsx @@ -110,6 +110,90 @@ function L1_EmergencyPanel() { ); } +// ═══════════════════════════════════════════════════════════════ +// L1.5: 实盘配置面板 +// ═══════════════════════════════════════════════════════════════ +function L15_LiveConfig() { + const [config, setConfig] = useState>({}); + const [editing, setEditing] = useState(false); + const [draft, setDraft] = useState>({}); + const [saving, setSaving] = useState(false); + + useEffect(() => { + const f = async () => { + try { + const r = await authFetch("/api/live/config"); + if (r.ok) { const d = await r.json(); setConfig(d); } + } catch {} + }; + f(); + }, []); + + const configOrder = ["risk_per_trade_usd", "initial_capital", "risk_pct", "max_positions", "leverage", "enabled_strategies", "trade_env"]; + const configIcons: Record = { + risk_per_trade_usd: "🎯", initial_capital: "💰", risk_pct: "📊", + max_positions: "📦", leverage: "⚡", enabled_strategies: "🧠", trade_env: "🌐" + }; + + const startEdit = () => { + const d: Record = {}; + for (const k of configOrder) { d[k] = config[k]?.value || ""; } + setDraft(d); setEditing(true); + }; + + const saveConfig = async () => { + setSaving(true); + try { + const r = await authFetch("/api/live/config", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(draft) }); + if (r.ok) { + const r2 = await authFetch("/api/live/config"); + if (r2.ok) setConfig(await r2.json()); + setEditing(false); + } + } catch {} finally { setSaving(false); } + }; + + const riskUsd = parseFloat(config.risk_per_trade_usd?.value || "2"); + + return ( +
+
+

+ ⚙️ 实盘配置 + 1R = ${riskUsd.toFixed(2)} +

+ {!editing ? ( + + ) : ( +
+ + +
+ )} +
+
+ {configOrder.map(k => { + const c = config[k]; + if (!c) return null; + return ( +
+
{configIcons[k]} {c.label}
+ {editing ? ( + setDraft({...draft, [k]: e.target.value})} + className="w-full text-center text-xs font-mono border border-slate-300 rounded px-1 py-0.5 focus:border-blue-500 focus:outline-none" /> + ) : ( +
+ {k === "risk_per_trade_usd" ? `$${c.value}` : k === "risk_pct" ? `${c.value}%` : k === "leverage" ? `${c.value}x` : c.value} +
+ )} +
+ ); + })} +
+
+ ); +} + // ═══════════════════════════════════════════════════════════════ // L2: 账户概览(8卡片) // ═══════════════════════════════════════════════════════════════ @@ -655,6 +739,7 @@ export default function LiveTradingPage() { +