HKUDS/Vibe-Trading · critical · UsdMObservationError

Binance USD-M assets must include USDT

Error message

Binance USD-M assets must include USDT

What it means

Raised by _require_usdt_assets when the parsed Binance USD-M account assets contain no USDT row (usdt_asset is None after scanning all asset balances). The Shadow Account model only supports USDT as margin asset, so the account snapshot is rejected as unusable. It is an internal consistency check on exchange account data, not on user input.

Source

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

        "marginBalance",
        "availableBalance",
        "initialMargin",
        "positionInitialMargin",
        "openOrderInitialMargin",
        "maintMargin",
        "unrealizedProfit",
    )
    for asset in assets:
        symbol = str(asset.get("asset") or "").upper()
        balances = tuple(_number(asset.get(field), field) for field in balance_fields)
        if symbol == "USDT":
            if usdt_asset is not None:
                raise UsdMObservationError("Binance USD-M assets must include exactly one USDT row")
            usdt_asset = asset
        elif any(value != 0 for value in balances):
            raise UsdMObservationError("Binance USD-M Shadow Account supports the USDT asset only")
    if usdt_asset is None:
        raise UsdMObservationError("Binance USD-M assets must include USDT")
    return usdt_asset


def _require_coherent_totals(
    account: Mapping[str, float],
    positions: list[dict[str, Any]],
    close_enough: CloseEnough,
) -> None:
    comparisons = (
        (
            account["total_unrealized_pnl"],
            sum(row["unrealized_pnl"] for row in positions),
        ),
        (
            account["total_initial_margin"],
            sum(row["initial_margin"] for row in positions),
        ),
        (

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Inspect the raw v2/account or balance payload and confirm an asset row with asset == 'USDT' exists
  2. If the account uses multi-assets margin or non-USDT collateral, disable it in Binance futures settings so the account is USDT-margined
  3. Fix test/mock data to include a USDT balance row
  4. Verify the connector is pointed at a USDⓈ-M (futures) account, not spot or COIN-M

Example fix

// before
balances = [{"asset": "USDC", "balance": "100.0"}]
// after
balances = [{"asset": "USDT", "balance": "100.0"}, {"asset": "USDC", "balance": "0.0"}]
Defensive patterns

Strategy: validation

Validate before calling

def has_usdt_balance(payload: dict) -> bool:
    return any(
        str(row.get("asset", "")).upper() == "USDT" for row in payload.get("balances", [])
    )

if not has_usdt_balance(account_payload):
    raise ValueError("account has no USDT margin asset")

Try / catch

try:
    obs = connector.read_account_observation(...)
except UsdMObservationError as exc:
    if "must include USDT" in str(exc):
        logger.error("account not USDT-margined: %s", exc)
        halt_trading()
    raise

Prevention

When it happens

Trigger: Calling read_account_observation (via _account_values) with an account payload whose balances list has no asset=='USDT' entry, e.g. an account holding only BNB/USDC margin, or a stubbed/mocked REST response missing the USDT balance row.

Common situations: Test fixtures or mocks that omit the USDT asset; multi-currency margin enabled on the Binance futures account; switching the exchange account to a USDⓈ-M account funded with a non-USDT asset; upstream schema changes renaming the asset field.

Related errors


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