HKUDS/Vibe-Trading · critical · UsdMObservationError
Binance USD-M account totals are incoherent
Error message
Binance USD-M account totals are incoherent
What it means
Raised by _require_coherent_totals when reported account totals do not match derived values within tolerance: margin_balance must equal wallet_balance + total_unrealized_pnl (and similar comparisons checked via close_enough). This guards against partially-read or inconsistent snapshots from the exchange. The error indicates the account payload is internally contradictory, so risk computations would be wrong.
Source
Thrown at agent/src/trading/connectors/binance/usdm.py:281
(
account["total_unrealized_pnl"],
sum(row["unrealized_pnl"] for row in positions),
),
(
account["total_initial_margin"],
sum(row["initial_margin"] for row in positions),
),
(
account["total_maintenance_margin"],
sum(row["maintenance_margin"] for row in positions),
),
(
account["margin_balance"],
account["wallet_balance"] + account["total_unrealized_pnl"],
),
)
if any(not close_enough(reported, derived) for reported, derived in comparisons):
raise UsdMObservationError("Binance USD-M account totals are incoherent")
def _mapping_rows(value: Any, name: str) -> list[Mapping[str, Any]]:
if not isinstance(value, list) or any(not isinstance(row, Mapping) for row in value):
raise UsdMObservationError(f"Binance USD-M {name} payload is invalid")
return value
def _require_one_way(rows: list[Mapping[str, Any]]) -> None:
if any(str(row.get("positionSide") or "").upper() != "BOTH" for row in rows):
raise UsdMObservationError("Binance USD-M Shadow Account requires one-way position mode")
def _active_by_symbol(rows: list[Mapping[str, Any]], source: str) -> dict[str, Mapping[str, Any]]:
active: dict[str, Mapping[str, Any]] = {}
for row in rows:
quantity = _number(row.get("positionAmt"), "positionAmt")
if quantity == 0:View on GitHub (pinned to 80ffdda44c)
Solutions
- Retry the observation — transient incoherence from concurrent position updates usually resolves
- Fetch account balances and positions atomically (same snapshot / Binance userData stream instead of two REST calls)
- Increase absolute_tolerance in the connector configuration if the account is large and discrepancies are float noise
- Fix mock/fixture numbers so margin_balance == wallet_balance + total_unrealized_pnl
Example fix
// before
{"wallet_balance": 1000.0, "total_unrealized_pnl": 5.0, "margin_balance": 1010.0}
// after
{"wallet_balance": 1000.0, "total_unrealized_pnl": 5.0, "margin_balance": 1005.0} Defensive patterns
Strategy: retry
Validate before calling
def totals_coherent(acc: dict, tol: float) -> bool:
return abs(acc["margin_balance"] - (acc["wallet_balance"] + acc["total_unrealized_pnl"])) <= tol
if not totals_coherent(acc, tol):
acc = refetch_account() Try / catch
for attempt in range(3):
try:
obs = connector.read_account_observation(...)
break
except UsdMObservationError as exc:
if "incoherent" not in str(exc) or attempt == 2:
raise
sleep(0.5) Prevention
- Fetch balances and positions from the same snapshot (user-data stream)
- Size absolute_tolerance to the account magnitude
- Retry transiently before treating incoherence as fatal
- Keep mock numbers internally consistent
When it happens
Trigger: read_account_observation receives an account dict where margin_balance deviates from wallet_balance + total_unrealized_pnl beyond absolute_tolerance, typically because balances and positions were fetched at different times or from different endpoints.
Common situations: Active positions changing between the balance and positionRisk REST calls; using absolute_tolerance too small for large accounts (floating point noise); mocks with hand-picked inconsistent numbers; exchange during settlement/funding events reporting transiently inconsistent totals.
Related errors
- Binance USD-M assets must include USDT
- Binance USD-M {name} payload is invalid
- Binance USD-M Shadow Account requires one-way position mode
- duplicate {source} position symbol
- Binance USD-M field {name} must be numeric
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/9abea2300c7489f1.
Report an issue: GitHub.