Route sim transfer and convert through local wallets instead of OKX private API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-14 18:47:41 +08:00
parent e7aa724a3b
commit 9d1495658c
2 changed files with 145 additions and 8 deletions
+133 -6
View File
@@ -136,6 +136,36 @@ def apply_sim_hooks(app_module: Any) -> None:
app_module.place_exchange_order = place_exchange_order
app_module.close_exchange_order = close_exchange_order
app_module.get_live_position_contracts = get_live_position_contracts
_orig_xfer = getattr(app_module, "execute_transfer_usdt", None)
if callable(_orig_xfer):
def execute_transfer_usdt(amount, from_account, to_account):
if is_sim_mode(app_module.get_db):
from lib.sim.wallets_lib import SimWallets, normalize_sim_account
try:
result = SimWallets(app_module.get_db).transfer(
ccy="USDT",
amount=float(amount),
from_account=normalize_sim_account(from_account),
to_account=normalize_sim_account(to_account),
)
except Exception as e:
return False, str(e), None
if not result.get("ok"):
return False, result.get("detail") or result.get("msg") or "划转失败", None
try:
from lib.instance.instance_live_push_lib import notify_instance_balance_changed
notify_instance_balance_changed()
except Exception:
pass
return True, "sim 划转成功", result
return _orig_xfer(amount, from_account, to_account)
app_module.execute_transfer_usdt = execute_transfer_usdt
app_module._sim_hooks_applied = True
_patch_okx_options_lib(app_module)
@@ -154,6 +184,8 @@ def _patch_okx_options_lib(app_module: Any) -> None:
_orig_fetch_pos = opt_lib.fetch_option_positions
_orig_ready = opt_lib.options_api_ready
_orig_fetch_bal = opt_lib.fetch_options_balances
_orig_transfer = opt_lib.transfer_ccy
_orig_swap = opt_lib.spot_market_swap_usdt_usdc
def options_header_balances(ex, *, force: bool = False):
try:
@@ -169,19 +201,30 @@ def _patch_okx_options_lib(app_module: Any) -> None:
pass
return _orig_header(ex, force=force)
def fetch_options_balances(ex, *, force: bool = False):
def fetch_options_balances(
ex, *, force: bool = False, scope: str = "main", sub_acct: str = ""
):
try:
if _GET_DB is not None and is_sim_mode(_GET_DB):
w = broker().balances_header()
fu = float(w["funding_usdt"])
fc = float(w["funding_usdc"])
tu = float(w["trading_usdt"])
tc = float(w["trading_usdc"])
return {
"trading_usdc": float(w["trading_usdc"]),
"funding_usdc": float(w["funding_usdc"]),
"funding_usdt": float(w["funding_usdt"]),
"trading_usdt": float(w["trading_usdt"]),
"scope": "main",
"funding_usdt": fu,
"funding_usdc": fc,
"trading_usdt": tu,
"trading_usdc": tc,
"funding_usdt_avail": fu,
"funding_usdc_avail": fc,
"trading_usdt_avail": tu,
"trading_usdc_avail": tc,
}
except Exception:
pass
return _orig_fetch_bal(ex, force=force)
return _orig_fetch_bal(ex, force=force, scope=scope, sub_acct=sub_acct)
def options_api_ready(ex):
try:
@@ -191,6 +234,72 @@ def _patch_okx_options_lib(app_module: Any) -> None:
pass
return _orig_ready(ex)
def transfer_ccy(ex, ccy, amount, from_acct, to_acct):
try:
if _GET_DB is not None and is_sim_mode(_GET_DB):
from lib.sim.wallets_lib import SimWallets, normalize_sim_account
result = SimWallets(_GET_DB).transfer(
ccy=str(ccy or "USDC"),
amount=float(amount),
from_account=normalize_sim_account(from_acct),
to_account=normalize_sim_account(to_acct),
)
if not result.get("ok"):
return {
"ok": False,
"msg": result.get("detail") or result.get("msg") or "划转失败",
}
try:
from lib.instance.instance_live_push_lib import notify_instance_balance_changed
notify_instance_balance_changed()
except Exception:
pass
return {"ok": True, "msg": "sim 划转成功", "sim": True, **result}
except Exception as e:
return {"ok": False, "msg": str(e)}
return _orig_transfer(ex, ccy, amount, from_acct, to_acct)
def spot_market_swap_usdt_usdc(ex, *, direction: str = "usdt_to_usdc", amount: float = 0):
try:
if _GET_DB is not None and is_sim_mode(_GET_DB):
from lib.sim.wallets_lib import SimWallets
d = (direction or "usdt_to_usdc").strip().lower()
if d == "usdc_to_usdt":
from_ccy, to_ccy = "USDC", "USDT"
else:
from_ccy, to_ccy = "USDT", "USDC"
result = SimWallets(_GET_DB).convert(
from_ccy=from_ccy,
to_ccy=to_ccy,
amount=float(amount),
account="funding",
)
if not result.get("ok"):
result = SimWallets(_GET_DB).convert(
from_ccy=from_ccy,
to_ccy=to_ccy,
amount=float(amount),
account="trading",
)
if not result.get("ok"):
return {
"ok": False,
"msg": result.get("detail") or result.get("msg") or "兑换失败",
}
try:
from lib.instance.instance_live_push_lib import notify_instance_balance_changed
notify_instance_balance_changed()
except Exception:
pass
return {"ok": True, "msg": "sim 兑换成功(1:1)", "sim": True, **result}
except Exception as e:
return {"ok": False, "msg": str(e)}
return _orig_swap(ex, direction=direction, amount=amount)
def fetch_option_order(ex, *, inst_id: str, ord_id: str):
oid = str(ord_id or "")
if oid.startswith("sim-"):
@@ -243,6 +352,8 @@ def _patch_okx_options_lib(app_module: Any) -> None:
opt_lib.options_header_balances = options_header_balances
opt_lib.fetch_options_balances = fetch_options_balances
opt_lib.options_api_ready = options_api_ready
opt_lib.transfer_ccy = transfer_ccy
opt_lib.spot_market_swap_usdt_usdc = spot_market_swap_usdt_usdc
opt_lib.fetch_option_order = fetch_option_order
opt_lib.wait_option_order_full_fill = wait_option_order_full_fill
opt_lib.fetch_option_positions = fetch_option_positions
@@ -279,6 +390,22 @@ def patch_options_cfg(cfg: dict[str, Any]) -> dict[str, Any]:
if not callable(get_db):
return cfg
set_get_db(get_db)
# 与 apply_sim_hooks 对齐: cfg 里可能仍是 import 时的旧引用
import lib.exchange.okx_options_lib as opt_lib
for key in (
"transfer_ccy",
"spot_market_swap_usdt_usdc",
"options_api_ready",
"fetch_options_balances",
"fetch_option_positions",
"fetch_option_order",
"wait_option_order_full_fill",
):
if key in cfg and hasattr(opt_lib, key):
cfg[key] = getattr(opt_lib, key)
live_limit = cfg.get("place_option_limit_order")
live_market = cfg.get("place_option_market_order")
if callable(live_limit) and callable(live_market):
+12 -2
View File
@@ -21,6 +21,16 @@ _ACCT_MAP = {
}
def normalize_sim_account(account: str | None) -> str:
"""统一账户别名: swap/unified → trading; spot → funding."""
a = (account or "").strip().lower()
if a in ("trading", "swap", "unified", "18"):
return "trading"
if a in ("funding", "spot", "6"):
return "funding"
return a
class InsufficientFunds(RuntimeError):
pass
@@ -172,8 +182,8 @@ class SimWallets:
if amt <= 0:
return {"ok": False, "detail": "划转金额须大于 0"}
ccy_l = (ccy or "USDT").strip().lower()
fa = (from_account or "").strip().lower()
ta = (to_account or "").strip().lower()
fa = normalize_sim_account(from_account)
ta = normalize_sim_account(to_account)
if fa not in ("funding", "trading") or ta not in ("funding", "trading"):
return {"ok": False, "detail": "账户仅支持 funding / trading"}
if fa == ta: