HKUDS/Vibe-Trading · error · UsdMObservationError
duplicate {source} position symbol
Error message
duplicate {source} position symbol What it means
Raised by _active_by_symbol when two non-zero position rows share the same uppercased symbol in the same source payload. In one-way mode each symbol must appear at most once among active positions, so a duplicate indicates a malformed or contradictory positionRisk (or, historically, hedge-mode rows for the same symbol). The connector refuses to guess which row is authoritative.
Source
Thrown at agent/src/trading/connectors/binance/usdm.py:304
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:
continue
symbol = str(row.get("symbol") or "").upper()
_canonical_symbol(symbol)
if symbol in active:
raise UsdMObservationError(f"duplicate {source} position symbol")
active[symbol] = row
return active
def _canonical_symbol(symbol: str) -> str:
if not symbol.endswith("USDT") or len(symbol) <= 4 or not symbol.isalnum():
raise UsdMObservationError("Binance USD-M supports canonical */USDT symbols only")
return f"{symbol[:-4]}-USDT-PERP"
def _number(
value: Any,
name: str,
*,
non_negative: bool = False,
positive: bool = False,
) -> float:
try:View on GitHub (pinned to 80ffdda44c)
Solutions
- Inspect the raw positionRisk payload for duplicate symbols with non-zero positionAmt
- If LONG/SHORT rows for the same symbol appear, resolve the hedge-mode positions (see error 1223) — the account is not in one-way mode
- Deduplicate or regenerate mock data so each symbol appears once
- Re-fetch a fresh snapshot; transient partial responses can self-correct on retry
Example fix
// before
[{"symbol":"BTCUSDT","positionAmt":"1"}, {"symbol":"BTCUSDT","positionAmt":"-1"}]
// after
[{"symbol":"BTCUSDT","positionAmt":"0"}] Defensive patterns
Strategy: validation
Validate before calling
from collections import Counter
def no_duplicate_active_symbols(rows: list[dict]) -> bool:
active = [str(r.get("symbol", "")).upper() for r in rows if float(r.get("positionAmt", 0)) != 0]
return len(active) == len(set(active))
assert no_duplicate_active_symbols(rows) Try / catch
try:
obs = connector.read_account_observation(...)
except UsdMObservationError as exc:
if "duplicate" in str(exc):
rows = refetch_positions() # fresh snapshot, retry once
else:
raise Prevention
- Deduplicate position rows by symbol before processing
- Replace, never append, cached position payloads on refetch
- Keep the account in one-way mode
When it happens
Trigger: _join_positions processes a positionRisk list containing two rows with symbol 'BTCUSDT' and non-zero positionAmt; commonly happens when hedge-mode LONG and SHORT rows are both present after collapsing, or when a mock concatenates two snapshots.
Common situations: Account switched modes leaving stale duplicate rows; test data duplicating symbols; retry logic appending instead of replacing the previous payload; exchange returning merged cross-margin and isolated rows for the same symbol in a shape the connector doesn't dedupe.
Related errors
- Binance USD-M assets must include USDT
- Binance USD-M account totals are incoherent
- Binance USD-M {name} payload is invalid
- Binance USD-M Shadow Account requires one-way position mode
- Binance USD-M field {name} must be numeric
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/88e03154f2cab544.
Report an issue: GitHub.