# Fees

Read effective spot fee rates, reserve rates, the next scheduled change, and the fee breakdown with the Go FeeService.

`client.Fees` reads the spot fee rates in force for an account on each Pair. It needs an API key. Rates are base-10 percentage strings without a percent sign.

## Methods

| Method            | Summary                            |
| ----------------- | ---------------------------------- |
| `GetSpotFeeRates` | One row per Pair, optionally by ID |

### GetSpotFeeRates(ctx, account, subAccountID, symbolID)

Pass `nil` for `symbolID` to read every listed Pair, or a `[]uint32` of numeric market IDs. Rows are ordered by numeric market ID.

```go
rates, err := client.Fees.GetSpotFeeRates(ctx, nil, nil, nil)
if err != nil { log.Fatal(err) }
for _, rate := range rates.FeeRates {
    fmt.Println(rate.Symbol, rate.MakerFeeRatePercent, rate.TakerFeeRatePercent)
    fmt.Println(rate.ReserveFeeRatePercent, rate.PostOnlyReserveFeeRatePercent)
    if rate.NextChangeAt != nil {
        fmt.Println("refetch at", rate.NextChangeAt)
    }
    if rate.Breakdown != nil {
        fmt.Println(rate.Breakdown.TierTakerFeeRatePercent, len(rate.Breakdown.AccountDiscounts))
    }
}
```

## SpotFeeRate fields

- **`SymbolID` / `Symbol`**: The Pair
- **`MakerFeeRatePercent`**: Effective maker rate; a negative rate is a rebate
- **`TakerFeeRatePercent`**: Effective taker rate
- **`VIPTier`**: VIP tier used to resolve these rates
- **`ReserveFeeRatePercent`**: Rate a new order reserves its quote fee at
- **`PostOnlyReserveFeeRatePercent`**: Rate a new post-only limit order reserves its quote fee at
- **`NextChangeAt`**: `*time.Time`: earliest scheduled change, or `nil`
- **`Breakdown`**: `*models.SpotFeeRateBreakdown`: components of the rates

The effective rates are the live rates, not a quote of every future fill. The completed [user trade](https://testnet.polyester.com/docs/sdk/go/reference/trades) holds the exact charge.

`NextChangeAt` is the earliest time a component of these rates starts or ends. The rates can change then, or stay the same, so refetch at that time. It is `nil` when no change is scheduled.

## Size a buy order

- A buy limit order that pays its fee in the quote Asset is sized with `ReserveFeeRatePercent`.
- A post-only limit order uses `PostOnlyReserveFeeRatePercent` instead.
- A buy sized by a quote amount (`MaxQuoteDebitScaled`) reserves at the lower of `ReserveFeeRatePercent` and `TakerFeeRatePercent`.

The reserve rates are at least the effective rates, and discounts never lower them. In units of 10^-18 of the quote Asset, 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 enough quote balance when N + F is at most the available quote balance; other order validation and market limits still apply. Fills charge the effective rate, and the rest of the reserve is released as the order fills or ends.

## Breakdown

`models.SpotFeeRateBreakdown` explains the effective rates:

- `TierMakerFeeRatePercent` / `TierTakerFeeRatePercent`: the VIP tier's rates, before the market multiplier and discounts.
- `MarketMultiplier`: the fee schedule in force on this market, or `nil` when none is. It has `MakerMultiplierPercent`, `TakerMultiplierPercent`, `EffectiveFrom`, and `EffectiveTo`.
- `AccountDiscounts`: account discounts in force, empty when there are none. The highest maker discount and the highest taker discount apply, each possibly from a different discount.
- `ReferralDiscount`: the referral discount in force, or `nil` when none is.

A discount has `MakerDiscountPercent`, `TakerDiscountPercent`, `AllMarkets`, `EffectiveFrom`, and `EffectiveTo`. Optional components and times may be absent.

## Related

- [Orders](https://testnet.polyester.com/docs/sdk/go/reference/orders)
- [User trades](https://testnet.polyester.com/docs/sdk/go/reference/trades)
- [Trading guide](https://testnet.polyester.com/docs/sdk/go/guides/trading)
- [Fees and VIP](https://testnet.polyester.com/docs/developer-docs/account/fees-and-vip)
