"""SIM 多账户资金:资金/交易 USDT + 期权资金/交易 USDC(对齐 OKX 展示)。""" from __future__ import annotations import time from typing import Any from ..models.db import Database, get_db WALLET_KEYS = ( "funding_usdt", "trading_usdt", "options_funding_usdc", "options_trading_usdc", "options_funding_usdt", "options_trading_usdt", ) # 划转账户映射 _ACCT_MAP = { ("funding", "usdt"): "funding_usdt", ("trading", "usdt"): "trading_usdt", ("options_funding", "usdc"): "options_funding_usdc", ("options_trading", "usdc"): "options_trading_usdc", ("options_funding", "usdt"): "options_funding_usdt", ("options_trading", "usdt"): "options_trading_usdt", # 简化别名:funding/trading + usdc → 期权侧 ("funding", "usdc"): "options_funding_usdc", ("trading", "usdc"): "options_trading_usdc", } def _now_ms() -> int: return int(time.time() * 1000) class SimFundsWallets: def __init__(self, db: Database | None = None) -> None: self.db = db or get_db() def snapshot(self) -> dict[str, float]: row = self.db.fetchone("SELECT * FROM funds_wallets WHERE id=1") if row is None: return {k: 0.0 for k in WALLET_KEYS} return {k: float(row[k] or 0) for k in WALLET_KEYS} def total_usdt_equiv(self, snap: dict[str, float] | None = None) -> float: """USDC 按 1:1 计入总资金(与 crypto_monitor 一致)。""" s = snap or self.snapshot() return round(sum(float(s.get(k) or 0) for k in WALLET_KEYS), 8) def reset_from_equity(self, equity: float) -> dict[str, float]: """重置:全部放入资金账户 USDT。""" amt = max(0.0, float(equity)) now = _now_ms() self.db.execute( """UPDATE funds_wallets SET funding_usdt=?, trading_usdt=0, options_funding_usdc=0, options_trading_usdc=0, options_funding_usdt=0, options_trading_usdt=0, updated_at_ms=? WHERE id=1""", (amt, now), ) return self.snapshot() def _set(self, **kwargs: float) -> dict[str, float]: snap = self.snapshot() for k, v in kwargs.items(): if k in WALLET_KEYS: snap[k] = float(v) now = _now_ms() self.db.execute( """UPDATE funds_wallets SET funding_usdt=?, trading_usdt=?, options_funding_usdc=?, options_trading_usdc=?, options_funding_usdt=?, options_trading_usdt=?, updated_at_ms=? WHERE id=1""", ( snap["funding_usdt"], snap["trading_usdt"], snap["options_funding_usdc"], snap["options_trading_usdc"], snap["options_funding_usdt"], snap["options_trading_usdt"], now, ), ) return snap def mirror_cash(self, amount: float, *, kind: str) -> None: """策略账本变动时镜像到对应钱包(SIM)。""" amt = float(amount) if abs(amt) < 1e-12: return snap = self.snapshot() k = (kind or "").lower() if "option" in k: key = "options_trading_usdc" elif "perp" in k or "funding" in k: key = "trading_usdt" else: key = "trading_usdt" snap[key] = float(snap.get(key) or 0) + amt # 允许短暂为负(与 ledger allow_negative 对齐时由调用方保证);展示侧夹到合理范围不在此做 self._set(**snap) def sync_ledger_equity(self) -> float: """兑换/划转后把总权益同步到 ledger_meta(不重置钱包分配)。""" total = self.total_usdt_equiv() now = _now_ms() self.db.execute( "UPDATE ledger_meta SET equity=?, available=?, updated_at_ms=? WHERE id=1", (total, total, now), ) return total def convert( self, *, direction: str, amount: float, rate: float = 1.0, ) -> dict[str, Any]: """ SIM 兑换(默认在「资金账户」内 USDT↔USDC,对齐 crypto_monitor 主路径)。 direction: usdt_to_usdc | usdc_to_usdt rate: 1 USDC = rate USDT(默认 1.0) """ amt = float(amount) if amt <= 0: return {"ok": False, "detail": "数量须大于 0"} r = float(rate) if rate and rate > 0 else 1.0 d = (direction or "").strip().lower() snap = self.snapshot() if d == "usdt_to_usdc": src = float(snap["funding_usdt"]) if amt > src + 1e-9: return {"ok": False, "detail": f"资金账户 USDT 不足(可用 {src:.4f})"} usdc = amt / r snap["funding_usdt"] = src - amt snap["options_funding_usdc"] = float(snap["options_funding_usdc"]) + usdc elif d == "usdc_to_usdt": src = float(snap["options_funding_usdc"]) if amt > src + 1e-9: return {"ok": False, "detail": f"期权资金账户 USDC 不足(可用 {src:.4f})"} usdt = amt * r snap["options_funding_usdc"] = src - amt snap["funding_usdt"] = float(snap["funding_usdt"]) + usdt else: return {"ok": False, "detail": "direction 须为 usdt_to_usdc 或 usdc_to_usdt"} self._set(**snap) total = self.sync_ledger_equity() return { "ok": True, "detail": "converted", "direction": d, "amount": amt, "rate": r, "wallets": self.snapshot(), "total_usdt_equiv": total, } def transfer( self, *, ccy: str, amount: float, from_account: str, to_account: str, ) -> dict[str, Any]: """SIM 划转:funding/trading/options_funding/options_trading × USDT|USDC。""" amt = float(amount) if amt <= 0: return {"ok": False, "detail": "划转金额须大于 0"} ccy_l = (ccy or "USDC").strip().lower() fa = (from_account or "").strip().lower() ta = (to_account or "").strip().lower() if fa == ta: return {"ok": False, "detail": "来源与目标账户不能相同"} src_key = _ACCT_MAP.get((fa, ccy_l)) dst_key = _ACCT_MAP.get((ta, ccy_l)) if not src_key or not dst_key: return { "ok": False, "detail": "账户须为 funding/trading/options_funding/options_trading,币种 USDT|USDC", } snap = self.snapshot() src_bal = float(snap[src_key]) if amt > src_bal + 1e-9: return {"ok": False, "detail": f"{src_key} 余额不足(可用 {src_bal:.4f})"} snap[src_key] = src_bal - amt snap[dst_key] = float(snap[dst_key]) + amt self._set(**snap) total = self.sync_ledger_equity() return { "ok": True, "detail": "transferred", "ccy": ccy_l.upper(), "amount": amt, "from": fa, "to": ta, "wallets": self.snapshot(), "total_usdt_equiv": total, }