实现OKX单笔期权币本位与USDT桥复利(中控只读,不改Gate)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
"""OKX 单笔期权本位模式与币本位 USDT 预算."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
|
||||
MODE_USDC = "usdc"
|
||||
MODE_COIN = "coin"
|
||||
|
||||
|
||||
def _env_bool(name: str, default: bool = False) -> bool:
|
||||
v = (os.getenv(name) or "").strip().lower()
|
||||
if not v:
|
||||
return default
|
||||
return v in ("1", "true", "yes", "on", "y")
|
||||
|
||||
|
||||
def _env_float(name: str, default: float) -> float:
|
||||
try:
|
||||
return float(os.getenv(name) or default)
|
||||
except (TypeError, ValueError):
|
||||
return float(default)
|
||||
|
||||
|
||||
def normalize_options_margin_mode(raw: Any = None) -> str:
|
||||
"""返回 usdc | coin."""
|
||||
if raw is None:
|
||||
raw = os.getenv("OKX_OPTIONS_MARGIN_MODE")
|
||||
v = str(raw or MODE_USDC).strip().lower()
|
||||
if v in ("coin", "coin_margin", "crypto", "crypto_margin", "币本位"):
|
||||
return MODE_COIN
|
||||
return MODE_USDC
|
||||
|
||||
|
||||
def is_coin_margin_mode(raw: Any = None) -> bool:
|
||||
return normalize_options_margin_mode(raw) == MODE_COIN
|
||||
|
||||
|
||||
def inst_family_for_underlying(underlying: str, *, margin_mode: str | None = None) -> str:
|
||||
u = (underlying or "ETH").strip().upper() or "ETH"
|
||||
mode = normalize_options_margin_mode(margin_mode)
|
||||
if mode == MODE_COIN:
|
||||
return f"{u}-USD"
|
||||
return f"{u}-USD_UM"
|
||||
|
||||
|
||||
def margin_mode_from_inst_id(inst_id: str) -> str:
|
||||
inst = (inst_id or "").strip().upper()
|
||||
if not inst:
|
||||
return normalize_options_margin_mode()
|
||||
if "_UM" in inst:
|
||||
return MODE_USDC
|
||||
# ETH-USD-260701-2500-C / BTC-USD-...
|
||||
if "-USD-" in inst and "_UM" not in inst:
|
||||
return MODE_COIN
|
||||
return normalize_options_margin_mode()
|
||||
|
||||
|
||||
def premium_ccy_for_mode(margin_mode: str, underlying: str = "ETH") -> str:
|
||||
if normalize_options_margin_mode(margin_mode) == MODE_COIN:
|
||||
return (underlying or "ETH").strip().upper() or "ETH"
|
||||
return "USDC"
|
||||
|
||||
|
||||
def spot_quote_inst_id(underlying: str) -> str:
|
||||
"""现货市价买卖: ETH-USDT / BTC-USDT."""
|
||||
u = (underlying or "ETH").strip().upper() or "ETH"
|
||||
return f"{u}-USDT"
|
||||
|
||||
|
||||
def compute_coin_budget_usdt(
|
||||
trading_usdt: float,
|
||||
*,
|
||||
compound: bool | None = None,
|
||||
buffer: float | None = None,
|
||||
fixed_budget_usdt: float | None = None,
|
||||
max_enabled: bool | None = None,
|
||||
max_usdt: float | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
币本位单笔 USDT 预算.
|
||||
复利开: trading_usdt × buffer; 复利关: fixed × buffer.
|
||||
上限开: min(..., max_usdt).
|
||||
"""
|
||||
bal = max(0.0, float(trading_usdt or 0))
|
||||
use_compound = _env_bool("OKX_OPTIONS_COIN_COMPOUND", True) if compound is None else bool(compound)
|
||||
buf = float(buffer) if buffer is not None else _env_float("OKX_OPTIONS_BUDGET_BUFFER", 0.95)
|
||||
if buf <= 0:
|
||||
buf = 0.95
|
||||
fixed = (
|
||||
float(fixed_budget_usdt)
|
||||
if fixed_budget_usdt is not None
|
||||
else _env_float("OKX_OPTIONS_COIN_BUDGET_USDT", 10.0)
|
||||
)
|
||||
if use_compound:
|
||||
raw = bal * buf
|
||||
source = "compound"
|
||||
else:
|
||||
raw = max(0.0, fixed) * buf
|
||||
source = "fixed"
|
||||
capped = False
|
||||
max_on = (
|
||||
_env_bool("OKX_OPTIONS_COIN_MAX_USDT_ENABLED", False)
|
||||
if max_enabled is None
|
||||
else bool(max_enabled)
|
||||
)
|
||||
max_n = (
|
||||
float(max_usdt)
|
||||
if max_usdt is not None
|
||||
else _env_float("OKX_OPTIONS_COIN_MAX_USDT", 50.0)
|
||||
)
|
||||
budget = raw
|
||||
if max_on and max_n > 0 and budget > max_n:
|
||||
budget = max_n
|
||||
capped = True
|
||||
return {
|
||||
"ok": budget > 0,
|
||||
"budget_usdt": round(budget, 8),
|
||||
"raw_usdt": round(raw, 8),
|
||||
"trading_usdt": round(bal, 8),
|
||||
"buffer": buf,
|
||||
"compound": use_compound,
|
||||
"source": source,
|
||||
"max_enabled": max_on,
|
||||
"max_usdt": max_n if max_on else None,
|
||||
"capped_by_max": capped,
|
||||
"msg": "" if budget > 0 else "交易账户 USDT 不足,无法计算币本位预算",
|
||||
}
|
||||
|
||||
|
||||
def calc_sheets_from_coin_balance(
|
||||
*,
|
||||
quote_per_unit: float,
|
||||
ct_mult: float,
|
||||
min_sz: int,
|
||||
coin_available: float,
|
||||
) -> dict[str, Any]:
|
||||
"""用可用标的币尽量开满(权利金以币计)."""
|
||||
import math
|
||||
|
||||
ask = float(quote_per_unit or 0)
|
||||
mult = float(ct_mult or 0.01)
|
||||
need = max(1, int(min_sz or 1))
|
||||
coin = max(0.0, float(coin_available or 0))
|
||||
if ask <= 0 or mult <= 0:
|
||||
return {"ok": False, "msg": "卖一价无效", "sheets": 0, "coin_premium": 0.0}
|
||||
per_sheet = ask * mult
|
||||
if per_sheet <= 0:
|
||||
return {"ok": False, "msg": "无法计算单张权利金(币)", "sheets": 0, "coin_premium": 0.0}
|
||||
sheets = int(math.floor((coin / per_sheet) + 1e-12))
|
||||
if sheets < need:
|
||||
return {
|
||||
"ok": False,
|
||||
"msg": f"可用币不足,无法买入 {need} 张(单张约 {per_sheet:.8g} 币,可用 {coin:g})",
|
||||
"sheets": sheets,
|
||||
"coin_premium": round(sheets * per_sheet, 8),
|
||||
"per_sheet_coin": per_sheet,
|
||||
}
|
||||
prem = sheets * per_sheet
|
||||
return {
|
||||
"ok": True,
|
||||
"msg": "",
|
||||
"sheets": sheets,
|
||||
"coin_premium": round(prem, 8),
|
||||
"per_sheet_coin": per_sheet,
|
||||
"eth_amount": round(sheets * mult, 8),
|
||||
}
|
||||
Reference in New Issue
Block a user