PerpForge
Get started

Concept · Maker/Taker Fees

Maker/Taker Fees

On a perpetual futures exchange, two fee tiers exist: a lower "maker" rate for orders that add liquidity to the book, and a higher "taker" rate for orders that remove liquidity immediately.

Maker/Taker Fees

On a perpetual futures exchange, two fee tiers exist: a lower "maker" rate for orders that add liquidity to the book, and a higher "taker" rate for orders that remove liquidity immediately.

In plain English

When you trade on a PERP (perpetual futures — a derivative with no expiry date that tracks the spot price via a funding mechanism) exchange, the exchange charges a fee based on how you filled your order:

  • Taker fee: You "take" liquidity that already exists on the order book. Any market order is a taker order. A limit order is also a taker if it immediately matches an existing resting order. You pay more because you demanded instant execution.
  • Maker fee: You "make" new liquidity by posting a limit order that sits on the book and waits. When someone else matches against your resting order, you pay less — you provided a service to the exchange.

On Binance USDT-perp (as of 2026): taker ≈ 5 bps (0.05%), maker ≈ 2 bps (0.02%). On Orderly Network, the maker fee has historically been negative — a rebate (the exchange pays you to post limit orders).

Why the split matters financially

At 5% position sizing on $10,000 equity ($500 per trade):

  • Taker/taker round trip: 2 × 5 bps × $500 = ~$0.50 per trade
  • Maker/maker round trip: 2 × 2 bps × $500 = ~$0.20 per trade

Over 400 trades (typical for 1h–4h swing strategies in this fleet), the difference is ~$120 — roughly 1.2% of starting equity lost to fees at the taker rate vs ~0.5% at the maker rate.

How a candle-based backtest engine simulates it

The engine needs to distinguish how each order was filled:

  1. Market order (always taker): Fill at candle close, apply takerFeeBps.
  2. Limit order (maker candidate): Set a target price P. Check each candle's range:
    • Long entry: if (candle.low <= P) → fills at P, apply makerFeeBps
    • Short entry: if (candle.high >= P) → fills at P, apply makerFeeBps

This is the same OHLC-range check the engine already uses for TP/SL exits (see fill-resolver.ts:26, the overrideFillPrice path). The machinery exists; it needs to be generalized to entries.

What this engine currently does (the gap)

fill-resolver.ts:54 resolves every backtest fill to candle.close with a flat cfg.feeBps — a pure taker model. No order types exist. No maker/taker distinction. The simulator-fidelity.md concept catalogs this as a known gap.

Why adding maker/taker is deeper than it looks

Limit orders change the set of trades that fire, not just the fee. A limit entry 0.2% below the current close only fills if the next candle's low reaches that price. If it doesn't, the trade is missed entirely. A strategy with 450 taker trades might have 340 limit-entry trades with identical conditions — different trade count, different win rate, different Sharpe.

Consequences for simulation design:

  • Each strategy needs a limitPriceFn — how far off the close to set the limit (a new optimizable axis, new overfitting risk)
  • Intra-candle fill timing is unknowable from OHLC data alone (the candle's high and low happened in some order; you can't know when your limit was hit)

See simulator fidelity for the full gap catalog and limit order for the fill mechanics in detail.

Examples from the live fleet

The current engine applies a flat 17 bps to every fill. All metrics below are at that rate.

  • id655 — EMA 21/50 · BTC · 1h · 1× long — 436 trades. At maker/maker (2 bps each leg), estimated fee savings vs current flat 17 bps: ~$660 over the backtest window. This is a hold-heavy swing strategy — it would benefit most from maker pricing.
  • id523 — EMA 21/50 · SOL · 1h · 2× long — 436 trades, same window. Same fee-savings order of magnitude. Significant edge (per significance classifier); maker pricing would widen that edge.

Both examples are directional at this point only — the engine does not yet model maker fills, so actual trade count could differ with limit entries.

Related

Sources

  • wiki/qa-sessions/2026-06-29-session.md#q2 (first asked here)
  • apps/backend/src/evaluation/position/fill-resolver.ts
  • apps/backend/src/evaluation/position/fill-resolver.ts:26 (overrideFillPrice — the existing OHLC-range fill path)

Related concepts

See it in a real result →

Put it to the test

Does your idea have a real edge, or just a big number?

Spawn your variant, run it on the same engine, and read the edge-significance verdict — before you risk real money.

Test your own idea — free →Free account, no card