Show editable exit on live cards; price perp PnL via close-side book VWAP.
Align monitor profit-exit with the same book VWAP so card and auto-exit stay consistent. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""对冲计划出场条件:读取/校验/更新."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
def _sf(v: Any) -> Optional[float]:
|
||||
try:
|
||||
if v is None or v == "":
|
||||
return None
|
||||
return float(v)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def normalize_exit_mode(raw: Any, *, profit_target: Any = None) -> str:
|
||||
m = str(raw or "").strip().lower()
|
||||
if m in ("profit", "target"):
|
||||
return m
|
||||
if _sf(profit_target) is not None and float(profit_target) > 0:
|
||||
return "profit"
|
||||
return "target"
|
||||
|
||||
|
||||
def validate_exit_payload(body: dict[str, Any] | None) -> tuple[bool, str, dict[str, Any]]:
|
||||
"""校验改出场请求 → (ok, msg, fields_for_update)."""
|
||||
b = body or {}
|
||||
mode = normalize_exit_mode(b.get("exit_mode") or b.get("mode"))
|
||||
if mode == "profit":
|
||||
pt = _sf(b.get("profit_target_usdt"))
|
||||
if pt is None or pt <= 0:
|
||||
return False, "盈利金额须大于 0", {}
|
||||
return True, "", {
|
||||
"exit_mode": "profit",
|
||||
"profit_target_usdt": float(pt),
|
||||
}
|
||||
opt_pts = _sf(b.get("option_target_points"))
|
||||
perp_pts = _sf(b.get("perp_target_points"))
|
||||
if opt_pts is None or opt_pts <= 0:
|
||||
return False, "期权目标点数须大于 0", {}
|
||||
if perp_pts is None or perp_pts <= 0:
|
||||
return False, "永续目标点数须大于 0", {}
|
||||
return True, "", {
|
||||
"exit_mode": "target",
|
||||
"option_target_points": float(opt_pts),
|
||||
"perp_target_points": float(perp_pts),
|
||||
}
|
||||
|
||||
|
||||
def update_plan_exit(cfg: dict[str, Any], conn: Any, plan_id: int, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
"""更新进行中计划的出场条件."""
|
||||
from lib.hedge_plan.hedge_plan_db import get_plan, get_plan_legs, update_plan
|
||||
|
||||
plan = get_plan(conn, int(plan_id))
|
||||
if not plan:
|
||||
return {"ok": False, "msg": "计划不存在"}
|
||||
st = str(plan.get("status") or "").lower()
|
||||
legs = get_plan_legs(conn, int(plan_id))
|
||||
has_open = any(str(x.get("status") or "").lower() == "open" for x in legs)
|
||||
if st not in ("opening", "active", "partial", "watching") and not (st == "closed" and has_open):
|
||||
return {"ok": False, "msg": f"当前状态 {st or '—'} 不可改出场"}
|
||||
|
||||
ok, msg, fields = validate_exit_payload(body)
|
||||
if not ok:
|
||||
return {"ok": False, "msg": msg}
|
||||
|
||||
update_plan(conn, int(plan_id), **fields)
|
||||
plan2 = get_plan(conn, int(plan_id))
|
||||
return {
|
||||
"ok": True,
|
||||
"plan_id": int(plan_id),
|
||||
"msg": "出场条件已更新",
|
||||
"exit": {
|
||||
"mode": fields.get("exit_mode"),
|
||||
"profit_target_usdt": plan2.get("profit_target_usdt") if plan2 else fields.get("profit_target_usdt"),
|
||||
"option_target_points": plan2.get("option_target_points") if plan2 else fields.get("option_target_points"),
|
||||
"perp_target_points": plan2.get("perp_target_points") if plan2 else fields.get("perp_target_points"),
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user