---
title: "Maker/Taker Fee Comparison"
description: "Compare flat fees vs split maker/taker fees and see how fee structure affects backtest P&L."
icon: "scale-balanced"
---

Compare flat fees versus split maker/taker fees to understand how fee structure impacts strategy performance.

## Full Code

```python
"""Compare flat fees vs maker/taker split fees on the same strategy."""

import horizon as hz


def fair_value(ctx: hz.Context) -> float:
    return ctx.feed.price * 1.01


def quoter(ctx: hz.Context, fair: float):
    if fair > ctx.feed.price + 0.02:
        return hz.quotes(ctx.feed.price, spread=0.04, size=10)


# Sample data
import random
random.seed(42)
price = 0.50
data = []
for i in range(500):
    price += random.gauss(0, 0.01)
    price = max(0.01, min(0.99, price))
    data.append({"timestamp": 1700000000 + i, "price": round(price, 4)})


# --- Flat fee ---
flat_result = hz.backtest(
    name="flat-fee",
    markets=["test"],
    data=data,
    pipeline=[fair_value, quoter],
    paper_fee_rate=0.001,
)

# --- Split maker/taker ---
split_result = hz.backtest(
    name="split-fee",
    markets=["test"],
    data=data,
    pipeline=[fair_value, quoter],
    paper_maker_fee_rate=0.0002,
    paper_taker_fee_rate=0.002,
)

print("=== Flat Fee (10 bps) ===")
print(flat_result.summary())
print(f"Total fees: ${flat_result.metrics.total_fees:.4f}")

print("\n=== Split Fee (2 bps maker / 20 bps taker) ===")
print(split_result.summary())
print(f"Total fees: ${split_result.metrics.total_fees:.4f}")

# Inspect maker/taker breakdown
maker_fills = [f for f in split_result.trades if f.is_maker]
taker_fills = [f for f in split_result.trades if not f.is_maker]
print(f"\nMaker fills: {len(maker_fills)}")
print(f"Taker fills: {len(taker_fills)}")
```

## How It Works

### Flat fees (default)

A single `paper_fee_rate` is applied to every fill regardless of whether the order was resting in the book (maker) or crossing the spread (taker).

```python
engine = Engine(paper_fee_rate=0.001)  # 10 bps on all fills
```

### Split maker/taker fees

Set different rates for each role. Makers add liquidity and typically pay lower fees. Takers remove liquidity and pay higher fees.

```python
engine = Engine(
    paper_maker_fee_rate=0.0002,  # 2 bps for makers
    paper_taker_fee_rate=0.002,   # 20 bps for takers
)
```

You can also override just one side. The other falls back to `paper_fee_rate`:

```python
engine = Engine(
    paper_fee_rate=0.001,          # 10 bps default
    paper_taker_fee_rate=0.003,    # 30 bps for takers only
)
# Makers still pay 10 bps
```

### Checking fill type

Every `Fill` includes an `is_maker` boolean:

```python
fills = engine.recent_fills()
for f in fills:
    role = "maker" if f.is_maker else "taker"
    print(f"{f.fill_id}: {role} fee={f.fee:.6f}")
```

## When to Use Split Fees

- **Backtesting market-making strategies** where most fills should be maker fills at lower fees
- **Modeling real exchange fee tiers** (Polymarket charges 0 bps maker / 100 bps taker)
- **Comparing strategy profitability** under different fee structures
- **Stress testing** to see if your edge survives higher taker fees

<Note>
  In the paper exchange, maker/taker classification is based on the order price relative to the mid price at fill time. For more realistic classification using L2 orderbook data, use `hz.backtest()` with `book_data` which switches to the BookSim exchange.
</Note>

## Run It

```bash
python examples/maker_taker_fees.py
```
