Live wallet balances and positions
The balances and positions channels: a wallet's portfolio, its positions, or one position, sent again whenever they change.
Subscribe to balances with wallet: the wallet portfolio answer (its first 100 holdings), and again each time it changes. Subscribe to positions with wallet: the wallet positions answer for its 200 most recently traded tokens; with token too (an address, or native), position messages carry that one position. Each message replaces the one before; a new block or balancesAt alone is no change, and is not sent.
Subscribe:
{"op":"subscribe","id":"b","channel":"balances","chain":"solana","wallet":"Fm1Nzv3EcgGMW2MFHHGKjdVDbvHsMMtoRdoBDmUmsFFE"}
{"op":"subscribe","id":"q","channel":"positions","chain":"solana","wallet":"Fm1Nzv3EcgGMW2MFHHGKjdVDbvHsMMtoRdoBDmUmsFFE","token":"native"}
| Trigger | Why |
|---|---|
| An indexed block moved the wallet | A swap it made, or an indexed transfer from or to it. Read at once, before other wallets. |
| Every 30 s | Prices move, and some changes are not indexed: gas and BNB moved by contracts on BSC, anything outside DEX transactions on Solana. |
| The server's feed is back after an outage | Everything followed is read again. |
Balances come from the chain, so their reads are limited: a wallet's at most once every 5 s however many follow it, and at most 5 wallets a second per chain, moved wallets first. A socket holds few balances subscriptions (live.balances in GET /plans). A read that fails is tried again later, doubling the wait, and the last message stands meanwhile. Positions come from the index: a topic is read at most once a second.
Captured on a test instance: balances, trimmed to 2 holdings:
{"type":"subscribed","id":"b1","channel":"balances","chain":"evm:56","wallet":"0x3304e22ddaa22bcdc5fca2269b418046ae7b566a"}
{
"id": "b1",
"type": "balances",
"data": {
"chain": "evm:56",
"wallet": "0x3304e22ddaa22bcdc5fca2269b418046ae7b566a",
"indexedSince": 1790546261920,
"labels": [
"sniper",
"fresh"
],
"fundedBy": {
"address": "0x8894e0a0c962cb723c1976a4421c95949be2d4e3",
"amount": "0.25",
"time": 1790616461920,
"tx": "0x124aa51a8b73a7c373249b80ec4beb9fa4e80b4aa78a8bc73a64c781b3f2925d",
"labels": []
},
"fundedByReason": null,
"block": 1000748,
"balancesAt": 1790618266812,
"totalValueUsd": 214134,
"pnl": {
"realizedUsd": 59.5089770354906,
"unrealizedUsd": 6.776263578034403e-14,
"totalUsd": 59.50897703549067
},
"holdings": [
{
"kind": "token",
"token": {
"address": "0xa9fcda9bd321adeb7cbbbd30ef2dd0ed1d657777",
"symbol": "TAX",
"name": "Tax Token",
"decimals": 18
},
"balance": "21500000",
"priceUsd": 0.006,
"valueUsd": 129000,
"allocation": 0.6024265179747261,
"thin": false,
"position": {
"quote": false,
"trades": 22,
"buys": 12,
"sells": 10,
"unpricedTrades": 0,
"bought": "20118000",
"sold": "9800",
"open": "20108200",
"boughtUsd": 1008,
"soldUsd": 60,
"avgBuyPriceUsd": 0.00005010438413361169,
"avgSellPriceUsd": 0.006122448979591836,
"priceUsd": 0.006,
"priceAt": 1790618400000,
"realizedPnlUsd": 59.5089770354906,
"unrealizedPnlUsd": null,
"totalPnlUsd": 59.5089770354906,
"firstTradeAt": 1790546864920,
"lastTradeAt": 1790618578665
}
},
{
"kind": "lp",
"protocol": "pancakeswap-v2",
"pool": "0x702e6533e0182381606adbe3ccbb77f682ccc58b",
"position": null,
"lpToken": {
"address": "0x702e6533e0182381606adbe3ccbb77f682ccc58b",
"balance": "2000000000000000000000",
"supply": "100000000000000000000000",
"share": 0.02
},
"tokens": [
{
"token": {
"address": "0xa9fcda9bd321adeb7cbbbd30ef2dd0ed1d657777",
"symbol": "TAX",
"name": "Tax Token",
"decimals": 18
},
"amount": "14000000",
"fees": null,
"raw": {
"amount": "14000000000000000000000000",
"fees": null
},
"priceUsd": 0.006,
"valueUsd": 84000
},
{
"token": {
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"symbol": "WBNB",
"name": "Wrapped BNB",
"decimals": 18
},
"amount": "0.14",
"fees": null,
"raw": {
"amount": "140000000000000000",
"fees": null
},
"priceUsd": 600,
"valueUsd": 84.00000000000001
}
],
"range": null,
"valueUsd": 84084,
"feesUsd": null,
"allocation": 0.39267001036734006,
"thin": false
}
],
"unpriced": 0,
"count": 6,
"liquidity": {
"protocols": [
"pancakeswap-v2",
"pancakeswap-v3",
"uniswap-v2",
"uniswap-v3"
],
"incomplete": [],
"notCovered": [
{
"protocol": "pancakeswap-infinity-cl",
"reason": "its positions are not enumerable per owner"
}
],
"error": null
}
}
}
Captured on a test instance: one position, then the wallet buys more:
{"type":"subscribed","id":"q1","channel":"positions","chain":"evm:56","wallet":"0x3304e22ddaa22bcdc5fca2269b418046ae7b566a","token":"0xa9fcda9bd321adeb7cbbbd30ef2dd0ed1d657777"}
{"id":"q1","type":"position","data":{"chain":"evm:56","wallet":"0x3304e22ddaa22bcdc5fca2269b418046ae7b566a","labels":["sniper","fresh"],"indexedSince":1790546261920,"position":{"token":{"address":"0xa9fcda9bd321adeb7cbbbd30ef2dd0ed1d657777","symbol":"TAX","name":"Tax Token","decimals":18},"quote":false,"trades":22,"buys":12,"sells":10,"unpricedTrades":0,"bought":"20118000","sold":"9800","open":"20108200","boughtUsd":1008,"soldUsd":60,"avgBuyPriceUsd":0.00005010438413361169,"avgSellPriceUsd":0.006122448979591836,"priceUsd":0.006,"priceAt":1790618400000,"realizedPnlUsd":59.5089770354906,"unrealizedPnlUsd":null,"totalPnlUsd":59.5089770354906,"firstTradeAt":1790546864920,"lastTradeAt":1790618578665}}}
{"id":"q1","type":"position","data":{"chain":"evm:56","wallet":"0x3304e22ddaa22bcdc5fca2269b418046ae7b566a","labels":["sniper","fresh"],"indexedSince":1790546261920,"position":{"token":{"address":"0xa9fcda9bd321adeb7cbbbd30ef2dd0ed1d657777","symbol":"TAX","name":"Tax Token","decimals":18},"quote":false,"trades":23,"buys":13,"sells":10,"unpricedTrades":0,"bought":"20168000","sold":"9800","open":"20158200","boughtUsd":1308,"soldUsd":60,"avgBuyPriceUsd":0.00006485521618405395,"avgSellPriceUsd":0.006122448979591836,"priceUsd":0.006,"priceAt":1790618400000,"realizedPnlUsd":59.36441888139627,"unrealizedPnlUsd":null,"totalPnlUsd":59.36441888139627,"firstTradeAt":1790546864920,"lastTradeAt":1790618628209}}}
Errors: 400 (wallet missing), 429 too_many_balances, and as the routes answer them (422, 502).