币本位买币改为按最大可开张数×权利金×可配缓冲,不全额兑换USDT

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-20 14:33:48 +08:00
parent 4fb3be35ef
commit 87910ed71a
9 changed files with 309 additions and 63 deletions
+150
View File
@@ -134,6 +134,156 @@ def compute_coin_budget_usdt(
}
def normalize_coin_spot_buy_buffer(raw: Any = None) -> float:
"""
现货买入相对权利金的倍数缓冲.
env OKX_OPTIONS_COIN_SPOT_BUY_BUFFER 默认 1.10(=多买 10%).
也可写 0.10 表示 +10%.
"""
if raw is None:
v = _env_float("OKX_OPTIONS_COIN_SPOT_BUY_BUFFER", 1.10)
else:
try:
v = float(raw)
except (TypeError, ValueError):
v = 1.10
if v <= 0:
return 1.10
if v < 1.0:
return 1.0 + v
return v
def plan_coin_open_by_budget(
*,
quote_per_unit: float,
ct_mult: float,
min_sz: int,
budget_usdt: float,
index_px: float,
ask_sz: float | None = None,
spot_buy_buffer: float | None = None,
target_sheets: int | None = None,
) -> dict[str, Any]:
"""
先按预算/卖一估最大可开张数,再按「权利金 × 现货缓冲」算应买现货 USDT.
不全额把预算换成币.
"""
import math
from lib.exchange.okx_options_lib import cap_option_buy_sheets_to_ask_depth
ask = float(quote_per_unit or 0)
mult = float(ct_mult or 0.01)
need = max(1, int(min_sz or 1))
budget = max(0.0, float(budget_usdt or 0))
idx = float(index_px or 0)
buf = normalize_coin_spot_buy_buffer(spot_buy_buffer)
if ask <= 0 or mult <= 0:
return {"ok": False, "msg": "卖一价无效", "sheets": 0, "buy_usdt": 0.0}
if idx <= 0:
return {"ok": False, "msg": "缺少指数价,无法估算买币 USDT", "sheets": 0, "buy_usdt": 0.0}
if budget <= 0:
return {"ok": False, "msg": "USDT 预算无效", "sheets": 0, "buy_usdt": 0.0}
per_sheet_coin = ask * mult
# 每张开仓需买的币(含缓冲)及其约合 USDT
per_sheet_buy_coin = per_sheet_coin * buf
per_sheet_usdt = per_sheet_buy_coin * idx
if per_sheet_usdt <= 0:
return {"ok": False, "msg": "无法计算单张买币成本", "sheets": 0, "buy_usdt": 0.0}
max_by_budget = int(math.floor((budget / per_sheet_usdt) + 1e-12))
if target_sheets is not None:
try:
want = int(target_sheets)
except (TypeError, ValueError):
want = 0
if want < need:
return {
"ok": False,
"msg": f"指定张数无效(需≥{need})",
"sheets": 0,
"buy_usdt": 0.0,
"max_by_budget": max_by_budget,
}
sheets = min(want, max_by_budget)
if sheets < want:
return {
"ok": False,
"msg": (
f"预算约可开 {max_by_budget} 张(含现货缓冲×{buf:g}),"
f"不足指定 {want}"
),
"sheets": 0,
"buy_usdt": 0.0,
"max_by_budget": max_by_budget,
"spot_buy_buffer": buf,
}
else:
sheets = max_by_budget
capped, cap_msg = cap_option_buy_sheets_to_ask_depth(sheets, ask_sz, min_sz=need)
ask_depth_capped = False
if capped is None:
return {
"ok": False,
"msg": cap_msg or "卖一深度不足",
"sheets": 0,
"buy_usdt": 0.0,
"spot_buy_buffer": buf,
}
if int(capped) < sheets:
sheets = int(capped)
ask_depth_capped = True
if sheets < need:
return {
"ok": False,
"msg": (
f"预算不足,无法买入 {need}"
f"(单张约需 {per_sheet_usdt:.4f} USDT,含现货缓冲×{buf:g})"
),
"sheets": sheets,
"buy_usdt": 0.0,
"per_sheet_usdt": round(per_sheet_usdt, 8),
"spot_buy_buffer": buf,
"max_by_budget": max_by_budget,
}
premium_coin = sheets * per_sheet_coin
buy_coin = premium_coin * buf
buy_usdt = min(budget, buy_coin * idx)
# 再保险:向下对齐,避免浮点导致略超预算
buy_usdt = min(budget, round(buy_usdt, 8))
out = {
"ok": True,
"msg": "" if not ask_depth_capped else (cap_msg or f"已按卖一深度限制为 {sheets}"),
"sheets": sheets,
"eth_amount": round(sheets * mult, 8),
"coin_premium": round(premium_coin, 8),
"total_premium": round(premium_coin, 8),
"per_sheet_coin": per_sheet_coin,
"buy_coin": round(buy_coin, 8),
"buy_usdt": round(buy_usdt, 8),
"budget_usdt": round(budget, 8),
"spot_buy_buffer": buf,
"index_px": idx,
"max_by_budget": max_by_budget,
"ask_depth_capped": ask_depth_capped,
"est_note": (
f"按最大可开 {sheets} 张×卖一权利金×现货缓冲{buf:g}估买币;"
f"不全额兑换预算"
),
}
if target_sheets is not None:
out["est_note"] = (
f"指定 {sheets} 张×卖一权利金×现货缓冲{buf:g}估买币;不全额兑换"
)
out["target_sheets"] = int(target_sheets)
return out
def calc_sheets_from_coin_balance(
*,
quote_per_unit: float,