模拟盘对齐币本位:钱包支持 ETH/BTC,现货桥与期权权利金走本地撮合。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-20 15:16:51 +08:00
parent c8688a11ae
commit 6f721d1e6d
6 changed files with 836 additions and 516 deletions
+59 -26
View File
@@ -1,4 +1,4 @@
"""模拟资金钱包: funding/trading × USDT/USDC."""
"""模拟资金钱包: funding/trading × USDT/USDC/ETH/BTC."""
from __future__ import annotations
@@ -11,6 +11,10 @@ WALLET_KEYS = (
"trading_usdt",
"funding_usdc",
"trading_usdc",
"funding_eth",
"trading_eth",
"funding_btc",
"trading_btc",
)
_ACCT_MAP = {
@@ -18,6 +22,10 @@ _ACCT_MAP = {
("trading", "usdt"): "trading_usdt",
("funding", "usdc"): "funding_usdc",
("trading", "usdc"): "trading_usdc",
("funding", "eth"): "funding_eth",
("trading", "eth"): "trading_eth",
("funding", "btc"): "funding_btc",
("trading", "btc"): "trading_btc",
}
@@ -42,13 +50,26 @@ class SimWallets:
def _now(self) -> str:
return datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
def _row_to_snap(self, row: Any) -> dict[str, float]:
if row is None:
return {k: 0.0 for k in WALLET_KEYS}
keys = set(row.keys()) if hasattr(row, "keys") else set()
out: dict[str, float] = {}
for k in WALLET_KEYS:
if keys and k not in keys:
out[k] = 0.0
else:
try:
out[k] = float(row[k] or 0)
except (KeyError, IndexError, TypeError, ValueError):
out[k] = 0.0
return out
def snapshot(self) -> dict[str, float]:
conn = self.get_db()
try:
row = conn.execute("SELECT * FROM sim_wallets WHERE id=1").fetchone()
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}
return self._row_to_snap(row)
finally:
conn.close()
@@ -56,6 +77,7 @@ class SimWallets:
return self.snapshot()
def total_usdt_equiv(self, snap: dict[str, float] | None = None) -> float:
"""稳定币合计(不含 ETH/BTC 折算)."""
v = snap or self.view()
return round(
float(v.get("funding_usdt") or 0)
@@ -71,23 +93,30 @@ class SimWallets:
conn = self.get_db()
try:
now = self._now()
full = {k: float(snap.get(k) or 0) for k in WALLET_KEYS}
conn.execute(
"""
UPDATE sim_wallets SET
funding_usdt=?, trading_usdt=?, funding_usdc=?, trading_usdc=?, updated_at=?
funding_usdt=?, trading_usdt=?, funding_usdc=?, trading_usdc=?,
funding_eth=?, trading_eth=?, funding_btc=?, trading_btc=?,
updated_at=?
WHERE id=1
""",
(
float(snap["funding_usdt"]),
float(snap["trading_usdt"]),
float(snap["funding_usdc"]),
float(snap["trading_usdc"]),
full["funding_usdt"],
full["trading_usdt"],
full["funding_usdc"],
full["trading_usdc"],
full["funding_eth"],
full["trading_eth"],
full["funding_btc"],
full["trading_btc"],
now,
),
)
if owns:
conn.commit()
return {k: float(snap[k]) for k in WALLET_KEYS}
return full
finally:
if owns:
conn.close()
@@ -117,14 +146,14 @@ class SimWallets:
raise ValueError("扣款金额须大于 0")
key = _ACCT_MAP.get(("trading", (ccy or "").lower()))
if not key:
raise ValueError("币种须为 USDT 或 USDC")
raise ValueError("币种须为 USDT/USDC/ETH/BTC")
conn = self.get_db()
try:
row = conn.execute("SELECT * FROM sim_wallets WHERE id=1").fetchone()
snap = {k: float(row[k] or 0) for k in WALLET_KEYS}
snap = self._row_to_snap(row)
bal = float(snap[key])
if amt > bal + 1e-9:
raise InsufficientFunds(f"交易账户 {ccy.upper()} 不足(可用 {bal:.4f})")
raise InsufficientFunds(f"交易账户 {ccy.upper()} 不足(可用 {bal:.8f})")
snap[key] = bal - amt
self._write(snap, conn=conn)
self._ledger(
@@ -149,11 +178,11 @@ class SimWallets:
return self.snapshot()
key = _ACCT_MAP.get(("trading", (ccy or "").lower()))
if not key:
raise ValueError("币种须为 USDT 或 USDC")
raise ValueError("币种须为 USDT/USDC/ETH/BTC")
conn = self.get_db()
try:
row = conn.execute("SELECT * FROM sim_wallets WHERE id=1").fetchone()
snap = {k: float(row[k] or 0) for k in WALLET_KEYS}
snap = self._row_to_snap(row)
snap[key] = float(snap[key]) + amt
self._write(snap, conn=conn)
self._ledger(
@@ -191,14 +220,14 @@ class SimWallets:
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": "币种须为 USDT 或 USDC"}
return {"ok": False, "detail": "币种须为 USDT/USDC/ETH/BTC"}
conn = self.get_db()
try:
row = conn.execute("SELECT * FROM sim_wallets WHERE id=1").fetchone()
snap = {k: float(row[k] or 0) for k in WALLET_KEYS}
snap = self._row_to_snap(row)
src_bal = float(snap[src_key])
if amt > src_bal + 1e-9:
return {"ok": False, "detail": f"余额不足(可用 {src_bal:.4f})"}
return {"ok": False, "detail": f"余额不足(可用 {src_bal:.8f})"}
snap[src_key] = src_bal - amt
snap[dst_key] = float(snap[dst_key]) + amt
self._write(snap, conn=conn)
@@ -246,7 +275,7 @@ class SimWallets:
fee: float | None = None,
note: str | None = None,
) -> dict[str, Any]:
"""USDT↔USDC 兑换. 默认交易账户; to_amount 未给时按 rate(USDT/USDC) 换算, 再否则 1:1."""
"""USDT↔USDC / USDT↔ETH / USDT↔BTC 兑换. to_amount 未给时按 rate(USDT per coin) 换算."""
amt = float(amount)
if amt <= 0:
return {"ok": False, "detail": "数量须大于 0"}
@@ -255,13 +284,14 @@ class SimWallets:
acct = normalize_sim_account(account) or "trading"
if acct not in ("funding", "trading"):
return {"ok": False, "detail": "account 须为 funding / trading"}
if {fa, ta} != {"usdt", "usdc"}:
return {"ok": False, "detail": "仅支持 USDT↔USDC"}
pair = {fa, ta}
if pair not in ({"usdt", "usdc"}, {"usdt", "eth"}, {"usdt", "btc"}):
return {"ok": False, "detail": "仅支持 USDT↔USDC/ETH/BTC"}
if to_amount is not None:
got = float(to_amount)
elif rate is not None and float(rate) > 0:
r = float(rate)
# rate = USDT per 1 USDC
# rate = USDT per 1 coin(USDC/ETH/BTC)
got = (amt / r) if fa == "usdt" else (amt * r)
else:
got = amt
@@ -272,10 +302,10 @@ class SimWallets:
conn = self.get_db()
try:
row = conn.execute("SELECT * FROM sim_wallets WHERE id=1").fetchone()
snap = {k: float(row[k] or 0) for k in WALLET_KEYS}
snap = self._row_to_snap(row)
src = float(snap[src_key])
if amt > src + 1e-9:
return {"ok": False, "detail": f"{acct} {fa.upper()} 不足(可用 {src:.4f})"}
return {"ok": False, "detail": f"{acct} {fa.upper()} 不足(可用 {src:.8f})"}
snap[src_key] = src - amt
snap[dst_key] = float(snap[dst_key]) + got
self._write(snap, conn=conn)
@@ -341,12 +371,15 @@ class SimWallets:
conn.execute("DELETE FROM sim_perp_positions")
conn.execute("DELETE FROM sim_option_positions")
conn.execute("DELETE FROM sim_option_orders")
now = self._now()
snap = {
"funding_usdt": amt,
"trading_usdt": 0.0,
"funding_usdc": 0.0,
"trading_usdc": 0.0,
"funding_eth": 0.0,
"trading_eth": 0.0,
"funding_btc": 0.0,
"trading_btc": 0.0,
}
self._write(snap, conn=conn)
self._ledger(
@@ -361,4 +394,4 @@ class SimWallets:
conn.commit()
return {"ok": True, "wallets": snap, "total_usdt_equiv": amt}
finally:
conn.close()
conn.close()