条件单未触发至周四23:00自动取消,下发/取消/开仓均推企业微信。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,10 +4,11 @@ from __future__ import annotations
|
||||
import json
|
||||
import sqlite3
|
||||
import time
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any
|
||||
|
||||
# armed:待命穿越; triggering:已穿越等偏离区内开仓; open:持仓中;
|
||||
# done:终态(leave_up/leave_down/cancel/closed)
|
||||
# done:终态(leave_up/leave_down/cancel/timeout/closed)
|
||||
ACTIVE_STATUSES = ("armed", "triggering", "open")
|
||||
|
||||
|
||||
@@ -87,6 +88,78 @@ def _now() -> str:
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
def _notify_cond(
|
||||
send_wechat: Any,
|
||||
*,
|
||||
title: str,
|
||||
order: dict[str, Any],
|
||||
account_label: str = "",
|
||||
extra: list[str] | None = None,
|
||||
) -> None:
|
||||
if not callable(send_wechat) or not order:
|
||||
return
|
||||
try:
|
||||
lines = [
|
||||
title,
|
||||
f"账户:{account_label or 'OKX期权'}",
|
||||
f"单号:{order.get('hub_order_id') or '—'}",
|
||||
f"标的:{order.get('underlying') or '—'} {order.get('opt_type') or '—'}",
|
||||
f"触发价:{order.get('trigger_px') if order.get('trigger_px') is not None else '—'}",
|
||||
]
|
||||
for line in extra or []:
|
||||
if line:
|
||||
lines.append(line)
|
||||
send_wechat("\n".join(lines))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def next_thursday_2300(created_at: Any) -> datetime | None:
|
||||
"""下发后第一个周四 23:00(业务时区).周四 23:00 之后下发的顺延到下周四."""
|
||||
from lib.options.options_time_lib import options_app_tz, options_wall_clock_to_ms
|
||||
|
||||
ms = options_wall_clock_to_ms(created_at)
|
||||
if ms is None:
|
||||
return None
|
||||
tz = options_app_tz()
|
||||
dt = datetime.fromtimestamp(ms / 1000.0, tz=timezone.utc).astimezone(tz)
|
||||
days = (3 - dt.weekday()) % 7
|
||||
cand = dt.replace(hour=23, minute=0, second=0, microsecond=0) + timedelta(days=days)
|
||||
if cand <= dt:
|
||||
cand += timedelta(days=7)
|
||||
return cand
|
||||
|
||||
|
||||
def expire_untriggered_cond_orders(
|
||||
conn: sqlite3.Connection,
|
||||
*,
|
||||
send_wechat: Any = None,
|
||||
account_label: str = "",
|
||||
now: datetime | None = None,
|
||||
) -> int:
|
||||
"""仅取消仍待命(未触发)的条件单;持仓/触发中不动."""
|
||||
from lib.options.options_time_lib import options_app_tz
|
||||
|
||||
now_dt = now or datetime.now(options_app_tz())
|
||||
n = 0
|
||||
for order in list_cond_orders(conn, active_only=True):
|
||||
if str(order.get("status") or "") != "armed":
|
||||
continue
|
||||
deadline = next_thursday_2300(order.get("created_at"))
|
||||
if deadline is None or now_dt < deadline:
|
||||
continue
|
||||
_end_order(
|
||||
conn,
|
||||
order,
|
||||
end_reason="timeout",
|
||||
message="周四23:00未触发自动取消",
|
||||
send_wechat=send_wechat,
|
||||
account_label=account_label,
|
||||
)
|
||||
n += 1
|
||||
return n
|
||||
|
||||
|
||||
def _row_to_dict(row: sqlite3.Row | None) -> dict[str, Any] | None:
|
||||
if row is None:
|
||||
return None
|
||||
@@ -156,7 +229,13 @@ def machine_has_option_position(cfg: dict[str, Any], ex: Any) -> bool:
|
||||
return count_live_option_positions(raw) > 0
|
||||
|
||||
|
||||
def create_cond_order(conn: sqlite3.Connection, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
def create_cond_order(
|
||||
conn: sqlite3.Connection,
|
||||
payload: dict[str, Any],
|
||||
*,
|
||||
send_wechat: Any = None,
|
||||
account_label: str = "",
|
||||
) -> dict[str, Any]:
|
||||
"""接收中控下发;同机仅允许一张活跃条件单."""
|
||||
ensure_cond_order_table(conn)
|
||||
hub_order_id = (payload.get("hub_order_id") or "").strip()
|
||||
@@ -224,6 +303,18 @@ def create_cond_order(conn: sqlite3.Connection, payload: dict[str, Any]) -> dict
|
||||
)
|
||||
conn.commit()
|
||||
order = get_cond_order_by_hub_id(conn, hub_order_id)
|
||||
_notify_cond(
|
||||
send_wechat,
|
||||
title="【OKX期权·条件单下发】",
|
||||
order=order or {},
|
||||
account_label=account_label,
|
||||
extra=[
|
||||
f"方向:{'Put 上穿' if opt_type == 'P' else 'Call 下穿'}",
|
||||
f"偏离:{deviation_pct:g}%",
|
||||
f"最短到期:{float(min_expiry_hours):g}h",
|
||||
"状态:待命",
|
||||
],
|
||||
)
|
||||
return {"ok": True, "msg": "条件单已 armed", "order": order}
|
||||
|
||||
|
||||
@@ -435,20 +526,20 @@ def _end_order(
|
||||
"leave_up": "上破偏离区结束",
|
||||
"leave_down": "下破偏离区结束",
|
||||
"cancel": "中控取消",
|
||||
"timeout": "周四23:00未触发自动取消",
|
||||
"closed": "出场完成",
|
||||
}
|
||||
lines = [
|
||||
"【OKX期权·条件单结束】",
|
||||
f"账户:{account_label or 'OKX期权'}",
|
||||
f"单号:{order.get('hub_order_id')}",
|
||||
f"标的:{order.get('underlying')} {order.get('opt_type')}",
|
||||
f"触发价:{order.get('trigger_px')}",
|
||||
f"原因:{reason_map.get(end_reason, end_reason)}",
|
||||
f"说明:{message}",
|
||||
]
|
||||
if index_px is not None:
|
||||
lines.append(f"当前指数:{index_px:g}")
|
||||
send_wechat("\n".join(lines))
|
||||
_notify_cond(
|
||||
send_wechat,
|
||||
title="【OKX期权·条件单结束】",
|
||||
order=order,
|
||||
account_label=account_label,
|
||||
extra=[
|
||||
f"原因:{reason_map.get(end_reason, end_reason)}",
|
||||
f"说明:{message}",
|
||||
]
|
||||
+ ([f"当前指数:{index_px:g}"] if index_px is not None else []),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -583,8 +674,11 @@ def run_cond_orders_tick(
|
||||
send_wechat: Any = None,
|
||||
account_label: str = "",
|
||||
) -> None:
|
||||
"""监控循环单步:穿越/偏离区/开仓/出场收尾."""
|
||||
"""监控循环单步:周四未触发取消 / 穿越 / 偏离区 / 开仓 / 出场收尾."""
|
||||
ensure_cond_order_table(conn)
|
||||
expire_untriggered_cond_orders(
|
||||
conn, send_wechat=send_wechat, account_label=account_label
|
||||
)
|
||||
ex = cfg.get("exchange_options")
|
||||
if ex is None:
|
||||
return
|
||||
@@ -747,8 +841,18 @@ def _tick_one(
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
# 确保出场规则挂上
|
||||
order2 = get_cond_order_by_hub_id(conn, order["hub_order_id"]) or order
|
||||
opened = get_cond_order_by_hub_id(conn, order["hub_order_id"]) or order
|
||||
_notify_cond(
|
||||
send_wechat,
|
||||
title="【OKX期权·条件单开仓】",
|
||||
order=opened,
|
||||
account_label=account_label,
|
||||
extra=[
|
||||
f"合约:{inst_id}",
|
||||
f"说明:已开仓 {inst_id}",
|
||||
],
|
||||
)
|
||||
order2 = opened
|
||||
order2["opened_inst_id"] = inst_id
|
||||
_apply_exit_to_position(
|
||||
conn,
|
||||
|
||||
Reference in New Issue
Block a user