feat(hub): trend plan breakeven and stop from monitor fullscreen
Proxy /api/hub/trend/stop and breakeven to instances; enable offset input and actions in hub UI. Add horizontal padding on strategy records page. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1124,6 +1124,77 @@ class PlaceTpslBody(BaseModel):
|
||||
contracts: float | None = None
|
||||
|
||||
|
||||
class TrendPlanActionBody(BaseModel):
|
||||
plan_id: int
|
||||
breakeven_offset_pct: float | None = None
|
||||
|
||||
|
||||
def _flask_hub_messages(parsed: dict | None) -> tuple[bool, str]:
|
||||
if not isinstance(parsed, dict):
|
||||
return False, "实例返回无效"
|
||||
msgs = list(parsed.get("messages") or [])
|
||||
if parsed.get("msg"):
|
||||
msgs.insert(0, str(parsed["msg"]))
|
||||
if parsed.get("error"):
|
||||
msgs.append(str(parsed["error"]))
|
||||
ok = parsed.get("ok") is not False
|
||||
if parsed.get("ok") is True:
|
||||
ok = True
|
||||
elif parsed.get("ok") is False:
|
||||
ok = False
|
||||
else:
|
||||
for m in msgs:
|
||||
if any(
|
||||
k in str(m)
|
||||
for k in ("失败", "错误", "无法", "缺少", "过期", "未找到", "不允许", "异常")
|
||||
):
|
||||
ok = False
|
||||
break
|
||||
text = ";".join(str(x) for x in msgs if x) or ("成功" if ok else "操作失败")
|
||||
return ok, text
|
||||
|
||||
|
||||
@app.post("/api/trend/{exchange_id}/stop")
|
||||
async def api_trend_plan_stop(exchange_id: str, body: TrendPlanActionBody):
|
||||
ex = _find_exchange(exchange_id)
|
||||
if not ex or not ex.get("enabled"):
|
||||
raise HTTPException(status_code=404, detail="账户未启用")
|
||||
if "trend" not in (ex.get("capabilities") or []):
|
||||
raise HTTPException(status_code=400, detail="该账户未启用趋势计划监控")
|
||||
pid = int(body.plan_id)
|
||||
async with httpx.AsyncClient() as client:
|
||||
parsed = await _fetch_flask_json(
|
||||
client, ex, f"/api/hub/trend/stop/{pid}", method="POST"
|
||||
)
|
||||
ok, text = _flask_hub_messages(parsed)
|
||||
_schedule_board_refresh()
|
||||
return {"ok": ok, "message": text, "payload": parsed}
|
||||
|
||||
|
||||
@app.post("/api/trend/{exchange_id}/breakeven")
|
||||
async def api_trend_plan_breakeven(exchange_id: str, body: TrendPlanActionBody):
|
||||
ex = _find_exchange(exchange_id)
|
||||
if not ex or not ex.get("enabled"):
|
||||
raise HTTPException(status_code=404, detail="账户未启用")
|
||||
if "trend" not in (ex.get("capabilities") or []):
|
||||
raise HTTPException(status_code=400, detail="该账户未启用趋势计划监控")
|
||||
pid = int(body.plan_id)
|
||||
data = {}
|
||||
if body.breakeven_offset_pct is not None:
|
||||
data["breakeven_offset_pct"] = str(body.breakeven_offset_pct)
|
||||
async with httpx.AsyncClient() as client:
|
||||
parsed = await _fetch_flask_json(
|
||||
client,
|
||||
ex,
|
||||
f"/api/hub/trend/breakeven/{pid}",
|
||||
method="POST",
|
||||
data=data,
|
||||
)
|
||||
ok, text = _flask_hub_messages(parsed)
|
||||
_schedule_board_refresh()
|
||||
return {"ok": ok, "message": text, "payload": parsed}
|
||||
|
||||
|
||||
@app.post("/api/orders/{exchange_id}/cancel")
|
||||
async def api_cancel_order(exchange_id: str, body: CancelOrderBody):
|
||||
ex = _find_exchange(exchange_id)
|
||||
|
||||
Reference in New Issue
Block a user