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:
@@ -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/balance(quoteAsset=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 Wallet)USDT 总额(free+冻结+锁定,与 APP 资金账户一致)。"""
|
||||
"""Binance 资金账户(Funding Wallet)USDT 总额,与 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():
|
||||
|
||||
Reference in New Issue
Block a user