永期手动下单:修币本位杠杆显示,张数定仓与双出场模式.

右侧以张数+比例算永续,杠杆取 env(BTC10/ETH5);开仓买币+5%滑点;出场可选盈利金额(扣双边费)或目标位;平仓后卖光币回 U.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-14 15:43:44 +08:00
parent e474c89c35
commit d68e9c3946
8 changed files with 392 additions and 63 deletions
+49 -16
View File
@@ -204,7 +204,10 @@ def _buy_option(
)
if idx <= 0:
return {"ok": False, "msg": "缺少指数价,无法估算买币 USDT", "quote": q}
buf = normalize_coin_spot_buy_buffer(None)
# 权利金币×缓冲买币;默认 +5%滑点(1.05),可经 cfg.spot_buy_buffer / env 覆盖
buf = normalize_coin_spot_buy_buffer(
cfg.get("spot_buy_buffer") if cfg.get("spot_buy_buffer") is not None else 1.05
)
need_coin = float(premium) * float(buf)
avail = float(fetch_trading_coin_available(ex, underlying) or 0)
if avail + 1e-12 < need_coin:
@@ -774,28 +777,46 @@ def refresh_po_option_quote_before_start(cfg: dict[str, Any], body: dict[str, An
cs = float(get_cs(sym) or cs)
except Exception:
pass
sized = size_from_premium(
premium_budget=float(body.get("premium_budget") or 0),
ask=float(body["ask"]),
ct_mult=float(body.get("ct_mult") or 0.01),
ratio=float(body.get("option_perp_ratio") or 2),
contract_size=cs,
)
if not sized.get("ok"):
return {"ok": False, "msg": sized.get("msg") or "定仓失败", "quote": q, "sizing": sized}
body["sheets"] = sized["sheets"]
body["contracts"] = sized["contracts"]
body["eth_qty"] = sized["eth_qty"]
market = body.get("market_order") in (True, 1, "1", "true", "yes", "on")
ct = float(body.get("ct_mult") or q.get("ct_mult") or 0.01)
ratio = float(body.get("option_perp_ratio") or 2)
body["contract_size"] = cs
if market and float(body.get("sheets") or 0) > 0:
# 手动下单:以张数为准,永续按比例
sheets_i = float(int(float(body["sheets"])))
eth = round(sheets_i * ct, 2)
body["sheets"] = sheets_i
body["eth_qty"] = eth
body["contracts"] = (eth / ratio) / cs if ratio > 0 and cs > 0 else 0
sized = {
"ok": True,
"sheets": sheets_i,
"eth_qty": eth,
"contracts": body["contracts"],
"ratio": ratio,
}
else:
sized = size_from_premium(
premium_budget=float(body.get("premium_budget") or 0),
ask=float(body["ask"]),
ct_mult=ct,
ratio=ratio,
contract_size=cs,
)
if not sized.get("ok"):
return {"ok": False, "msg": sized.get("msg") or "定仓失败", "quote": q, "sizing": sized}
body["sheets"] = sized["sheets"]
body["contracts"] = sized["contracts"]
body["eth_qty"] = sized["eth_qty"]
# 深度不足则缩量
ask_sz = float(q.get("ask_sz") or 0)
if ask_sz > 0 and float(body["sheets"]) > ask_sz:
body["sheets"] = float(int(ask_sz))
if body["sheets"] <= 0:
return {"ok": False, "msg": "卖一深度不足 1 张", "quote": q, "sizing": sized}
eth = round(float(body["sheets"]) * float(body.get("ct_mult") or 0.01), 2)
eth = round(float(body["sheets"]) * ct, 2)
body["eth_qty"] = eth
body["contracts"] = (eth / float(body.get("option_perp_ratio") or 2)) / cs
body["contracts"] = (eth / ratio) / cs if ratio > 0 and cs > 0 else 0
return {
"ok": True,
"ask": float(q["ask"]),
@@ -806,8 +827,13 @@ def refresh_po_option_quote_before_start(cfg: dict[str, Any], body: dict[str, An
"sizing": sized,
"quote": q,
"msg": (
f"期权为主定仓: 权利金×0.95→{body.get('eth_qty')}ETH / "
f"手动定仓: {body.get('eth_qty')}币 / "
f"{body.get('sheets')}张期权 / {float(body.get('contracts') or 0):.4f}张永续 @{q['ask']}"
if market
else (
f"期权为主定仓: 权利金×0.95→{body.get('eth_qty')}ETH / "
f"{body.get('sheets')}张期权 / {float(body.get('contracts') or 0):.4f}张永续 @{q['ask']}"
)
),
}
return {
@@ -827,6 +853,13 @@ def execute_perp_options_start(
dry_run: bool = False,
persist: Optional[Callable[..., Any]] = None,
) -> dict[str, Any]:
# 买币滑点缓冲:手动单默认 +5%
if body.get("spot_buy_buffer") is not None:
cfg = dict(cfg)
cfg["spot_buy_buffer"] = body.get("spot_buy_buffer")
elif body.get("market_order") in (True, 1, "1", "true", "yes", "on"):
cfg = dict(cfg)
cfg["spot_buy_buffer"] = 1.05
refresh = refresh_po_option_quote_before_start(cfg, body)
if not refresh.get("ok"):
return {"ok": False, "msg": refresh.get("msg") or "启动前刷新卖一失败", "refresh": refresh}