单独期权增加全仓复利模式:可选上限开关,仅允许一仓
用期权户全部可用×缓冲开仓,默认不设上限;开启上限后按 env 封顶,全仓时禁止已有持仓再开。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -103,6 +103,8 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
|
||||
"render_main_page": app_module.render_main_page,
|
||||
"trade_budget": _env_float("OKX_OPTIONS_TRADE_BUDGET_USDC", 10.0),
|
||||
"budget_buffer": _env_float("OKX_OPTIONS_BUDGET_BUFFER", 0.95),
|
||||
"compound_full_cap_enabled": _env_bool("OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", False),
|
||||
"compound_full_cap_usdc": _env_float("OKX_OPTIONS_COMPOUND_FULL_CAP_USDC", 300.0),
|
||||
"default_underly": (os.getenv("OKX_OPTIONS_DEFAULT_UNDERLY") or "ETH").strip().upper(),
|
||||
"max_dte_days": _env_float("OKX_OPTIONS_MAX_DTE_DAYS", 2.0),
|
||||
"chain_max_dte_days": _env_float("OKX_OPTIONS_CHAIN_MAX_DTE_DAYS", 14.0),
|
||||
@@ -174,6 +176,34 @@ def _budget_full_usdc(cfg: dict[str, Any], ex: Any) -> tuple[float | None, str]:
|
||||
return resolve_budget_full_usdc(trading, float(cap)), ""
|
||||
|
||||
|
||||
def _compound_full_usdc(cfg: dict[str, Any], ex: Any) -> tuple[float | None, str]:
|
||||
"""全仓复利 = 期权交易户可用(可选上限封顶);再由 calc_order_size × budget_buffer."""
|
||||
from lib.exchange.okx_options_lib import fetch_options_trading_usdc
|
||||
from lib.options.options_pricing_lib import resolve_compound_full_usdc
|
||||
|
||||
raw = fetch_options_trading_usdc(ex)
|
||||
if raw is None or float(raw) <= 0:
|
||||
return None, "交易账户 USDC 可用余额不足"
|
||||
trading = float(raw)
|
||||
# 额度热更读 env(与模板启动值无关)
|
||||
cap_on = _env_bool("OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED", False)
|
||||
cap_v = _env_float("OKX_OPTIONS_COMPOUND_FULL_CAP_USDC", 300.0)
|
||||
if cap_on and cap_v <= 0:
|
||||
return None, "全仓上限无效(OKX_OPTIONS_COMPOUND_FULL_CAP_USDC)"
|
||||
return (
|
||||
resolve_compound_full_usdc(
|
||||
trading,
|
||||
cap_enabled=cap_on,
|
||||
cap_usdc=cap_v,
|
||||
),
|
||||
"",
|
||||
)
|
||||
|
||||
|
||||
def _is_budget_mode(mode: str) -> bool:
|
||||
return mode in ("budget_full", "compound_full")
|
||||
|
||||
|
||||
def _open_premium_paid(cfg: dict[str, Any], inst_id: str) -> float | None:
|
||||
conn = cfg["get_db"]()
|
||||
try:
|
||||
@@ -440,6 +470,14 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
budget_cap = budget
|
||||
from lib.exchange.okx_options_lib import fetch_options_trading_usdc
|
||||
|
||||
available_usdc = fetch_options_trading_usdc(ex)
|
||||
elif mode == "compound_full":
|
||||
budget, budget_err = _compound_full_usdc(cfg, ex)
|
||||
if budget is None:
|
||||
return jsonify({"ok": False, "msg": budget_err})
|
||||
budget_cap = budget
|
||||
from lib.exchange.okx_options_lib import fetch_options_trading_usdc
|
||||
|
||||
available_usdc = fetch_options_trading_usdc(ex)
|
||||
eth_amount = None
|
||||
try:
|
||||
@@ -471,6 +509,7 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
},
|
||||
"available_usdc": available_usdc,
|
||||
"budget_full_usdc": budget if mode == "budget_full" else None,
|
||||
"compound_full_usdc": budget if mode == "compound_full" else None,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -507,6 +546,7 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
},
|
||||
"available_usdc": available_usdc,
|
||||
"budget_full_usdc": budget if mode == "budget_full" else None,
|
||||
"compound_full_usdc": budget if mode == "compound_full" else None,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -531,9 +571,39 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
},
|
||||
"available_usdc": available_usdc,
|
||||
"budget_full_usdc": budget if mode == "budget_full" else None,
|
||||
"compound_full_usdc": budget if mode == "compound_full" else None,
|
||||
}
|
||||
)
|
||||
from lib.options.options_position_limit_lib import option_position_limit_block_msg
|
||||
from lib.options.options_position_limit_lib import (
|
||||
compound_full_single_position_block_msg,
|
||||
option_position_limit_block_msg,
|
||||
)
|
||||
|
||||
if mode == "compound_full":
|
||||
compound_block = compound_full_single_position_block_msg(
|
||||
ex, fetch_positions=cfg.get("fetch_option_positions")
|
||||
)
|
||||
if compound_block:
|
||||
return jsonify(
|
||||
{
|
||||
**q,
|
||||
"ok": True,
|
||||
"can_open": False,
|
||||
"msg": compound_block,
|
||||
"quote_per_unit": ask,
|
||||
"premium_per_sheet": None,
|
||||
"sizing": {
|
||||
"ok": False,
|
||||
"msg": compound_block,
|
||||
"sheets": 0,
|
||||
"eth_amount": 0.0,
|
||||
"total_premium": 0.0,
|
||||
},
|
||||
"available_usdc": available_usdc,
|
||||
"budget_full_usdc": None,
|
||||
"compound_full_usdc": budget,
|
||||
}
|
||||
)
|
||||
|
||||
pos_limit_msg = option_position_limit_block_msg(
|
||||
ex,
|
||||
@@ -558,17 +628,18 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
},
|
||||
"available_usdc": available_usdc,
|
||||
"budget_full_usdc": budget if mode == "budget_full" else None,
|
||||
"compound_full_usdc": budget if mode == "compound_full" else None,
|
||||
}
|
||||
)
|
||||
sizing = calc_order_size(
|
||||
quote_per_unit=float(ask),
|
||||
ct_mult=float(ct_mult),
|
||||
min_sz=int(min_sz),
|
||||
budget_usdc=budget if mode == "budget_full" else None,
|
||||
budget_usdc=budget if _is_budget_mode(mode) else None,
|
||||
budget_buffer=cfg["budget_buffer"],
|
||||
eth_amount=eth_amount if mode == "eth_amount" else None,
|
||||
sheets=sheet_count if mode == "sheets" else None,
|
||||
budget_cap=budget_cap if mode in ("budget_full", "sheets", "eth_amount") else None,
|
||||
budget_cap=budget_cap if mode in ("budget_full", "compound_full", "sheets", "eth_amount") else None,
|
||||
)
|
||||
if sizing.get("ok"):
|
||||
capped, cap_msg = cap_option_buy_sheets_to_ask_depth(
|
||||
@@ -590,7 +661,7 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
ct_mult=float(ct_mult),
|
||||
min_sz=int(min_sz),
|
||||
sheets=capped,
|
||||
budget_cap=budget_cap if mode in ("budget_full", "sheets", "eth_amount") else None,
|
||||
budget_cap=budget_cap if mode in ("budget_full", "compound_full", "sheets", "eth_amount") else None,
|
||||
)
|
||||
if sizing.get("ok"):
|
||||
sizing["ask_depth_capped"] = True
|
||||
@@ -612,6 +683,7 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
"sizing": sizing,
|
||||
"available_usdc": available_usdc,
|
||||
"budget_full_usdc": budget if mode == "budget_full" else None,
|
||||
"compound_full_usdc": budget if mode == "compound_full" else None,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -672,7 +744,17 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
"ref_ask": q.get("ref_ask"),
|
||||
}
|
||||
)
|
||||
from lib.options.options_position_limit_lib import option_position_limit_block_msg
|
||||
from lib.options.options_position_limit_lib import (
|
||||
compound_full_single_position_block_msg,
|
||||
option_position_limit_block_msg,
|
||||
)
|
||||
|
||||
if mode == "compound_full":
|
||||
compound_block = compound_full_single_position_block_msg(
|
||||
ex, fetch_positions=cfg.get("fetch_option_positions")
|
||||
)
|
||||
if compound_block:
|
||||
return jsonify({"ok": False, "msg": compound_block, "can_open": False})
|
||||
|
||||
pos_limit_msg = option_position_limit_block_msg(
|
||||
ex,
|
||||
@@ -702,15 +784,20 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
if budget is None:
|
||||
return jsonify({"ok": False, "msg": budget_err})
|
||||
budget_cap = budget
|
||||
elif mode == "compound_full":
|
||||
budget, budget_err = _compound_full_usdc(cfg, ex)
|
||||
if budget is None:
|
||||
return jsonify({"ok": False, "msg": budget_err})
|
||||
budget_cap = budget
|
||||
sizing = calc_order_size(
|
||||
quote_per_unit=float(ask),
|
||||
ct_mult=ct_mult,
|
||||
min_sz=min_sz,
|
||||
budget_usdc=budget if mode == "budget_full" else None,
|
||||
budget_usdc=budget if _is_budget_mode(mode) else None,
|
||||
budget_buffer=cfg["budget_buffer"],
|
||||
eth_amount=eth_amount,
|
||||
sheets=sheet_count,
|
||||
budget_cap=budget_cap if mode in ("budget_full", "sheets", "eth_amount") else None,
|
||||
budget_cap=budget_cap if mode in ("budget_full", "compound_full", "sheets", "eth_amount") else None,
|
||||
)
|
||||
if not sizing.get("ok"):
|
||||
return jsonify({"ok": False, "msg": sizing.get("msg") or "张数计算失败", "sizing": sizing})
|
||||
|
||||
Reference in New Issue
Block a user