fix(binance): read funding balance from wallet/balance API

Prefer sapi asset/wallet/balance Funding USDT (matches App), merge get-funding-asset and ccxt as fallbacks, and add optional BINANCE_FUNDING_INCLUDE_SPOT plus a richer verify script.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-04 07:12:28 +08:00
parent dd74770c66
commit b1952dcd6e
3 changed files with 132 additions and 22 deletions
+2
View File
@@ -48,6 +48,8 @@ UPLOAD_DIR=static/images
# 已废弃:资金账户仅显示交易所 funding 余额,不再读取此变量
# TOTAL_CAPITAL=100
# 页顶「资金账户」默认仅 Binance Funding 钱包;若 USDT 主要在现货,可改为 true 合并 Spot
# BINANCE_FUNDING_INCLUDE_SPOT=false
# 每天起始基数(U
DAILY_START_CAPITAL=30
# 日内回撤后基数(U
+94 -19
View File
@@ -288,6 +288,13 @@ def build_binance_ccxt_proxies():
BINANCE_CCXT_PROXIES = build_binance_ccxt_proxies()
# 页顶「资金账户」是否合并现货 USDT(部分用户把现货当资金仓;默认仅 Funding)
BINANCE_FUNDING_INCLUDE_SPOT = os.getenv("BINANCE_FUNDING_INCLUDE_SPOT", "false").lower() in (
"1",
"true",
"yes",
"on",
)
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(ORDER_CHART_DIR, exist_ok=True)
@@ -2660,6 +2667,61 @@ def _parse_binance_funding_asset_rows(rows):
return None
def _parse_binance_wallet_balance_usdt(rows, wallet_names):
"""解析 /sapi/v1/asset/wallet/balancequoteAsset=USDT):按 walletName 取折合 USDT 余额。"""
if isinstance(rows, dict):
rows = [rows]
if not isinstance(rows, list):
return None
want = {str(n).strip().lower() for n in (wallet_names or []) if str(n).strip()}
for row in rows:
if not isinstance(row, dict):
continue
name = str(row.get("walletName") or row.get("name") or "").strip().lower()
if name not in want:
continue
if row.get("activate") is False:
continue
bal = _float_balance_field(row.get("balance"))
if bal is not None:
return bal
return None
def _fetch_binance_funding_usdt_from_wallet_overview():
"""与币安 App 资产页「资金/Funding」钱包 USDT 估值一致(wallet/balance)。"""
try:
ensure_markets_loaded()
raw = exchange.sapiGetAssetWalletBalance({"quoteAsset": TRANSFER_CCY})
val = _parse_binance_wallet_balance_usdt(raw, ("Funding",))
if val is not None:
return float(val)
except Exception:
pass
return None
def _fetch_binance_spot_usdt_total():
"""现货账户 USDT 总额(free+locked)。"""
try:
ensure_markets_loaded()
raw = exchange.sapiGetAssetWalletBalance({"quoteAsset": TRANSFER_CCY})
val = _parse_binance_wallet_balance_usdt(raw, ("Spot",))
if val is not None:
return float(val)
except Exception:
pass
try:
ensure_markets_loaded()
bal = exchange.fetch_balance(params={"type": "spot"})
val = _extract_usdt_total(bal)
if val is not None:
return float(val)
except Exception:
pass
return None
def _extract_usdt_free(balance):
usdt_info = balance.get("USDT", {}) if isinstance(balance, dict) else {}
free_map = balance.get("free", {}) if isinstance(balance, dict) else {}
@@ -2732,32 +2794,45 @@ def _fetch_binance_swap_usdt_free():
def _fetch_binance_funding_usdt():
"""Binance 资金账户(Funding WalletUSDT 总额free+冻结+锁定,与 APP 资金账户一致"""
"""Binance 资金账户(Funding WalletUSDT 总额,与 App「资金账户一致。"""
candidates = []
wallet_val = _fetch_binance_funding_usdt_from_wallet_overview()
if wallet_val is not None:
candidates.append(wallet_val)
try:
ensure_markets_loaded()
raw = exchange.sapiPostAssetGetFundingAsset({"asset": TRANSFER_CCY})
val = _parse_binance_funding_asset_rows(raw)
if val is not None:
candidates.append(float(val))
except Exception:
pass
if not candidates:
try:
ensure_markets_loaded()
raw = exchange.sapiPostAssetGetFundingAsset({})
val = _parse_binance_funding_asset_rows(raw)
if val is not None:
candidates.append(float(val))
except Exception:
pass
try:
ensure_markets_loaded()
bal = exchange.fetch_balance(params={"type": "funding"})
val = _extract_usdt_total(bal)
if val is not None:
return float(val)
candidates.append(float(val))
except Exception:
pass
try:
ensure_markets_loaded()
raw = exchange.sapiPostAssetGetFundingAsset({"asset": "USDT"})
val = _parse_binance_funding_asset_rows(raw)
if val is not None:
return float(val)
except Exception:
pass
try:
ensure_markets_loaded()
raw = exchange.sapiPostAssetGetFundingAsset({})
val = _parse_binance_funding_asset_rows(raw)
if val is not None:
return float(val)
except Exception:
pass
return None
if not candidates:
base = None
else:
base = max(candidates)
if BINANCE_FUNDING_INCLUDE_SPOT:
spot_val = _fetch_binance_spot_usdt_total()
if spot_val is not None:
base = (base or 0.0) + float(spot_val)
return base
def get_available_trading_usdt():
@@ -3,6 +3,7 @@
python scripts/verify_binance_funding.py
打印 BINANCE_API_KEY 前 8 位便于与 Binance 控制台核对(不含 Secret)。用于服务器自检。
对比 App:资产 → 资金账户(Funding) / 现货账户(Spot) / U本位合约。
"""
import os
import sys
@@ -33,19 +34,51 @@ def main():
if not s or "REPLACE" in s.upper():
print("WARN: BINANCE_API_SECRET 为空或仍像占位符,请核对 .env")
print("BINANCE_API_KEY prefix (8 chars):", (k[:8] + "") if len(k) > 8 else "(short)")
print("BINANCE_FUNDING_INCLUDE_SPOT:", os.getenv("BINANCE_FUNDING_INCLUDE_SPOT", "false"))
import app as mod # noqa: E402
mod.ensure_markets_loaded()
fu = mod._fetch_binance_funding_usdt()
print(">>> _fetch_binance_funding_usdt() =", fu)
ccy = getattr(mod, "TRANSFER_CCY", "USDT")
try:
raw = mod.exchange.sapiGetAssetWalletBalance({"quoteAsset": ccy})
print(f"\n>>> sapi/v1/asset/wallet/balance (quoteAsset={ccy}):")
if isinstance(raw, list):
for row in raw:
if isinstance(row, dict):
print(
" ",
row.get("walletName"),
"activate=",
row.get("activate"),
"balance=",
row.get("balance"),
)
else:
print(" ", raw)
except Exception as e:
print(">>> wallet/balance error:", e)
try:
raw = mod.exchange.sapiPostAssetGetFundingAsset({"asset": ccy})
print(f"\n>>> get-funding-asset (asset={ccy}):", raw)
except Exception as e:
print(">>> get-funding-asset error:", e)
fu = mod._fetch_binance_funding_usdt()
print("\n>>> _fetch_binance_funding_usdt() (页顶资金账户) =", fu)
try:
fw = mod._fetch_binance_funding_usdt_from_wallet_overview()
print(">>> _fetch_binance_funding_usdt_from_wallet_overview() =", fw)
sp = mod._fetch_binance_spot_usdt_total()
print(">>> _fetch_binance_spot_usdt_total() =", sp)
sw = mod._fetch_binance_swap_usdt_total()
print(">>> _fetch_binance_swap_usdt_total() (合约账户) =", sw)
sf = mod._fetch_binance_swap_usdt_free()
print(">>> _fetch_binance_swap_usdt_free() (合约可用) =", sf)
except Exception as e:
print(">>> swap balance fetch error:", e)
print(">>> balance fetch error:", e)
if __name__ == "__main__":