An exchange API balance snapshot states an amount at an observation boundary; a balance delta states a signed change to an amount. Replace a field with an absolute value, but add a delta only once and only to a compatible starting state. A message containing just changed assets can still carry absolute balances. Record the payload semantics, account scope and reconciliation boundary before combining REST responses with private-stream events.
Classify the value and the asset list separately
Two independent questions govern a balance update: does each number replace or change the stored value, and does the message cover the whole account or a subset? Calling every WebSocket message a “delta” hides this distinction.
| Payload contract | Example value | Correct operation | Meaning of an omitted asset |
|---|---|---|---|
| Complete snapshot for a defined scope | USDT balance is 1,000 | Establish that scope's state at its boundary | Interpret under the endpoint's coverage and filtering rules |
| Partial update with absolute values | USDT balance is now 1,125 | Replace the included field; preserve unrelated assets | No update for that asset |
| Additive movement | USDT changed by +125 | Add once to the matching balance basis | No movement reported for that asset |
“Absolute” describes the number, while “partial” describes coverage. They can both be true. Treating a partial asset list as a complete replacement can erase untouched holdings. Treating an absolute number as a movement can multiply the apparent account balance.
A snapshot read can also have filters. Binance Spot account information supports omitZeroBalances: setting it to true returns only nonzero balances. Preserve that request option with the response. Omission under a documented snapshot filter and omission from a changed-assets stream message need different handling.
Keep the field identity precise: venue, environment, account or subaccount, wallet or product, asset, units and balance field. An additive wallet movement cannot automatically update an available-margin valuation. The available-versus-total balance guide covers that separate question of what a field measures. Here the problem is how an observation changes stored state.
Binance illustrates two different event meanings
Binance Spot's outboundAccountPosition contains assets potentially affected by the triggering event. Its published payload gives each included asset's f free amount and l locked amount. These are balance values for those assets, rather than instructions to add those amounts. Merge the included asset fields without clearing assets absent from the message. See the Binance Spot account-update payload.
The separate balanceUpdate event reports deposits, withdrawals and transfers between accounts. Its d field is explicitly a balance delta, with a identifying the asset and T the clear time. The Binance user-data event documentation and balance-update schema establish that narrower meaning. A consumer that watches only this event does not thereby capture every trading-related balance change.
These two observations can describe overlapping economic activity. Choose a defined state authority and use other observations as supporting evidence or reconciliation inputs. Adding a movement after accepting an absolute balance that already contains it counts the same activity twice. The exact event ordering and inclusion boundary must come from the applicable protocol; the event names alone do not establish either.
A deposit, a duplicate and a missed withdrawal
Consider this hypothetical consumer for one unleveraged wallet. It starts with 1,000 USDT and four ABC. The fictional protocol supplies stable movement identifiers D1 and D2 and absolute observations with known boundaries. Those identifiers and guarantees are assumptions for the exercise, not Binance field names or delivery promises. There are no trades, fees, reservations or other movements.
| Step | Evidence processed | Local USDT | Local ABC | Explanation |
|---|---|---|---|---|
| 1 | Opening complete snapshot | 1,000 | 4 | Establish the starting state |
| 2 | D1: deposit +125 USDT | 1,125 | 4 | Apply the previously unseen movement |
| 3 | D1 delivered again | 1,125 | 4 | Recognize the same movement; apply no second effect |
| 4 | Partial absolute update: USDT is 1,125, includes D1 | 1,125 | 4 | Replace USDT; preserve ABC |
| 5 | Observation gap; D2 withdraws 30 USDT but is not received | 1,125, unverified | 4, unverified | Stored values are no longer a verified current account view |
| 6 | Recovery snapshot: USDT 1,095 and ABC 4 | 1,095 | 4 | Establish a new boundary; explain the missing movement separately |
The checked closing USDT amount is 1,000 + 125 − 30 = 1,095. Applying the duplicate deposit would overstate it by 125, producing 1,220 after the withdrawal. Adding the absolute 1,125 at step 4 to the existing 1,125 would instead produce 2,250 at that step. Replacing the account with step 4's one-asset list would incorrectly discard four ABC.
A recovery snapshot can repair the current balance while leaving the historical explanation incomplete. It does not tell you which transfer, trade or charge caused an unexplained difference. Retain the prior state and recovery observation so that the correction remains auditable.
Join a snapshot to a stream without double counting
Bootstrap is difficult because the account can change while the snapshot request is in flight. A stream-first buffer helps retain evidence, but buffering alone does not establish which buffered events the snapshot already includes.
Suppose a snapshot showing 1,125 USDT already includes D1 from the example. Replaying a buffered +125 afterward would create a false 1,250 USDT. Conversely, discarding every buffered message could lose an event that occurred after the snapshot boundary.
Use this sequence when designing the handoff:
- Confirm the intended authenticated stream and retain incoming observations with their source metadata.
- Fetch the account snapshot for the identical account and field scope; preserve request, response and documented update information.
- Apply the provider's documented ordering and boundary rules to distinguish observations included in the snapshot from later ones.
- Reconcile the resulting state before permitting decisions that require a verified balance.
Do not borrow an order-book sequence procedure for a private wallet feed unless that feed documents the same guarantee. An event time, account-update time and local receipt time answer different questions. Comparing timestamps is insufficient when the provider has not established that they form a shared, complete ordering boundary.
If the contract lacks an exact handoff mechanism, label the view provisional and use supported account and history queries to investigate the overlap. Repeated agreement during a quiet period is useful evidence, but it is not proof that every historical event arrived. The WebSocket recovery guide addresses the wider continuity problem.
Bybit provides another concrete bootstrap warning: its private wallet stream sends no initial snapshot when subscription succeeds, and unrealized-PnL changes do not trigger wallet events. A successful subscription therefore cannot initialize all balances or prove that a valuation stayed unchanged during silence. These are explicit limits in the Bybit wallet-stream documentation.
Deduplicate movements without erasing legitimate changes
An additive consumer needs an identity for the economic movement under the provider's contract. Scope that identity to the appropriate account and record type. Two equal-sized deposits can be different deposits; two events sharing a millisecond can be different events.
A timestamp-plus-amount hash is not automatically a safe movement key. It may collapse two legitimate movements or miss the same movement represented differently. Binance's published balanceUpdate payload supplies event time, asset, delta and clear time; it does not label any of these fields as a unique transfer identifier. Do not invent a stronger uniqueness guarantee than the contract provides.
Where a supported durable identity exists, commit the “already applied” record and balance effect together. Otherwise a crash after changing the balance but before recording the identity can cause a replay to apply it again. Retaining deduplication records only in memory creates the same risk after restart.
Where a stream lacks sufficient identity or continuity evidence, consider using its events to request fresh absolute state and using supported transaction history for the movement ledger. This is an integration design choice requiring reconciliation, not a claim that every venue exposes a complete replayable ledger.
Absolute updates need their own safeguards. Applying the same absolute observation twice is harmless only if a newer observation has not intervened. A delayed 1,125 observation must not overwrite a justified later 1,095 state. Nor should a consumer simply keep the largest balance: legitimate withdrawals and purchases reduce balances. Resolve precedence through documented version or time semantics, or expose the conflict for recovery.
Validate the resulting state and its explanation
For a single native-asset balance with complete movement coverage between two known boundaries, check:
Closing balance = opening balance + sum of distinct signed movements
Use exact decimal values and compare matching fields. A sum of transfers cannot explain a field also affected by fees and trades unless those effects are included. A USD-equivalent valuation can change with prices even when native asset quantities do not.
Keep three acceptance questions separate: does the current absolute state agree, is the event history complete for the interval, and are the causes explained? The recovery snapshot in step 6 can answer the first while D2's record is still missing. Plan retrieval before records age out; history retention and export coverage explains why an endpoint's lookback limit can constrain later investigation.
Replay at least the worked example's duplicate, partial asset update, overlap and gap. Also restart the consumer between applying D1 and receiving its duplicate; deliver an older absolute update after the recovery snapshot; and process two distinct movements with the same timestamp. Each exercise should produce either the justified state or an explicit unresolved condition, never an unexplained adjustment chosen to make totals match.
A verified balance is still only one input to an action. Check exchange trading modes and status separately before submitting an instruction: matching cash does not establish that the requested order type is currently permitted.
Before using a balance to authorize another order, write down which values replace, which values add, how duplicate movements are identified and where the last reconciled boundary sits. Run the replay fixtures against those four rules. A balance becomes useful when its amount and the evidence supporting it survive the same recovery process.
