Files
crypto_monitor/lib/options/options_monitor_lib.py
T

139 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""期权持仓监控:浮盈翻倍微信提醒。"""
from __future__ import annotations
import sqlite3
import time
from typing import Any, Callable
def _safe_float(v: Any) -> float | None:
if v is None:
return None
try:
return float(v)
except (TypeError, ValueError):
return None
def build_profit_alert_message(
*,
account_label: str,
inst_id: str,
premium_paid: float,
upl: float,
upl_ratio: float | None,
bid: float | None,
) -> str:
pct = f"{upl_ratio * 100:.1f}%" if upl_ratio is not None else ""
bid_txt = f"{bid:.4f}" if bid is not None else ""
return "\n".join(
[
"【OKX期权·翻倍提醒】",
f"账户:{account_label}",
f"合约:{inst_id}",
f"已付权利金:{premium_paid:.4f} USDC",
f"未实现盈亏:{upl:+.4f} USDC{pct}",
f"当前买一:{bid_txt}(可考虑限价平仓锁利)",
]
)
def run_options_profit_alerts(
conn: sqlite3.Connection,
positions: list[dict[str, Any]],
*,
profit_ratio: float,
send_wechat: Callable[[str], None],
account_label: str,
ticker_bid_fn: Callable[[str], float | None],
) -> int:
"""
对比 DB 中 open 记录与交易所持仓;达到阈值发微信。
返回发送条数。
"""
sent = 0
pos_by_inst = {str(p.get("inst_id") or p.get("instId") or ""): p for p in positions}
rows = conn.execute(
"""
SELECT id, inst_id, premium_paid, profit_alert_sent
FROM options_trades
WHERE status = 'open'
"""
).fetchall()
for row in rows:
if int(row["profit_alert_sent"] or 0):
continue
inst_id = str(row["inst_id"] or "")
prem = _safe_float(row["premium_paid"])
if not inst_id or prem is None or prem <= 0:
continue
pos = pos_by_inst.get(inst_id)
if not pos:
continue
upl = _safe_float(pos.get("upl"))
upl_ratio = _safe_float(pos.get("upl_ratio_pct"))
if upl_ratio is not None:
ratio = upl_ratio / 100.0
elif upl is not None:
ratio = upl / prem
else:
continue
if ratio < float(profit_ratio):
continue
bid = ticker_bid_fn(inst_id)
msg = build_profit_alert_message(
account_label=account_label,
inst_id=inst_id,
premium_paid=prem,
upl=upl or 0.0,
upl_ratio=ratio,
bid=bid,
)
try:
send_wechat(msg)
conn.execute(
"UPDATE options_trades SET profit_alert_sent = 1 WHERE id = ?",
(int(row["id"]),),
)
sent += 1
except Exception:
pass
return sent
def options_monitor_loop(
*,
enabled: bool,
poll_seconds: float,
get_db: Callable[[], sqlite3.Connection],
fetch_positions: Callable[[], list[dict[str, Any]]],
ticker_bid_fn: Callable[[str], float | None],
send_wechat: Callable[[str], None],
account_label: str,
profit_ratio: float,
stop_event: Any = None,
) -> None:
if not enabled:
return
while True:
if stop_event is not None and getattr(stop_event, "is_set", lambda: False)():
break
try:
conn = get_db()
try:
positions = fetch_positions()
run_options_profit_alerts(
conn,
positions,
profit_ratio=profit_ratio,
send_wechat=send_wechat,
account_label=account_label,
ticker_bid_fn=ticker_bid_fn,
)
conn.commit()
finally:
conn.close()
except Exception:
pass
time.sleep(max(5.0, float(poll_seconds)))