feat中控条件单可指令实例账户内划转,Bridge 暴露划转接口。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-13 14:30:26 +08:00
parent 3d3253247e
commit 67d087277a
5 changed files with 274 additions and 44 deletions
+15
View File
@@ -158,6 +158,8 @@ def build_options_hub_snapshot(cfg: dict[str, Any]) -> dict[str, Any]:
"open_bridges": open_bridges,
"cond_order_locked": False,
"cond_orders_active": [],
"transfer_blocked": False,
"transfer_block_msg": "",
}
try:
from lib.options.options_cond_order_lib import has_active_cond_order, list_cond_orders
@@ -173,6 +175,19 @@ def build_options_hub_snapshot(cfg: dict[str, Any]) -> dict[str, Any]:
conn_c.close()
except Exception:
pass
try:
from lib.options.options_account_pnl_lib import transfer_blocked_by_position_msg
conn_tb = cfg["get_db"]()
try:
tmsg = transfer_blocked_by_position_msg(conn_tb, ex)
if tmsg:
out["transfer_blocked"] = True
out["transfer_block_msg"] = tmsg
finally:
conn_tb.close()
except Exception:
pass
return out
except Exception as e:
return {"ok": False, "enabled": True, "msg": str(e)}
+13 -44
View File
@@ -1261,52 +1261,21 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
ex, err = _require_options_ex(cfg)
if ex is None:
return jsonify({"ok": False, "msg": err})
# 持仓/待卖回禁止划转(规格)
conn_blk = cfg["get_db"]()
try:
from lib.options.options_account_pnl_lib import transfer_blocked_by_position_msg
block = transfer_blocked_by_position_msg(conn_blk, ex)
if block:
return jsonify({"ok": False, "msg": block, "transfer_blocked": True})
finally:
conn_blk.close()
del ex # 执行层自行取 exchange_options
data = request.get_json(silent=True) or {}
ccy = (data.get("ccy") or "USDC").upper()
from_acct = (data.get("from") or "funding").strip()
to_acct = (data.get("to") or "trading").strip()
try:
amount = float(data.get("amount"))
except (TypeError, ValueError):
return jsonify({"ok": False, "msg": "数量无效"})
try:
import lib.exchange.okx_options_lib as _opt_lib
from lib.options.options_transfer_exec_lib import execute_options_internal_transfer
xfer = getattr(_opt_lib, "transfer_ccy", None)
except Exception:
xfer = None
if not callable(xfer):
xfer = cfg.get("transfer_ccy")
result = xfer(ex, ccy, amount, from_acct, to_acct) if callable(xfer) else {
"ok": False,
"msg": "划转不可用",
}
if result.get("ok"):
conn = cfg["get_db"]()
try:
init_options_tables(conn)
conn.execute(
"""
INSERT INTO options_transfer_log (ccy, amount, from_account, to_account, status, message)
VALUES (?, ?, ?, ?, 'ok', '')
""",
(ccy, amount, from_acct, to_acct),
)
conn.commit()
finally:
conn.close()
_mark_balances_stale(cfg)
return jsonify(result)
# 前端传 ccy;缺省 USDT(币本位)
ccy = (data.get("ccy") or "USDT").upper()
return jsonify(
execute_options_internal_transfer(
cfg,
amount=data.get("amount"),
from_acct=(data.get("from") or "funding"),
to_acct=(data.get("to") or "trading"),
ccy=ccy,
)
)
@app.route("/api/options/spot/swap", methods=["POST"])
@lr
+147
View File
@@ -0,0 +1,147 @@
"""期权账户内划转执行(本机设置 / 中控 Bridge 共用)."""
from __future__ import annotations
from typing import Any
def _safe_float(v: Any) -> float | None:
if v is None or v == "":
return None
try:
return float(v)
except (TypeError, ValueError):
return None
def _pick_stable(bal: dict[str, Any] | None, *, account: str, ccy: str) -> float | None:
if not isinstance(bal, dict):
return None
acct = "trading" if account == "trading" else "funding"
ccy_l = (ccy or "usdt").strip().lower() or "usdt"
avail = _safe_float(bal.get(f"{acct}_{ccy_l}_avail"))
if avail is not None:
return round(float(avail), 8)
total = _safe_float(bal.get(f"{acct}_{ccy_l}"))
if total is not None:
return round(float(total), 8)
return None
def execute_options_internal_transfer(
cfg: dict[str, Any],
*,
amount: Any,
from_acct: str = "funding",
to_acct: str = "trading",
ccy: str = "USDT",
) -> dict[str, Any]:
"""资金户↔交易户划转;持仓/待卖回拒绝.成功后附带最新 USDT 余额."""
from lib.options.options_account_pnl_lib import transfer_blocked_by_position_msg
from lib.options.options_db import init_options_tables
ex = cfg.get("exchange_options")
ready_fn = cfg.get("options_api_ready")
if callable(ready_fn):
ok, reason = ready_fn(ex)
if not ok:
return {"ok": False, "msg": reason or "期权 API 未配置"}
if ex is None:
return {"ok": False, "msg": "交易所未就绪"}
src = (from_acct or "funding").strip().lower()
dst = (to_acct or "trading").strip().lower()
if src not in ("funding", "trading") or dst not in ("funding", "trading"):
return {"ok": False, "msg": "仅支持 funding/trading"}
if src == dst:
return {"ok": False, "msg": "转出与转入账户不能相同"}
ccy_u = (ccy or "USDT").strip().upper() or "USDT"
if ccy_u not in ("USDT", "USDC"):
return {"ok": False, "msg": "仅支持 USDT/USDC"}
amt = _safe_float(amount)
if amt is None or amt <= 0:
return {"ok": False, "msg": "数量无效"}
get_db = cfg.get("get_db")
if not callable(get_db):
return {"ok": False, "msg": "数据库未就绪"}
conn = get_db()
try:
block = transfer_blocked_by_position_msg(conn, ex)
if block:
return {"ok": False, "msg": block, "transfer_blocked": True}
finally:
conn.close()
xfer = cfg.get("transfer_ccy")
if not callable(xfer):
try:
import lib.exchange.okx_options_lib as _opt_lib
xfer = getattr(_opt_lib, "transfer_ccy", None)
except Exception:
xfer = None
if not callable(xfer):
return {"ok": False, "msg": "划转不可用"}
result = xfer(ex, ccy_u, float(amt), src, dst)
if not isinstance(result, dict):
return {"ok": False, "msg": "划转返回异常"}
out = dict(result)
if not out.get("ok"):
out.setdefault("msg", "划转失败")
return out
conn2 = get_db()
try:
init_options_tables(conn2)
conn2.execute(
"""
INSERT INTO options_transfer_log (ccy, amount, from_account, to_account, status, message)
VALUES (?, ?, ?, ?, 'ok', ?)
""",
(ccy_u, float(amt), src, dst, ""),
)
conn2.commit()
except Exception:
try:
conn2.rollback()
except Exception:
pass
finally:
conn2.close()
try:
from lib.exchange.okx_options_lib import invalidate_options_balance_cache
from lib.instance.instance_live_push_lib import notify_instance_balance_changed
invalidate_options_balance_cache()
app_mod = cfg.get("app_module")
if app_mod is not None and hasattr(app_mod, "invalidate_account_balance_cache"):
app_mod.invalidate_account_balance_cache()
notify_instance_balance_changed()
except Exception:
pass
bal = {}
try:
fetch_bal = cfg.get("fetch_options_balances")
if callable(fetch_bal):
bal = fetch_bal(ex) or {}
except Exception:
bal = {}
trading_usdt = _pick_stable(bal, account="trading", ccy="USDT")
funding_usdt = _pick_stable(bal, account="funding", ccy="USDT")
out.update(
{
"ok": True,
"msg": out.get("msg") or "划转成功",
"ccy": ccy_u,
"amount": round(float(amt), 8),
"from": src,
"to": dst,
"trading_usdt": trading_usdt,
"funding_usdt": funding_usdt,
"transfer_blocked": False,
}
)
return out