This commit is contained in:
dekun
2026-05-28 12:11:02 +08:00
parent 2965744c45
commit 6184e7a11f
3 changed files with 85 additions and 16 deletions
+2
View File
@@ -85,6 +85,8 @@ KEY_ALERT_INTERVAL_MINUTES=5
BALANCE_REFRESH_SECONDS=60
# 后台监控轮询周期(秒)
MONITOR_POLL_SECONDS=3
# 移动保本同步交易所止盈止损的最小间隔(秒),避免频繁撤挂叠单
BREAKEVEN_EXCHANGE_MIN_INTERVAL_SEC=60
# 使用可用资金时的缓冲比例(如0.98代表用98%)
FULL_MARGIN_BUFFER_RATIO=0.98
+20 -16
View File
@@ -47,7 +47,7 @@ from fib_key_monitor_lib import (
key_signal_type_for_trade_record,
stored_key_signal_type,
)
from okx_orders_lib import fetch_okx_all_open_orders
from okx_orders_lib import cancel_okx_all_open_orders, fetch_okx_all_open_orders
from journal_chart_lib import (
JOURNAL_CHART_DEFAULT_LIMIT,
JOURNAL_CHART_DEFAULT_TF1,
@@ -202,6 +202,10 @@ AUTO_TRANSFER_BJ_HOUR = int(os.getenv("AUTO_TRANSFER_BJ_HOUR", "8"))
WECHAT_TIMEOUT_SECONDS = int(os.getenv("WECHAT_TIMEOUT_SECONDS", "10"))
AI_TIMEOUT_SECONDS = int(os.getenv("AI_TIMEOUT_SECONDS", "120"))
MONITOR_POLL_SECONDS = int(os.getenv("MONITOR_POLL_SECONDS", "3"))
BREAKEVEN_EXCHANGE_MIN_INTERVAL_SEC = max(
15, int(os.getenv("BREAKEVEN_EXCHANGE_MIN_INTERVAL_SEC", "60"))
)
_BREAKEVEN_LAST_EX_SYNC: dict[int, float] = {}
KLINE_TIMEFRAME = os.getenv("KLINE_TIMEFRAME", "5m")
FULL_MARGIN_BUFFER_RATIO = float(os.getenv("FULL_MARGIN_BUFFER_RATIO", "0.98"))
TRANSFER_CCY = os.getenv("TRANSFER_CCY", "USDT")
@@ -2579,18 +2583,7 @@ def cancel_okx_swap_open_orders(exchange_symbol):
return
ensure_markets_loaded()
try:
exchange.cancel_all_orders(exchange_symbol)
except Exception:
pass
try:
for o in fetch_okx_all_open_orders(exchange, exchange_symbol):
oid = o.get("id")
if oid is None:
continue
try:
exchange.cancel_order(str(oid), exchange_symbol)
except Exception:
pass
cancel_okx_all_open_orders(exchange, exchange_symbol)
except Exception:
pass
@@ -2867,7 +2860,11 @@ def cancel_okx_tpsl_slot(exchange_symbol, slot):
if not oid:
return
ensure_markets_loaded()
exchange.cancel_order(str(oid), exchange_symbol)
cancel_id = str(oid).split(":", 1)[0]
try:
exchange.cancel_order(cancel_id, exchange_symbol, {"stop": True})
except Exception:
exchange.cancel_order(str(oid), exchange_symbol, {"stop": True})
def replace_active_monitor_tpsl_on_exchange(order_row, stop_loss, take_profit):
@@ -2877,7 +2874,9 @@ def replace_active_monitor_tpsl_on_exchange(order_row, stop_loss, take_profit):
raise RuntimeError(reason or "实盘未就绪")
ex_sym = resolve_monitor_exchange_symbol(order_row)
direction = order_row["direction"]
cancel_okx_swap_open_orders(ex_sym)
cancelled = cancel_okx_all_open_orders(exchange, ex_sym)
if cancelled > 0:
time.sleep(0.12)
pos_amt = get_live_position_contracts(ex_sym, direction)
if pos_amt is None or float(pos_amt) <= 0:
try:
@@ -4687,10 +4686,15 @@ def check_order_monitors():
tp_ex = float(take_profit or 0)
ok_live, _live_reason = ensure_okx_live_ready()
synced_ex = not ok_live
if ok_live and tp_ex > 0:
last_ex_sync = float(_BREAKEVEN_LAST_EX_SYNC.get(pid, 0))
interval_ok = (
time.time() - last_ex_sync
) >= BREAKEVEN_EXCHANGE_MIN_INTERVAL_SEC
if ok_live and tp_ex > 0 and interval_ok:
try:
replace_active_monitor_tpsl_on_exchange(r, new_sl, tp_ex)
synced_ex = True
_BREAKEVEN_LAST_EX_SYNC[pid] = time.time()
_clear_breakeven_exchange_warn(pid)
except Exception as e:
print(
+63
View File
@@ -14,6 +14,31 @@ def _order_dedupe_key(order: dict) -> str:
return str(order.get("id") or info.get("algoId") or info.get("ordId") or "")
def _okx_algo_cancel_id(order_id: str) -> str:
oid = str(order_id or "")
if ":" in oid:
return oid.split(":", 1)[0]
return oid
def _okx_order_needs_stop_cancel_param(order: dict) -> bool:
"""OKX 条件/算法单撤单须 params.stop=True,否则 cancel_order 走普通单接口会静默失败。"""
if not isinstance(order, dict):
return False
info = order.get("info") or {}
if not isinstance(info, dict):
info = {}
if order.get("stopLossPrice") is not None or order.get("takeProfitPrice") is not None:
return True
if info.get("algoId") or info.get("slTriggerPx") or info.get("tpTriggerPx"):
return True
typ = str(order.get("type") or info.get("ordType") or "").lower()
for token in ("conditional", "oco", "trigger", "move_order_stop", "iceberg"):
if token in typ:
return True
return False
def fetch_okx_all_open_orders(ex, exchange_symbol: str) -> list[dict]:
"""合并 OKX 普通挂单与算法挂单(去重)。"""
if not exchange_symbol:
@@ -51,3 +76,41 @@ def fetch_okx_all_open_orders(ex, exchange_symbol: str) -> list[dict]:
except Exception:
pass
return out
def cancel_okx_all_open_orders(ex, exchange_symbol: str) -> int:
"""
撤销某合约全部挂单(普通 + 条件/算法)。
OKX 止盈止损在 orders-algo-pending,必须用 stop=True 才能撤掉。
"""
if not exchange_symbol:
return 0
ex.load_markets()
sym = exchange_symbol
try:
sym = ex.market(exchange_symbol)["symbol"]
except Exception:
pass
n = 0
for o in fetch_okx_all_open_orders(ex, sym):
oid = _order_dedupe_key(o)
if not oid:
continue
cancel_id = _okx_algo_cancel_id(oid)
params = {"stop": True} if _okx_order_needs_stop_cancel_param(o) else None
try:
ex.cancel_order(cancel_id, sym, params)
n += 1
continue
except Exception:
pass
try:
ex.cancel_order(oid, sym, params)
n += 1
except Exception:
pass
try:
ex.cancel_all_orders(sym)
except Exception:
pass
return n