HKUDS/Vibe-Trading · error · UsdMObservationError

Binance USD-M position reads are incoherent

Error message

Binance USD-M position reads are incoherent

What it means

Raised by _join_positions when the set of active position symbols reported by Binance USD-M Futures' account endpoint (v2/account or account) does not match the set from the position-risk endpoint (v2/positionRisk). The library cross-reads both endpoints and treats any disagreement in the *presence* of open positions as an incoherent snapshot, refusing to build the observation.

Source

Thrown at agent/src/trading/connectors/binance/usdm.py:157

    ):
        raise UsdMObservationError("Binance USD-M account totals and USDT asset totals are incoherent")
    return values


def _join_positions(
    account: Mapping[str, Any],
    position_risk: list[Any],
    close_enough: CloseEnough,
) -> list[dict[str, Any]]:
    account_rows = _mapping_rows(account.get("positions"), "account positions")
    risk_rows = _mapping_rows(position_risk, "position risk")
    _require_one_way(account_rows)
    _require_one_way(risk_rows)

    account_active = _active_by_symbol(account_rows, "account")
    risk_active = _active_by_symbol(risk_rows, "position risk")
    if set(account_active) != set(risk_active):
        raise UsdMObservationError("Binance USD-M position reads are incoherent")

    result: list[dict[str, Any]] = []
    for raw_symbol in sorted(account_active):
        account_row = account_active[raw_symbol]
        risk_row = risk_active[raw_symbol]
        quantity = _number(account_row.get("positionAmt"), "positionAmt")
        risk_quantity = _number(risk_row.get("positionAmt"), "positionAmt")
        entry_price = _number(account_row.get("entryPrice"), "entryPrice", positive=True)
        risk_entry = _number(risk_row.get("entryPrice"), "entryPrice", positive=True)
        if not math.isclose(quantity, risk_quantity, rel_tol=0, abs_tol=1e-12) or not math.isclose(
            entry_price, risk_entry, rel_tol=0, abs_tol=1e-12
        ):
            raise UsdMObservationError("Binance USD-M position reads are incoherent")
        open_order_margins = (
            _number(
                account_row.get("openOrderInitialMargin"),
                "openOrderInitialMargin",
                non_negative=True,

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Retry the observation after a short delay (e.g. 1-2s) so both endpoints converge
  2. Ensure no other bot/manual session trades this account while observations are read
  3. Check whether one endpoint was served from cache/stale data and force a fresh request
  4. Inspect the raw account_rows vs risk_rows symbol sets to confirm which side is stale

Example fix

// before
observation = await read_account_observation()

// after
for attempt in range(3):
    try:
        observation = await read_account_observation()
        break
    except UsdMObservationError:
        if attempt == 2:
            raise
        await asyncio.sleep(1.5)
Defensive patterns

Strategy: retry

Validate before calling

# fetch both endpoints and compare active symbol sets before observing
acct = await client.futures_account()
risk = await client.futures_position_risk()
a_syms = {p['symbol'] for p in acct['positions'] if float(p['positionAmt']) != 0}
r_syms = {p['symbol'] for p in risk if float(p['positionAmt']) != 0}
assert a_syms == r_syms, f"stale snapshot: {a_syms ^ r_syms}"

Try / catch

try:
    obs = await connector.read_account_observation()
except UsdMObservationError as e:
    if "incoherent" in str(e):
        await asyncio.sleep(1.5)
        obs = await connector.read_account_observation()
    else:
        raise

Prevention

When it happens

Trigger: Calling read_account_observation while a position is being opened/closed/filled so one endpoint reflects the change before the other; querying the two endpoints non-atomically (sequentially, not via the same moment); a symbol with positionAmt != 0 on one endpoint but 0/absent on the other.

Common situations: Race conditions during concurrent manual trading on the same account, slow API replication between Binance's account and risk services, partial fills in flight, or a stale cached response from one of the two endpoints.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/87444070ba2cf7f2. Report an issue: GitHub.