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() { +