修复okx止盈止损

This commit is contained in:
dekun
2026-05-25 11:19:33 +08:00
parent 4e8498f736
commit 436d2aef6c
3 changed files with 85 additions and 14 deletions
+60 -6
View File
@@ -21,6 +21,22 @@ def _coerce_float(*values) -> float | None:
return None
def _symbol_base_coin(symbol: str) -> str:
"""ZEC/USDT:USDT、ZEC-USDT-SWAP 等统一为标的币 ZEC。"""
s = (symbol or "").strip().upper()
if not s:
return ""
if "-SWAP" in s:
s = s.replace("-SWAP", "")
if "-" in s:
return s.split("-", 1)[0]
if "/" in s:
return s.split("/", 1)[0]
if ":" in s:
return s.split(":", 1)[0]
return s
def symbols_match(position_symbol: str, order_symbol: str) -> bool:
a = (position_symbol or "").strip().upper()
b = (order_symbol or "").strip().upper()
@@ -28,6 +44,9 @@ def symbols_match(position_symbol: str, order_symbol: str) -> bool:
return False
if a == b:
return True
ba, bb = _symbol_base_coin(a), _symbol_base_coin(b)
if ba and bb and ba == bb:
return True
for suf in (":USDT", "/USDT:USDT", "/USDT"):
a2 = a.replace(suf, "")
b2 = b.replace(suf, "")
@@ -54,7 +73,7 @@ def _is_conditional_type(typ: str) -> bool:
t = (typ or "").upper()
if not t:
return False
keys = ("STOP", "TAKE_PROFIT", "TRAIL", "TRIGGER", "CONDITIONAL")
keys = ("STOP", "TAKE_PROFIT", "TRAIL", "TRIGGER", "CONDITIONAL", "OCO")
return any(k in t for k in keys)
@@ -90,7 +109,7 @@ def _normalize_raw_order(order: dict, *, channel: str) -> dict[str, Any] | None:
oid = order.get("id") or info.get("algoId") or info.get("orderId") or info.get("ordId")
if oid is None:
return None
sym = str(order.get("symbol") or info.get("symbol") or "")
sym = str(order.get("symbol") or info.get("symbol") or info.get("instId") or "")
typ = _order_type_str(order)
side = str(order.get("side") or info.get("side") or "").lower()
reduce_only = order.get("reduceOnly")
@@ -137,6 +156,42 @@ def _normalize_raw_order(order: dict, *, channel: str) -> dict[str, Any] | None:
}
def _okx_normalize_orders(raw: dict, channel: str) -> list[dict[str, Any]]:
"""OKX 算法单常一笔同时含 SL+TP,拆成两条供中控「交易所止盈止损」展示。"""
n = _normalize_raw_order(dict(raw), channel=channel)
if not n:
return []
info = raw.get("info") or {}
if not isinstance(info, dict):
info = {}
sl_trig = _coerce_float(info.get("slTriggerPx"), raw.get("stopLossPrice"))
tp_trig = _coerce_float(info.get("tpTriggerPx"), raw.get("takeProfitPrice"))
if sl_trig is None or tp_trig is None:
return [n]
base_id = n["id"]
rows: list[dict[str, Any]] = []
for role, px, lbl in (
("sl", sl_trig, f"止损 {sl_trig:g}"),
("tp", tp_trig, f"止盈 {tp_trig:g}"),
):
row = dict(n)
row["id"] = f"{base_id}:{role}"
row["algo_id"] = base_id
row["label"] = lbl
row["trigger_price"] = px
row["category"] = "conditional"
row["channel"] = channel
rows.append(row)
return rows
def _okx_algo_order_id(order_id: str) -> str:
oid = str(order_id or "")
if ":" in oid:
return oid.split(":", 1)[0]
return oid
def _binance_list(ex: Any, symbol: str | None) -> list[dict]:
ex.load_markets()
out: list[dict] = []
@@ -227,9 +282,7 @@ def _okx_list(ex: Any, symbol: str | None) -> list[dict]:
try:
for o in fetch_okx_all_open_orders(ex, sym):
ch = "algo" if _is_conditional_type(_order_type_str(o)) else "regular"
n = _normalize_raw_order(dict(o), channel=ch)
if not n:
continue
for n in _okx_normalize_orders(dict(o), channel=ch):
key = (n["id"], n.get("channel") or ch)
if key in seen:
continue
@@ -345,7 +398,8 @@ def cancel_order(
params = None
if kind == "gate" and ch == "algo":
params = _gate_trigger_params(ex)
ex.cancel_order(str(order_id), unified, params)
oid = _okx_algo_order_id(order_id) if kind == "okx" else str(order_id)
ex.cancel_order(oid, unified, params)
def cancel_orders_for_symbol(
+1 -1
View File
@@ -43,7 +43,7 @@ HUB_BRIDGE_TOKEN = (os.getenv("HUB_BRIDGE_TOKEN") or os.getenv("CONTROL_TOKEN")
_trust_raw = (os.getenv("HUB_TRUST_LAN", "true") or "").strip().lower()
HUB_TRUST_LAN = _trust_raw not in ("0", "false", "no", "off")
DIR = Path(__file__).resolve().parent
HUB_BUILD = "20260525-perf"
HUB_BUILD = "20260525-okx-tpsl"
HUB_AGENT_TIMEOUT = float(os.getenv("HUB_AGENT_TIMEOUT", "8"))
HUB_FLASK_TIMEOUT = float(os.getenv("HUB_FLASK_TIMEOUT", "10"))
_board_key_prices_raw = (os.getenv("HUB_BOARD_KEY_PRICES", "true") or "").strip().lower()
+19 -2
View File
@@ -410,10 +410,27 @@
</details>`;
}
function pickExTpslOrders(cond) {
let sl = cond.find((o) => /^止损\b/.test(o.label || ""));
let tp = cond.find((o) => /^止盈\b/.test(o.label || "") && !(o.label || "").includes("止盈止损"));
if (!sl || !tp) {
const combo = cond.find((o) => (o.label || "").includes("止盈止损"));
if (combo) {
const m = (combo.label || "").match(/SL=([\d.eE+-]+).*TP=([\d.eE+-]+)/i);
if (m) {
if (!sl) sl = { ...combo, label: "止损", trigger_price: Number(m[1]) };
if (!tp) tp = { ...combo, label: "止盈", trigger_price: Number(m[2]) };
}
}
}
if (!sl) sl = cond.find((o) => (o.label || "").includes("止损"));
if (!tp) tp = cond.find((o) => (o.label || "").includes("止盈") && o !== sl);
return { sl, tp };
}
function renderExTpslRows(exchangeId, symbol, cond) {
const symAttr = esc(symbol || "").replace(/"/g, "&quot;");
const sl = cond.find((o) => (o.label || "").includes("止损"));
const tp = cond.find((o) => (o.label || "").includes("止盈"));
const { sl, tp } = pickExTpslOrders(cond);
function row(label, o) {
if (!o) {
return `<div class="pos-ex-order-row"><span class="pos-ex-order-main">${label}:—</span></div>`;