# Portfolio tracker

Build a revision-aware Rust account monitor for balances, reservations, open orders, fills, and reconciliation.

Build an authenticated account monitor that keeps exact funding and trading balances separate, tracks reservations and open orders, and uses revisions to reject stale balance updates.

> **A dashboard is not the source of truth**
>
> Treat every screen as a projection of ledger and OMS state. Preserve raw scaled values, revisions, and timestamps. Never calculate spendable collateral from rounded display strings.

1. Read the balance snapshot

   ```rust
   use polyester::proto::ledger::read::v1::GetBalancesRequest;

   let balances = client.balances.list(GetBalancesRequest::default()).await?;
   for balance in &balances.balances {
       println!(
           "{} available={} reserved={} trading_revision={} funding_revision={}",
           balance.asset_id,
           polyester::codecs::scalars::format_ledger_u128(&balance.available, 18)?,
           polyester::codecs::scalars::format_ledger_u128(&balance.reserved, 18)?,
           balance.trading_revision,
           balance.funding_revision,
       );
   }
   ```

   The balance components are raw ledger `u128` strings at ledger scale 18. The formatter is for display only; retain the raw strings in the tracker.

   `funding` is deposit/withdraw custody state. `trading`, `reserved`, and `available` are execution state. Their revisions are independent; compare `trading_revision` only with trading-state updates and `funding_revision` only with funding-state updates.

2. Reconcile open orders

   ```rust
   use polyester::models::ListOpenOrdersOpts;

   let open = client
       .orders
       .list_open_with(ListOpenOrdersOpts {
           include_attached_risk: true,
           include_attached_risk_state: true,
           ..Default::default()
       })
       .await?;
   for order in &open.orders {
       println!(
           "{} {} {} {} {:?} {:?} {:?}",
           order.order_id,
           order.client_order_id,
           order.symbol_id,
           order.status,
           order.orig_qty,
           order.cum_qty,
           order.leaves_qty,
       );
   }
   ```

   Reserved balance and open-order leaves should move together eventually, but they are served by different read models and can lag briefly. Do not flag a mismatch until a bounded reconciliation window and a fresh read have both completed.

3. Subscribe for low-latency order changes

   ```rust
   let mut subscription = client
       .orders
       .subscribe(client.default_account_id.as_deref())
       .await?;
   if let Some(order) = subscription.recv_result().await? {
       let scale = client
           .catalogs
           .base_quantity_scale_for_symbol_id(order.symbol_id)
           .ok_or_else(|| polyester::Error::validation("stream symbol quantity scale is unavailable"))?;
       let cum = match &order.cum_qty {
           Some(qty) => Some(qty.format(Some(scale))?),
           None => None,
       };
       let leaves = match &order.leaves_qty {
           Some(qty) => Some(qty.format(Some(scale))?),
           None => None,
       };
       println!("{} {} {:?} {:?}", order.order_id, order.status, cum, leaves);
   }
   if let Some(err) = subscription.err() {
       eprintln!("{err}");
   }
   ```

   The private stream accelerates the UI; it does not replace snapshots. On reconnect or queue overflow, discard local assumptions and reconcile balances plus open orders again. Stream quantities can omit scale, so resolve it from the hydrated catalog by `symbol_id` and fail closed if unavailable; never invent a stream scale.

4. Resolve fills before computing inventory

   ```rust
   use std::time::Duration;

   let details = client
       .orders
       .wait_for_order_trades_complete(
           None,
           Some("order-id-from-create"),
           Duration::from_secs(15),
       )
       .await?;
   if let Some(order) = details.order {
       println!("{:?} {}", order.cum_qty, details.trades.len());
   }
   ```

   The order projection can show cumulative quantity before every related trade row is queryable. Use the completed trade projection for fee-source-aware received inventory and realized execution reporting.

5. Poll as a safety net

   Poll slowly enough to respect rate limits, add jitter across replicas, and set one overall `tokio::time::timeout` per cycle. A useful cycle is balances → open orders → recent history. Publish the new dashboard state atomically only after all required reads succeed; otherwise retain the previous state and mark it stale.

## Account-state API map

| Method                                  | Important inputs                            | Use                                    |
| --------------------------------------- | ------------------------------------------- | -------------------------------------- |
| `balances.list`                         | `GetBalancesRequest`                        | Funding/trading snapshot and revisions |
| `balances.get_balance_history`          | request options                             | Auditable balance movement             |
| `orders.list_open_with`                 | subaccount, pagination, attached-risk flags | Current reservation intent             |
| `orders.list_history_with`              | symbol, pagination                          | Terminal and recent order states       |
| `orders.get` / `get_with`               | order ID or client order ID                 | One order plus related fills           |
| `orders.subscribe`                      | account ID                                  | Low-latency private lifecycle updates  |
| `orders.wait_for_order_trades_complete` | order identity, overall duration            | Complete fill projection               |
| `trades.list`                           | request filters                             | User execution ledger                  |

## Tested source

Example `02` in [`polyester-examples-rust`](https://github.com/Fabric-Labs/polyester-examples-rust) performs authenticated balance and order reads. Every target compiles and passes Clippy against the exact `v0.1.0a22` SDK commit.

## Related

- [Accounts & balances](https://testnet.polyester.com/docs/sdk/rust/guides/accounts-and-balances)
- [Orders](https://testnet.polyester.com/docs/sdk/rust/reference/orders)
- [Trades](https://testnet.polyester.com/docs/sdk/rust/reference/trades)
- [Streaming](https://testnet.polyester.com/docs/sdk/rust/guides/streaming)
