# Balances Channel


Source: https://docs.stxapp.io/websockets/channels/balances/

Topic: `balances:{user_id}`

Delivers one account's balance and fee summary as it changes, plus notifications
about your payments. Money arrives as dollar strings; see
[Wire format](/websockets/channels/wire-format/).

This channel is scoped to an account rather than to markets, so it takes no
`market_ids` filter. It takes an optional `account_id` instead.

## Balances object

### Identity

- `account_id` : The id of the account.
- `user_id` : The id of the user that owns the account.

### Balances and liabilities

- `account_balance` : The account's cash balance. Unaffected by placing an order;
  not all of it may be available.
- `available_balance` : The balance available to withdraw or place additional orders
  with.
- `buy_order_liability` : The account's total liability from buy orders, including
  the potential trade fee reserve.
- `sell_order_liability` : The account's total liability from sell orders, including
  the potential trade fee reserve.
- `position_premium_liability` : The account's total liability from position
  premiums. Routinely negative.
- `escrow` : The account's escrow balance.

:::caution[These four are rounded, in opposite directions]
`available_balance` is rounded **down** to the cent and the three liabilities
**up**, in both signs, so neither overstates what you can spend nor understates what
you owe. They therefore need not reconcile to the cent; treat each as authoritative
on its own rather than deriving one from the others.
:::

### Lifetime totals

- `total_deposits` : The account's total deposits.
- `total_withdrawals` : The account's total withdrawals.
- `total_adjustments` : The account's total balance adjustments.
- `total_settlement_pnl` : Total gross profit and loss from all settlements.
- `total_fees` : Total fees from all settlements and other fees.
- `total_trade_count` : The number of trades the account has made across every
  market. An integer.
- `total_traded` : The risk the account has committed across every market.

### Loyalty

- `loyalty_tier` : The tier the account is in, one of `rookie`, `veteran`,
  `all_star`, `mvp` or `hall_of_fame`. Every account has one; new accounts start at
  `rookie`.
- `points` : Loyalty points the account has accumulated. A whole number, not a money
  string.

### Fees

- `fee_schedule` : The account's fee schedule, one of `fixed_percent`,
  `revenue_share`, `loyalty_tier`, `fixed_percent_market_group`,
  `fixed_percent_event`, `on_trade` or `loyalty_tier_on_trade`. See
  [Fees](/concepts/fees/).
- `base_fee_percent` : The fee percentage, when the account is on the
  `fixed_percent` schedule. Null for any other schedule. A number, not a money
  string.
- `taker_factor` / `maker_factor` : Per-trade fee factors, present only for the
  `on_trade` and `loyalty_tier_on_trade` schedules. Null otherwise. Numbers, not
  money strings.

## Use Cases

| Use case | Message to send |
| --- | --- |
| Join and receive the balance summary for your account | `["3","3","balances:<user_id>","phx_join",{}]` |
| Join for a specific account you own | `["3","3","balances:<user_id>","phx_join",{"account_id":"<uuid>"}]` |
| Check the connection is alive | `["3","4","balances:<user_id>","ping",{}]` |

### Joining

```json
["3","3","balances:<user_id>","phx_join",{}]
```

The reply is empty; there is no filter to echo:

```json
{"status":"ok","response":{}}
```

### Naming an account

A user may hold more than one account. Omit `account_id` and you get the one the
socket resolved when it connected; name one to reach another:

```json
["3","3","balances:<user_id>","phx_join",{"account_id":"<uuid>"}]
```

`GET /api/v1/me` returns your `account_id`. An id belonging to another user (or one
that is not a valid UUID) fails the join with `unauthorized`, which does not reveal
whether the account exists:

```json
{"status":"error","response":{"reason":"unauthorized"}}
```

If you join twice, once per account, each socket receives only its own account's
frames.

### Initial response after joining

The join event is `balances`, and it carries the same object that later `update`
frames carry.

```json
[null, null, "balances:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "balances", Balances]
```

```json
[
  null,
  null,
  "balances:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "balances",
  {
    "account_id": "c1c3614c-32ae-4bea-bae4-fc3f52047790",
    "user_id": "7fb55544-3a7e-4f6b-bd66-fc89bd3a1087",
    "account_balance": "154.7500",
    "available_balance": "91.3000",
    "buy_order_liability": "20.4500",
    "sell_order_liability": "28.0000",
    "position_premium_liability": "15.0000",
    "escrow": "0.0000",
    "total_deposits": "150.0000",
    "total_withdrawals": "0.0000",
    "total_adjustments": "0.0000",
    "total_settlement_pnl": "5.0000",
    "total_fees": "0.2500",
    "total_trade_count": 4,
    "total_traded": "185.8220",
    "loyalty_tier": "rookie",
    "points": 186,
    "fee_schedule": "on_trade",
    "base_fee_percent": null,
    "taker_factor": 0.02,
    "maker_factor": 0.01
  }
]
```

### Pushed when the summary changes

```json
[null, null, "balances:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "update", Balances]
```

```json
[
  null,
  null,
  "balances:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "update",
  {
    "account_id": "c1c3614c-32ae-4bea-bae4-fc3f52047790",
    "user_id": "7fb55544-3a7e-4f6b-bd66-fc89bd3a1087",
    "account_balance": "159.5000",
    "available_balance": "91.0500",
    "buy_order_liability": "20.4500",
    "sell_order_liability": "28.0000",
    "position_premium_liability": "20.0000",
    "escrow": "0.0000",
    "total_deposits": "150.0000",
    "total_withdrawals": "0.0000",
    "total_adjustments": "0.0000",
    "total_settlement_pnl": "10.0000",
    "total_fees": "0.5000",
    "total_trade_count": 5,
    "total_traded": "218.4400",
    "loyalty_tier": "rookie",
    "points": 218,
    "fee_schedule": "on_trade",
    "base_fee_percent": null,
    "taker_factor": 0.02,
    "maker_factor": 0.01
  }
]
```

:::caution[Balance pushes are event-driven, not price-driven]
This channel fires when you deposit or withdraw, place or cancel, get filled, or a
market settles. It does **not** fire when prices move, so marking your
[positions](/websockets/channels/positions/) to market is your job.
:::

### Pushed when a payment's status changes

Sent as your payments are processed.

- `id` : The id of the payment.
- `account_id` : The id of the account the payment is for.
- `provider` : The name of the payment provider, e.g. `paysafe`.
- `provider_id` : The transaction id from the provider.
- `status` : `pending_approval`, `initiated`, `received`, `pending`, `held`,
  `completed`, `failed` or `cancelled`.
- `data` : Provider-specific details about the payment. Shape varies by provider and
  payment method.
- `type` : `deposit`, `withdrawal` or `adjustment`.
- `amount` : The payment amount, as a dollar string.

```json
[
  null,
  null,
  "balances:a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "payment_update",
  {
    "id": "73335df1-37ef-434d-aa4e-7ba1f985ca00",
    "account_id": "b6e736c0-926f-4150-b613-fe3da9ef2a3f",
    "provider": "paysafe",
    "provider_id": "25dd5124-11f5-46fa-b49e-48acb6bb53d1",
    "status": "completed",
    "data": {"handle": "SCtzjJMtI2CrWKSk", "method": "card"},
    "type": "deposit",
    "amount": "100.0000"
  }
]
```

Interac SendMoney deposits are not pushed at `initiated` or `pending`; the first
frame you see for one is at a later status.
