Implementing Custom Fee Models in Backtesting Frameworks: A QuantConnect Guide

Implement custom fee models in QuantConnect by inheriting from the FeeModel base class and overriding the GetOrderFee method to return calculated transaction costs wrapped in a CashAmount object.

Implementing custom fee models in backtesting frameworks is essential for realistic performance attribution, as transaction costs can significantly erode strategy returns in live trading. The paperswithbacktest/awesome-systematic-trading repository demonstrates production-tested patterns for modeling commissions within QuantConnect's algorithm framework. This guide extracts the implementation details from the repository's systematic strategies to show you how to build parameterizable, currency-aware fee models that integrate seamlessly with your backtesting workflow.

Understanding the FeeModel Abstraction

QuantConnect exposes transaction cost calculation through the abstract FeeModel base class, which requires a single method implementation to customize commission logic. The GetOrderFee method receives an OrderFeeParameters object containing the security instance and order details, and must return an OrderFee object wrapping a CashAmount. This architecture isolates fee calculation from strategy logic, allowing you to swap commission structures without modifying algorithm code. According to the repository source code, all custom implementations should inherit from FeeModel and override this specific method signature to ensure compatibility with the backtesting engine.

Building a Reusable Custom Fee Model

Create the Fee Model Class

Encapsulate your commission logic in a dedicated class that derives from FeeModel, implementing the required GetOrderFee method to calculate costs based on price, quantity, and your specific rate structure.

from AlgorithmImports import *

class FixedCommissionFeeModel(FeeModel):
    """Applies a fixed commission rate (in basis points) to every order."""
    def __init__(self, rate_bps: float = 5.0):
        # Convert basis points to a decimal proportion (e.g., 5 bps → 0.00005)

        self._rate = rate_bps / 1_00_000

    def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee:
        # fee = price × quantity × rate

        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * self._rate
        return OrderFee(CashAmount(fee, "USD"))

Parameterize Commission Rates

Store fee rates as class attributes or constructor parameters rather than hard-coding values, enabling you to test different commission structures without touching algorithm logic. The example above accepts rate_bps as a configurable parameter, converting basis points to decimal form for calculation. This pattern appears consistently throughout the repository, allowing strategies to specify different rates for various security types or broker assumptions.

Handle Currency Correctly

Always return fees wrapped in a CashAmount object specifying the correct currency—typically "USD" for US equities or the security's quote currency for international assets. This prevents accounting errors where fees might be calculated in one currency but applied to an account denominated in another. The repository implementations consistently use CashAmount(fee, "USD") to ensure currency alignment with the security's pricing context.

Attaching Fee Models to Securities

Attach your custom model to each security during the Initialize method using SetFeeModel(), ensuring every order execution uses your custom logic. This attachment must occur after adding the security to the algorithm but before any orders are placed.

class MyStrategy(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2020, 1, 1)
        self.SetCash(1_000_000)
        
        # Add an equity and attach the custom fee model

        equity = self.AddEquity("SPY", Resolution.Daily)
        equity.SetFeeModel(FixedCommissionFeeModel(rate_bps=4.0))   # 4 bps

        
        # Add another security with the default 5 bps fee model

        another = self.AddEquity("EWJ", Resolution.Daily)
        another.SetFeeModel(FixedCommissionFeeModel())             # uses default 5 bps

Real-World Examples from the Repository

The awesome-systematic-trading repository demonstrates these patterns across multiple strategy implementations. In static/strategies/value-factor-effect-within-countries.py (lines 42-46), a minimal custom fee model multiplies security price by order quantity and a fixed rate of 0.00005 (5 basis points), illustrating the essential pattern for simple commission structures.

Similarly, static/strategies/momentum-factor-effect-in-stocks.py (lines 17-21) reuses the same CustomFeeModel pattern, reinforcing the repository's consistent approach to fee modeling. The implementation in static/strategies/short-term-reversal-in-stocks.py shows how fee models integrate with different factor-based strategies, while static/strategies/fx-carry-trade.py demonstrates currency-agnostic usage for foreign exchange equities.

These examples collectively illustrate a modular approach where fee logic remains stateless aside from rate parameters, making models safe for parallel backtests and easy to tune for different broker fee schedules.

Summary

  • Encapsulate fee logic in a dedicated class inheriting from FeeModel with a deterministic GetOrderFee implementation to isolate transaction costs from strategy code.
  • Parameterize rates via constructor arguments rather than hard-coding values, allowing flexible commission testing across different broker scenarios.
  • Use CashAmount with the correct currency code to prevent accounting mismatches and ensure proper fee deduction from portfolio value.
  • Attach models early by calling SetFeeModel() immediately after adding securities in Initialize() to ensure all orders use custom fee calculations.
  • Maintain consistency across strategies by following the patterns demonstrated in the repository's Value Factor and Momentum Factor implementations.

Frequently Asked Questions

How do I implement a custom fee model in QuantConnect?

Create a class that inherits from FeeModel and override the GetOrderFee method to calculate your commission based on the OrderFeeParameters provided, then return an OrderFee object containing a CashAmount. Attach this model to individual securities using security.SetFeeModel(CustomFeeModel()) within your algorithm's Initialize method.

What parameters does the GetOrderFee method receive?

The GetOrderFee method receives an OrderFeeParameters object that includes the Security instance (providing access to current price) and the Order object (providing AbsoluteQuantity and other order details). You use these to calculate the notional value and apply your commission rate.

Can I use different fee models for different securities in the same algorithm?

Yes, you can instantiate different fee model classes or the same class with different parameters for each security. Call SetFeeModel() on each security individually after adding it to the algorithm, as demonstrated in the repository's examples where SPY might use a 4 bps model while EWJ uses 5 bps.

Why should I avoid random components in fee calculations?

Deterministic fees ensure your backtest results remain reproducible across multiple runs, which is critical for debugging strategy logic and comparing performance across different parameter sets. Only introduce stochastic cost elements if you are explicitly modeling variable costs like slippage or market impact, and even then, use seeded random generators for reproducibility.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →