Skip to content

fills.fees

Per-half-turn commissions and exchange fees. Charged on entry AND exit, which is why so many statistics here are per half-turn.

fees

Topstep fee model: per-side commission + exchange + NFA costs.

Conforms to protocols.FeeModel. Costs are charged PER SIDE (entry AND exit), split per the SDK HalfTradeModel convention::

fees        = (exchange + nfa) * qty     (venue + regulatory)
commissions = commission * qty           (broker)

Rates follow docs/topstep-rules.md §7 (researched July 2026, medium confidence — reconcile against a real account blotter before trusting micro-scalp verdicts; for micros the round-turn cost can rival the edge).

FeeSchedule

Bases: Struct

Per-side cost components for one product (all USD Decimals).

commission_per_side instance-attribute

commission_per_side: Decimal

exchange_per_side instance-attribute

exchange_per_side: Decimal

nfa_per_side instance-attribute

nfa_per_side: Decimal

TopstepFees

TopstepFees(overrides: dict[str, FeeSchedule] | None = None)

Per-side Topstep fee model (protocols.FeeModel).

overrides (keyed by product symbol) replace the built-in schedule for those symbols — the calibration hook for reconciling against a real account blotter.

Source code in src/topstep_backtest/fills/fees.py
def __init__(self, overrides: dict[str, FeeSchedule] | None = None) -> None:
    self._overrides: dict[str, FeeSchedule] = dict(overrides) if overrides else {}

schedule_for

schedule_for(symbol: str) -> FeeSchedule

The effective schedule for symbol: override > built-in > conservative default.

Source code in src/topstep_backtest/fills/fees.py
def schedule_for(self, symbol: str) -> FeeSchedule:
    """The effective schedule for ``symbol``: override > built-in > conservative default."""
    override = self._overrides.get(symbol)
    if override is not None:
        return override
    return _DEFAULT_SCHEDULES.get(symbol, _UNKNOWN_SCHEDULE)

fee

fee(instrument: InstrumentSpec, side: OrderSide, qty: int, liquidity: Liquidity) -> tuple[Decimal, Decimal]

Cost of one side of qty contracts: (fees, commissions).

fees = (exchange + nfa) * qty; commissions = commission * qty (the SDK HalfTradeModel split). Topstep's schedule does not vary by side or maker/taker at this tier, so side/liquidity are accepted for protocol conformance but do not change the amounts.

Source code in src/topstep_backtest/fills/fees.py
def fee(
    self,
    instrument: InstrumentSpec,
    side: OrderSide,
    qty: int,
    liquidity: Liquidity,
) -> tuple[Decimal, Decimal]:
    """Cost of one side of ``qty`` contracts: ``(fees, commissions)``.

    ``fees = (exchange + nfa) * qty``; ``commissions = commission * qty``
    (the SDK ``HalfTradeModel`` split). Topstep's schedule does not vary
    by side or maker/taker at this tier, so ``side``/``liquidity`` are
    accepted for protocol conformance but do not change the amounts.
    """
    if qty <= 0:
        raise ValueError(f"qty must be positive, got {qty}")
    schedule = self.schedule_for(instrument.symbol)
    fees = (schedule.exchange_per_side + schedule.nfa_per_side) * qty
    commissions = schedule.commission_per_side * qty
    return fees, commissions