All guides

Tutorial

Detecting Crypto Position Changes from Balance Snapshots

Normalize trader balances and classify opened, increased, reduced and closed positions while avoiding false signals from prices, transfers and indexer updates.

By Fomo API 2 min read

Balance snapshots answer a different question from swaps: “What does this account hold now?” Comparing snapshots can reveal changes even when an event page is unavailable, but the comparison must separate token amount from market value.

Normalize each position

Use networkId:tokenAddress as the key and store amount and price independently.

def normalize_balances(response):
    items = response.get("responseObject", {}).get("balances", [])
    positions = {}

    for item in items:
        balance = item.get("balance") or {}
        token_result = item.get("tokenFilterResult") or {}
        token = token_result.get("token") or {}
        address = balance.get("tokenAddress")
        network_id = token.get("networkId")

        if not address or network_id is None:
            continue

        positions[f"{network_id}:{address}"] = {
            "networkId": network_id,
            "tokenAddress": address,
            "amount": balance.get("shiftedBalance", 0),
            "priceUsd": token_result.get("priceUSD"),
            "symbol": token.get("symbol"),
        }

    return positions

Classify amount transitions

PreviousCurrentClassification
0> 0Opened
> 0LargerIncreased
> 0Smaller but positiveReduced
> 00 or absentClosed

Use decimal arithmetic and a token-specific tolerance. Tiny dust changes should not create high-priority alerts.

Do not compare value alone

USD value can increase because price moved while the amount stayed constant. Track amount change, price change and value change separately. Respect valuation flags before including an item in portfolio equity.

Correlate with events

A matching swap strengthens the interpretation, but other causes remain possible:

  • Wallet transfer or deposit
  • Withdrawal
  • Token rebase
  • Decimal or metadata correction
  • Delayed indexing
  • Cross-network movement

Label unverified changes as observations. Before execution, confirm the related transaction and current state through the target chain.

Store complete snapshots

Write an immutable snapshot with observedAt, source response ID if available and raw payload reference. Derive transitions in a separate table. This allows replay when comparison logic changes and supports historical portfolio charts.

Balance comparison is excellent for monitoring and reconciliation. It should complement, not replace, swap events and onchain verification.