Knowledge baseResearch

What your bot thinks your fees are — and what you pay

We measured where trading software gets your fee rate. On two of three venues it is a library default, not the exchange — and our account pays 3.6× more.

8 min read

Anton Shchur· Founder· Updated September 18, 2026

Every automated trading system needs one number before it can decide anything: the fee your account pays. A grid has to know whether its step clears a round trip. A funding-rate arbitrage has to know whether the carry survives two legs. A market maker has to know where its quote stops being worth placing.

On 18 September 2026 we measured where that number actually comes from in our own platform, using a read-only instrument against live accounts. On two of the three venues we support, it does not come from the exchange at all.

What we measured

Our instrument asks two questions in the same run:

  1. what the market description in the client library reports for a pair, and
  2. what `fetchTradingFee` returns for each connected account.

Library: ccxt 4.5.52. Venues: Binance, Bybit, OKX. Symbol: BTC/USDT, spot and perpetual.

Finding 1: none of the three venues states a fee in its market description

Every trading library exposes market.maker and market.taker. These fields always contain a number, which makes them look like a quote from the exchange. They are not.

In ccxt 4.5.52, Bybit spot markets get the literal 0.001 / 0.001 written in directly (bybit.js:1881). Bybit contracts get safeNumber(market, 'takerFee', 0.0006) — a default that applies whenever the public instruments endpoint does not carry a fee field, and it does not carry one. OKX gets the same treatment from its own defaults block (okx.js:665-678).

We checked the raw venue response behind each of the six rows. Not one of the three venues declares a maker/taker rate in its market description. All six numbers were library defaults.

That is not a flaw in the library — a market description is not a statement about your account, and it was never meant to be one. The flaw is in reading it as if it were.

Finding 2: our Bybit account pays roughly three times the library's number

library defaultour account (fetchTradingFee)
Bybit perpetualmaker 0.0100 % · taker 0.0600 %maker 0.0360 % · taker 0.1000 %
Bybit spotmaker 0.1000 % · taker 0.1000 %maker 0.1000 % · taker 0.1800 %
Binance perpetualmaker 0.0200 % · taker 0.0500 %maker 0.0200 % · taker 0.0500 %
Binance spotmaker 0.1000 % · taker 0.1000 %maker 0.1000 % · taker 0.1000 %

On Bybit perpetuals the maker rate our account pays is 3.6× the default, and the taker rate is 1.67×. This is not a reading error on the API side: we confirmed it against actual charges on 24 August 2026 — a limit entry cost 0.00116356 on a notional of 3.2321 (0.036 %), and a market order cost 0.00333615 on 3.3361 (0.100 %).

Finding 3: the library's number is also behind the venue's own published table

Bybit's published fee page (updated 2 September 2026) lists non-VIP perpetuals at maker 0.02 % / taker 0.055 %. The library default is 0.01 % / 0.06 %. So there are three different numbers in play: the library default, the venue's published table, and what the account is actually charged — and they disagree with each other.

Why this quietly breaks software

Most systems resolve the fee with a ladder that reads as obviously correct:

account rate (fetchTradingFee)   →   market description   →   conservative fallback

The last step never runs. market.taker is always greater than zero, so the condition that would fall through to the fallback is never satisfied. Whatever careful number you put at the bottom of that ladder is dead code.

We found this in our own platform, and the cost was measurable. Our fallback for perpetuals had been raised to 0.036 % / 0.10 % in August, specifically because a measurement showed the round trip was being understated by up to 3.6×. It never applied once. A machine-assisted rewrite found 18 places reading the rate, against six that a manual search had found. Among them:

  • Funding-rate arbitrage computed a two-leg round trip at 0.10 % + 0.06 % instead of 0.15 % + 0.10 %. The break-even funding rate came out at 0.020 % instead of 0.031 % — so a funding rate of 0.025 % per 8h passed the entry gate and did not cover the round trip.
  • Cross-exchange arbitrage carried a second, separate fallback literal of its own, below the measured spot taker — inflating the net edge by 0.10 % per round trip.
  • The exchange card in the UI displayed a library default to the user as "your account's commission".

How to check this yourself

Three lines, against your own key:

const market = exchange.markets['BTC/USDT:USDT'];
console.log(market.taker, market.maker);            // what the library says
console.log(await exchange.fetchTradingFee('BTC/USDT:USDT'));  // what your account pays

And the question worth asking of your own code: when the account rate is unavailable, does your fallback actually run — or is it shadowed by a field that is never empty?

Limits of this measurement

State them plainly, because a measurement without its boundaries is a claim:

  • Three venues, our accounts. This is not a survey of the market. Your rates differ by VIP tier, region, and fee-token settings.
  • The OKX row is a testnet account. We have not measured a live OKX account's rates here.
  • The Bybit spot taker of 0.18 % rests on a single source — the account endpoint. It is not confirmed by any published Bybit table we could find, and we have not yet matched it against actual spot charges. Bybit does operate regional variants with an asymmetric spot schedule (its EU entity publishes maker 0.1 % / taker 0.25 %), which is the same shape but a different value.
  • Binance published rates were not fetched during this measurement; the account and library numbers agreed, so there was no dispute to resolve.

What we changed

The question "what is the rate" now has an owner that answers a different question first: did the venue state it at all. It reads the raw venue response rather than the normalised field, and returns "not stated" as a third answer — not a zero, and not a default dressed up as a quote. Where the venue says nothing, the measured conservative fallback applies, which is what it was measured for.

Reading is free. So is testing.

Paper trading costs nothing and needs no card. Build a system, backtest it on real candles, and decide from the result.