币本位期权开仓/翻倍提醒/翻倍平仓/目标平仓/手动平仓微信推送:按ETH/BTC计价并补齐全平必发
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""OKX 期权开仓/平仓企业微信推送(必发,幂等落库标记)."""
|
||||
"""OKX 期权开仓/平仓企业微信推送(必发,幂等落库标记).支持 USDC / 币本位(ETH/BTC)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
@@ -23,6 +23,63 @@ def _opt_type_label(opt_type: Any) -> str:
|
||||
return t or "—"
|
||||
|
||||
|
||||
def resolve_options_premium_ccy(
|
||||
*,
|
||||
inst_id: str = "",
|
||||
underlying: str = "",
|
||||
premium_ccy: Any = None,
|
||||
margin_mode: Any = None,
|
||||
row: dict[str, Any] | None = None,
|
||||
) -> str:
|
||||
"""权利金计价币种:USDC 或 ETH/BTC."""
|
||||
raw = premium_ccy
|
||||
if (raw is None or str(raw).strip() == "") and row:
|
||||
raw = row.get("premium_ccy")
|
||||
ccy = str(raw or "").strip().upper()
|
||||
if ccy:
|
||||
return ccy
|
||||
try:
|
||||
from lib.options.options_margin_mode_lib import margin_mode_from_inst_id, premium_ccy_for_mode
|
||||
|
||||
mid = str(inst_id or (row or {}).get("inst_id") or "").strip()
|
||||
mode = margin_mode if margin_mode is not None else (row or {}).get("margin_mode")
|
||||
if mode is None and mid:
|
||||
mode = margin_mode_from_inst_id(mid)
|
||||
u = str(
|
||||
underlying
|
||||
or (row or {}).get("underlying")
|
||||
or (mid.split("-")[0] if mid else "ETH")
|
||||
or "ETH"
|
||||
).strip().upper() or "ETH"
|
||||
return premium_ccy_for_mode(str(mode or "usdc"), u)
|
||||
except Exception:
|
||||
return "USDC"
|
||||
|
||||
|
||||
def _amount_decimals(ccy: str) -> int:
|
||||
c = (ccy or "USDC").strip().upper()
|
||||
if c in ("ETH", "BTC"):
|
||||
return 6
|
||||
return 4
|
||||
|
||||
|
||||
def _mode_tag(*, inst_id: str = "", premium_ccy: str = "", margin_mode: Any = None) -> str:
|
||||
ccy = (premium_ccy or "").strip().upper()
|
||||
if ccy and ccy != "USDC":
|
||||
return "币本位"
|
||||
try:
|
||||
from lib.options.options_margin_mode_lib import is_coin_margin_mode, margin_mode_from_inst_id
|
||||
|
||||
mode = margin_mode
|
||||
if mode is None and inst_id:
|
||||
mode = margin_mode_from_inst_id(inst_id)
|
||||
if is_coin_margin_mode(mode):
|
||||
return "币本位"
|
||||
except Exception:
|
||||
pass
|
||||
return "USDC"
|
||||
|
||||
|
||||
def ensure_options_notify_columns(conn: sqlite3.Connection) -> None:
|
||||
for ddl in (
|
||||
"ALTER TABLE options_trades ADD COLUMN wechat_open_sent INTEGER DEFAULT 0",
|
||||
@@ -57,10 +114,21 @@ def build_options_open_message(
|
||||
target_index: Any = None,
|
||||
signal_note: str = "",
|
||||
trade_id: Any = None,
|
||||
premium_ccy: Any = None,
|
||||
margin_mode: Any = None,
|
||||
) -> str:
|
||||
ccy = resolve_options_premium_ccy(
|
||||
inst_id=inst_id,
|
||||
underlying=underlying,
|
||||
premium_ccy=premium_ccy,
|
||||
margin_mode=margin_mode,
|
||||
)
|
||||
d = _amount_decimals(ccy)
|
||||
mode = _mode_tag(inst_id=inst_id, premium_ccy=ccy, margin_mode=margin_mode)
|
||||
lines = [
|
||||
"【OKX期权·开仓】",
|
||||
f"账户:{account_label or 'OKX期权'}",
|
||||
f"本位:{mode}",
|
||||
]
|
||||
if trade_id is not None:
|
||||
lines.append(f"本地单号:#{trade_id}")
|
||||
@@ -69,8 +137,8 @@ def build_options_open_message(
|
||||
f"合约:{inst_id}",
|
||||
f"标的:{(underlying or '—')} · {_opt_type_label(opt_type)}",
|
||||
f"张数:{sheets if sheets is not None else '—'}",
|
||||
f"开仓报价:{_fmt(open_quote)} USDC",
|
||||
f"权利金:{_fmt(premium_paid)} USDC",
|
||||
f"开仓报价:{_fmt(open_quote, d)} {ccy}",
|
||||
f"权利金:{_fmt(premium_paid, d)} {ccy}",
|
||||
]
|
||||
)
|
||||
if target_index is not None and str(target_index).strip() != "":
|
||||
@@ -98,10 +166,21 @@ def build_options_close_message(
|
||||
target_index: Any = None,
|
||||
trigger_idx: Any = None,
|
||||
trade_id: Any = None,
|
||||
premium_ccy: Any = None,
|
||||
margin_mode: Any = None,
|
||||
) -> str:
|
||||
ccy = resolve_options_premium_ccy(
|
||||
inst_id=inst_id,
|
||||
underlying=underlying,
|
||||
premium_ccy=premium_ccy,
|
||||
margin_mode=margin_mode,
|
||||
)
|
||||
d = _amount_decimals(ccy)
|
||||
mode = _mode_tag(inst_id=inst_id, premium_ccy=ccy, margin_mode=margin_mode)
|
||||
lines = [
|
||||
"【OKX期权·平仓】",
|
||||
f"账户:{account_label or 'OKX期权'}",
|
||||
f"本位:{mode}",
|
||||
]
|
||||
if trade_id is not None:
|
||||
lines.append(f"本地单号:#{trade_id}")
|
||||
@@ -111,9 +190,9 @@ def build_options_close_message(
|
||||
f"标的:{(underlying or '—')} · {_opt_type_label(opt_type)}",
|
||||
f"原因:{(reason or '平仓').strip()}",
|
||||
f"张数:{sheets if sheets is not None else '—'}",
|
||||
f"平仓报价:{_fmt(close_quote)} USDC",
|
||||
f"已付/收回:{_fmt(premium_paid)} / {_fmt(premium_received)} USDC",
|
||||
f"实现盈亏:{_fmt(realized_pnl, 4)} USDC",
|
||||
f"平仓报价:{_fmt(close_quote, d)} {ccy}",
|
||||
f"已付/收回:{_fmt(premium_paid, d)} / {_fmt(premium_received, d)} {ccy}",
|
||||
f"实现盈亏:{_fmt(realized_pnl, d)} {ccy}",
|
||||
]
|
||||
)
|
||||
if target_index is not None and str(target_index).strip() != "":
|
||||
@@ -142,15 +221,26 @@ def notify_options_open(
|
||||
open_quote: Any = None,
|
||||
target_index: Any = None,
|
||||
signal_note: str = "",
|
||||
premium_ccy: Any = None,
|
||||
margin_mode: Any = None,
|
||||
) -> bool:
|
||||
ensure_options_notify_columns(conn) if conn is not None else None
|
||||
row_ccy = premium_ccy
|
||||
row_mode = margin_mode
|
||||
if conn is not None and trade_id is not None:
|
||||
row = conn.execute(
|
||||
"SELECT wechat_open_sent FROM options_trades WHERE id=?",
|
||||
"SELECT wechat_open_sent, premium_ccy, margin_mode, underlying FROM options_trades WHERE id=?",
|
||||
(int(trade_id),),
|
||||
).fetchone()
|
||||
if row and int(row["wechat_open_sent"] or 0):
|
||||
return False
|
||||
if row:
|
||||
if row_ccy is None:
|
||||
row_ccy = row["premium_ccy"] if "premium_ccy" in row.keys() else None
|
||||
if row_mode is None:
|
||||
row_mode = row["margin_mode"] if "margin_mode" in row.keys() else None
|
||||
if not underlying:
|
||||
underlying = str(row["underlying"] or "") if "underlying" in row.keys() else underlying
|
||||
msg = build_options_open_message(
|
||||
account_label=str(cfg.get("account_label") or "OKX期权"),
|
||||
inst_id=inst_id,
|
||||
@@ -162,6 +252,8 @@ def notify_options_open(
|
||||
target_index=target_index,
|
||||
signal_note=signal_note,
|
||||
trade_id=trade_id,
|
||||
premium_ccy=row_ccy,
|
||||
margin_mode=row_mode,
|
||||
)
|
||||
ok = notify_options_send(cfg, msg)
|
||||
if ok and conn is not None and trade_id is not None:
|
||||
@@ -197,6 +289,8 @@ def notify_options_close(
|
||||
close_quote: Any = None,
|
||||
target_index: Any = None,
|
||||
trigger_idx: Any = None,
|
||||
premium_ccy: Any = None,
|
||||
margin_mode: Any = None,
|
||||
force: bool = False,
|
||||
) -> bool:
|
||||
"""平仓必发.默认按 trade_id / 同合约未标记行幂等."""
|
||||
@@ -230,6 +324,18 @@ def notify_options_close(
|
||||
).fetchone()
|
||||
if q2:
|
||||
rows = [dict(q2)]
|
||||
if not rows:
|
||||
# 已有平仓记录且均已推送:幂等跳过,避免再走「无库行」重复推
|
||||
exists = conn.execute(
|
||||
"""
|
||||
SELECT 1 FROM options_trades
|
||||
WHERE inst_id=? AND status='closed'
|
||||
LIMIT 1
|
||||
""",
|
||||
(inst_id,),
|
||||
).fetchone()
|
||||
if exists:
|
||||
return False
|
||||
|
||||
if rows:
|
||||
# 同次平仓可能多腿:合并一条推送,逐条标记
|
||||
@@ -259,6 +365,8 @@ def notify_options_close(
|
||||
target_index=target_index,
|
||||
trigger_idx=trigger_idx,
|
||||
trade_id=head.get("id") if len(rows) == 1 else None,
|
||||
premium_ccy=premium_ccy or head.get("premium_ccy"),
|
||||
margin_mode=margin_mode or head.get("margin_mode"),
|
||||
)
|
||||
ok = notify_options_send(cfg, msg)
|
||||
if ok and conn is not None:
|
||||
@@ -288,6 +396,8 @@ def notify_options_close(
|
||||
target_index=target_index,
|
||||
trigger_idx=trigger_idx,
|
||||
trade_id=trade_id,
|
||||
premium_ccy=premium_ccy,
|
||||
margin_mode=margin_mode,
|
||||
)
|
||||
return notify_options_send(cfg, msg)
|
||||
|
||||
@@ -327,4 +437,6 @@ def notify_options_close_trade_ids(
|
||||
premium_received=sum(float(r["premium_received"] or 0) for r in rows if r["premium_received"] is not None),
|
||||
realized_pnl=sum(float(r["realized_pnl"]) for r in rows if r["realized_pnl"] is not None),
|
||||
close_quote=first.get("close_quote"),
|
||||
premium_ccy=first.get("premium_ccy"),
|
||||
margin_mode=first.get("margin_mode"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user