# Fees

Read effective spot fee rates, reserve rates, scheduled changes, and the fee breakdown with client.fees.

`client.fees` returns the spot fee rates in force for an account target, one row per listed market. It is authenticated and account-scoped (`account` / `sub_account_id`). These are the live effective rates, not a guaranteed quote of every future fill: the completed [user trade](https://testnet.polyester.com/docs/sdk/python/reference/trades) owns the exact charge.

## Get spot fee rates

```python
await client.wait_for_catalogs()
rates = await client.fees.get_spot_fee_rates(
    symbol_id=client.catalogs.symbol_id_for_symbol("BTC-USDT"),  # optional: int or list of ints
)
for rate in rates.fee_rates:
    print(rate.symbol, rate.maker_fee_rate_percent, rate.taker_fee_rate_percent, rate.vip_tier)
    print(rate.reserve_fee_rate_percent, rate.post_only_reserve_fee_rate_percent)
    if rate.next_change_at is not None:
        print("refetch at", rate.next_change_at)
```

Rates are decimal percentage strings with no percent sign. Parse them with `Decimal` or integer arithmetic, never binary floats.

## SpotFeeRate

- **`symbol_id` / `symbol`**: Market; `symbol` is filled from the catalog
- **`maker_fee_rate_percent`**: Effective maker rate; negative is a rebate
- **`taker_fee_rate_percent`**: Effective taker rate
- **`vip_tier`**: VIP tier used to resolve these rates
- **`reserve_fee_rate_percent`**: Rate a new order reserves its quote fee at
- **`post_only_reserve_fee_rate_percent`**: Same, for a new post-only limit order
- **`next_change_at`**: Next scheduled change, or `None`
- **`breakdown`**: Components of the effective rates, or `None`

The reserve rate is the highest maker or taker rate of every VIP tier under the fee schedule in force on the market, so it is at least the effective rates and discounts never lower it. The post-only reserve rate is the same from maker rates only.

`next_change_at` is a timezone-aware UTC `datetime`, the earliest time a fee schedule, discount, VIP tier, or fee policy starts or ends. Refetch rates at that time: the rates can change then, or stay the same.

## Size a buy

A buy limit order that pays its fee in the quote Asset is sized with `reserve_fee_rate_percent`, or with `post_only_reserve_fee_rate_percent` for a post-only order. A buy sized by a quote amount (`max_quote_debit`) reserves at the lower of the reserve and taker rates. Fills charge the effective rate, and the rest of the reserve is released as the order fills or ends.

Work in integer quote units of 10^-18, whatever the quote Asset's own decimals. The order reserves `N + F`: `N` is its price times its quantity, rounded up, and `F` is `N` times the rate, rounded up. It has sufficient quote balance when `N + F` is at most the asset's `available` balance. Other order validation and market limits still apply. The SDK has no helper for this.

More on the model: [Fees and VIP](https://testnet.polyester.com/docs/developer-docs/account/fees-and-vip).

## Breakdown

`breakdown` is a `SpotFeeRateBreakdown`. Optional components may be `None`, and absent account discounts decode as an empty list.

- **`tier_maker_fee_rate_percent` / `tier_taker_fee_rate_percent`**: rates of the account's VIP tier, before the market multiplier and discounts.
- **`market_multiplier`**: `SpotFeeMultiplier` in force on this market, with `maker_multiplier_percent`, `taker_multiplier_percent`, `effective_from`, and `effective_to`.
- **`account_discounts`**: list of `SpotFeeDiscount` in force, with `maker_discount_percent`, `taker_discount_percent`, `all_markets`, `effective_from`, and `effective_to`.
- **`referral_discount`**: a `SpotFeeDiscount`, when one is in force.

## Related

- [Balances](https://testnet.polyester.com/docs/sdk/python/reference/balances)
- [Orders](https://testnet.polyester.com/docs/sdk/python/reference/orders)
- [User trades](https://testnet.polyester.com/docs/sdk/python/reference/trades)
