Initial standalone crypto_okx with one-click deploy.

Add deploy/manage.sh bootstrap for git.bz121.com/dekun/crypto_okx and point docs at this repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-13 20:00:59 +08:00
commit a1abe159fa
238 changed files with 69214 additions and 0 deletions
+335
View File
@@ -0,0 +1,335 @@
"""模拟资金钱包: funding/trading × USDT/USDC."""
from __future__ import annotations
from datetime import datetime
from typing import Any, Callable
WALLET_KEYS = (
"funding_usdt",
"trading_usdt",
"funding_usdc",
"trading_usdc",
)
_ACCT_MAP = {
("funding", "usdt"): "funding_usdt",
("trading", "usdt"): "trading_usdt",
("funding", "usdc"): "funding_usdc",
("trading", "usdc"): "trading_usdc",
}
class InsufficientFunds(RuntimeError):
pass
class SimWallets:
def __init__(self, get_db: Callable) -> None:
self.get_db = get_db
def _now(self) -> str:
return datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
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}
finally:
conn.close()
def view(self) -> dict[str, float]:
return self.snapshot()
def total_usdt_equiv(self, snap: dict[str, float] | None = None) -> float:
v = snap or self.view()
return round(
float(v.get("funding_usdt") or 0)
+ float(v.get("trading_usdt") or 0)
+ float(v.get("funding_usdc") or 0)
+ float(v.get("trading_usdc") or 0),
8,
)
def _write(self, snap: dict[str, float], conn=None) -> dict[str, float]:
owns = conn is None
if owns:
conn = self.get_db()
try:
now = self._now()
conn.execute(
"""
UPDATE sim_wallets SET
funding_usdt=?, trading_usdt=?, funding_usdc=?, trading_usdc=?, updated_at=?
WHERE id=1
""",
(
float(snap["funding_usdt"]),
float(snap["trading_usdt"]),
float(snap["funding_usdc"]),
float(snap["trading_usdc"]),
now,
),
)
if owns:
conn.commit()
return {k: float(snap[k]) for k in WALLET_KEYS}
finally:
if owns:
conn.close()
def _ledger(
self,
conn,
*,
kind: str,
amount: float,
ccy: str,
account: str,
balance_after: float,
note: str = "",
) -> None:
conn.execute(
"""
INSERT INTO sim_ledger_entries(kind, amount, ccy, account, balance_after, note, ts)
VALUES (?,?,?,?,?,?,?)
""",
(kind, float(amount), ccy.upper(), account, float(balance_after), note or "", self._now()),
)
def debit_trading(self, ccy: str, amount: float, *, kind: str = "debit", note: str = "") -> dict[str, float]:
amt = float(amount)
if amt <= 0:
raise ValueError("扣款金额须大于 0")
key = _ACCT_MAP.get(("trading", (ccy or "").lower()))
if not key:
raise ValueError("币种须为 USDT 或 USDC")
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}
bal = float(snap[key])
if amt > bal + 1e-9:
raise InsufficientFunds(f"交易账户 {ccy.upper()} 不足(可用 {bal:.4f})")
snap[key] = bal - amt
self._write(snap, conn=conn)
self._ledger(
conn,
kind=kind,
amount=-amt,
ccy=ccy,
account="trading",
balance_after=snap[key],
note=note,
)
conn.commit()
return snap
finally:
conn.close()
def credit_trading(self, ccy: str, amount: float, *, kind: str = "credit", note: str = "") -> dict[str, float]:
amt = float(amount)
if amt < 0:
raise ValueError("入账金额不能为负")
if amt < 1e-12:
return self.snapshot()
key = _ACCT_MAP.get(("trading", (ccy or "").lower()))
if not key:
raise ValueError("币种须为 USDT 或 USDC")
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[key] = float(snap[key]) + amt
self._write(snap, conn=conn)
self._ledger(
conn,
kind=kind,
amount=amt,
ccy=ccy,
account="trading",
balance_after=snap[key],
note=note,
)
conn.commit()
return snap
finally:
conn.close()
def transfer(
self,
*,
ccy: str,
amount: float,
from_account: str,
to_account: str,
) -> dict[str, Any]:
amt = float(amount)
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()
if fa not in ("funding", "trading") or ta not in ("funding", "trading"):
return {"ok": False, "detail": "账户仅支持 funding / trading"}
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": "币种须为 USDT 或 USDC"}
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}
src_bal = float(snap[src_key])
if amt > src_bal + 1e-9:
return {"ok": False, "detail": f"余额不足(可用 {src_bal:.4f})"}
snap[src_key] = src_bal - amt
snap[dst_key] = float(snap[dst_key]) + amt
self._write(snap, conn=conn)
self._ledger(
conn,
kind="transfer",
amount=-amt,
ccy=ccy_l,
account=fa,
balance_after=snap[src_key],
note=f"to {ta}",
)
self._ledger(
conn,
kind="transfer",
amount=amt,
ccy=ccy_l,
account=ta,
balance_after=snap[dst_key],
note=f"from {fa}",
)
conn.commit()
return {
"ok": True,
"detail": "transferred",
"ccy": ccy_l.upper(),
"amount": amt,
"from": fa,
"to": ta,
"wallets": {k: float(snap[k]) for k in WALLET_KEYS},
"total_usdt_equiv": self.total_usdt_equiv(snap),
}
finally:
conn.close()
def convert(
self,
*,
from_ccy: str,
to_ccy: str,
amount: float,
account: str = "funding",
) -> dict[str, Any]:
amt = float(amount)
if amt <= 0:
return {"ok": False, "detail": "数量须大于 0"}
fa = (from_ccy or "").strip().lower()
ta = (to_ccy or "").strip().lower()
acct = (account or "funding").strip().lower()
if acct not in ("funding", "trading"):
return {"ok": False, "detail": "account 须为 funding / trading"}
if {fa, ta} != {"usdt", "usdc"}:
return {"ok": False, "detail": "仅支持 USDT↔USDC 1:1"}
src_key = _ACCT_MAP[(acct, fa)]
dst_key = _ACCT_MAP[(acct, ta)]
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}
src = float(snap[src_key])
if amt > src + 1e-9:
return {"ok": False, "detail": f"{acct} {fa.upper()} 不足(可用 {src:.4f})"}
snap[src_key] = src - amt
snap[dst_key] = float(snap[dst_key]) + amt
self._write(snap, conn=conn)
self._ledger(
conn,
kind="convert",
amount=-amt,
ccy=fa,
account=acct,
balance_after=snap[src_key],
note=f"to {ta}",
)
self._ledger(
conn,
kind="convert",
amount=amt,
ccy=ta,
account=acct,
balance_after=snap[dst_key],
note=f"from {fa}",
)
conn.commit()
return {
"ok": True,
"detail": "converted",
"from_ccy": fa.upper(),
"to_ccy": ta.upper(),
"amount": amt,
"rate": 1.0,
"account": acct,
"wallets": {k: float(snap[k]) for k in WALLET_KEYS},
"total_usdt_equiv": self.total_usdt_equiv(snap),
}
finally:
conn.close()
def has_open_positions(self) -> bool:
conn = self.get_db()
try:
p = conn.execute(
"SELECT COUNT(*) AS n FROM sim_perp_positions WHERE contracts > 1e-12"
).fetchone()
o = conn.execute(
"SELECT COUNT(*) AS n FROM sim_option_positions WHERE sheets > 1e-12"
).fetchone()
pn = int(p["n"] if hasattr(p, "keys") else p[0])
on = int(o["n"] if hasattr(o, "keys") else o[0])
return pn > 0 or on > 0
finally:
conn.close()
def reset_equity(self, amount: float, *, force: bool = False) -> dict[str, Any]:
if self.has_open_positions() and not force:
return {"ok": False, "detail": "仍有模拟持仓,请先平仓或传 force=true"}
amt = max(0.0, float(amount))
conn = self.get_db()
try:
if force:
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,
}
self._write(snap, conn=conn)
self._ledger(
conn,
kind="reset",
amount=amt,
ccy="USDT",
account="funding",
balance_after=amt,
note="reset equity",
)
conn.commit()
return {"ok": True, "wallets": snap, "total_usdt_equiv": amt}
finally:
conn.close()