Transaction Fees

Key Concepts

Introduction

Your orders incur a transaction fee when a brokerage fills them in the market. LEAN uses transaction fee models in backtesting to model the live trading fees you would incur with the strategy. Transaction fee models make backtest results more realistic. To give your backtests the most accurate fees, LEAN contains transaction fee models that model the fee structure of many popular brokerages.

Set Models

The brokerage model of your algorithm automatically sets the fee model for each security, but you can override it. To manually set the fee model of a security, call the SetFeeModelset_fee_model method on the Security object.

public override Initialize()
{
    var security = AddEquity("SPY");
    // Set the fee model for the requested security to backtest with the most realistic scenario
    // Constant fee model of 0 dollar is accurate for the commission-free brokerage
    security.SetFeeModel(new ConstantFeeModel(0));
}
def initialize(self):
    security = self.add_equity("SPY")
    # Set the fee model for the requested security to backtest with the most realistic scenario
    # Constant fee model of 0 dollar is accurate for the commission-free brokerage
    security.set_fee_model(ConstantFeeModel(0))

You can also set the fee model in a security initializer. If your algorithm has a dynamic universe, use the security initializer technique. In order to initialize single security subscriptions with the security initializer, call AddSecurityInitializeradd_security_initializer before you create the subscriptions.

public class AddSecurityInitializerExampleAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        // In the Initialize method, set the security initializer to set models of assets.
        AddSecurityInitializer(CustomSecurityInitializer);
    }

    private void CustomSecurityInitializer(Security security)
    {
        // Overwrite some of the reality models
        security.SetFeeModel(new ConstantFeeModel(0));
    }
}
class AddSecurityInitializerExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        # In the Initialize method, set the security initializer to set models of assets.
        self.add_security_initializer(self._custom_security_initializer)

    def _custom_security_initializer(self, security: Security) -> None:
        # Overwrite some of the reality models        
        security.set_fee_model(ConstantFeeModel(0))

In live trading, the SetFeeModelset_fee_model method isn't ignored. If we use order helper methods like SetHoldingsset_holdings, the fee model helps to calculate the order quantity. However, the algorithm doesn't update the cash book with the fee from the fee model. The algorithm uses the actual fee from the brokerage to update the cash book.

To view all the pre-built fee models, see Supported Models.

Default Behavior

The brokerage model of your algorithm automatically sets the fill model for each security. The default brokerage model is the DefaultBrokerageModel, which set the ConstantFeeModel with no fees for Forex, CFD, and Crypto assets and sets the InteractiveBrokersFeeModel for the remaining asset classes.

Model Structure

Fee models should extend the FeeModel class. Extensions of the FeeModel class must implement the GetOrderFeeget_order_fee method, which receives OrderFeeParameters and returns an OrderFee that represents a cash amount in a currency.

public class CustomFeeModelExampleAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        // In the Initialize method, set the custom fee model for an added security to use the custom model
        var security = AddEquity("SPY");
        security.SetFeeModel(new MyFeeModel());
    }
}

// Define the custom fee model
public class MyFeeModel : FeeModel 
{
    public override OrderFee GetOrderFee(OrderFeeParameters parameters) 
    {
        return new OrderFee(new CashAmount(0.5m, "USD"));
    }
}
class CustomFeeModelExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        # In the Initialize method, set the custom fee model for an added security to use the custom model
        security = self.add_equity("SPY")
        security.set_fee_model(MyFeeModel())

# Define the custom fee model
class MyFeeModel(FeeModel):

    def get_order_fee(self, parameters: OrderFeeParameters) -> OrderFee:
        return OrderFee(CashAmount(0.5, 'USD'))

The OrderFeeParameters object has the following members:

Estimate Fees

To estimate the modeled fee of an order before you place it, create an order request with the order factory and convert it into an Order object with the QuantConnect.Orders.Order.CreateOrderOrder.create_order method. Then pass the order and its security to the fee model of the security. The conversion doesn't submit the order.

var security = Securities[_symbol];
var request = OrderFactory.LimitOrder(_symbol, 100, limitPrice);
var order = QuantConnect.Orders.Order.CreateOrder(request);
var fee = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, order)).Value;
Debug($"Estimated fee: {fee.Amount} {fee.Currency}");
security = self.securities[self._symbol]
request = self.order_factory.limit_order(self._symbol, 100, limit_price)
order = Order.create_order(request)
fee = security.fee_model.get_order_fee(OrderFeeParameters(security, order)).value
self.debug(f"Estimated fee: {fee.amount} {fee.currency}")

The estimate is a modeled fee. The fee model calculates it inside your algorithm with the current order and security values. In live trading, LEAN doesn't request the estimate from the brokerage. The fee that the brokerage charges can differ from the modeled fee, especially if the fee depends on the fill price or the fill quantity. To place the order after the estimate, pass the same request to the Orderorder method.

Zero Transaction Fees

To model zero transaction fees, use the ConstantFeeModel with zero as fee and specify the currency.

public override Initialize()
{
    var security = AddEquity("SPY");
    security.SetFeeModel(new ConstantFeeModel(fee: 0m, currency: "USD"));
}
def initialize(self):
    security = self.add_equity("SPY")
    security.set_fee_model(ConstantFeeModel(fee=0, currency="USD"))

Negative Transaction Fees

If you short a security and receive interest payments, they are negative transaction fees.

Examples

The following examples demonstrate common practices for implementing custom fee models and estimating fees.

Example 1: Fixed Fees

The following algorithm incurs a $0.50 fee for each order.

public class CustomFeeModelAlgorithm : QCAlgorithm
{

    public override void Initialize()
    {
        SetStartDate(2024, 9, 1);
        SetEndDate(2024, 12, 31);
        SetCash(100000);
        var security = AddEquity("SPY", Resolution.Daily);
        security.SetFeeModel(new MyFeeModel());
    }

    public override void OnData(Slice data)
    {
        if (!Portfolio.Invested)
        {
            SetHoldings("SPY", 1);
        }
    }
}

public class MyFeeModel : FeeModel 
{
    public override OrderFee GetOrderFee(OrderFeeParameters parameters) 
    {
        return new OrderFee(new CashAmount(0.5m, "USD"));
    }
}
class CustomFeeModelAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(100000)
        security = self.add_equity('SPY', Resolution.DAILY)
        security.set_fee_model(MyFeeModel())

    def on_data(self, data: Slice) -> None:
        if not self.portfolio.invested:
            self.set_holdings('SPY', 1)


class MyFeeModel(FeeModel):

    def get_order_fee(self, parameters: OrderFeeParameters) -> OrderFee:
        return OrderFee(CashAmount(0.5, 'USD'))

Example 2: Buy When Fees Are Low

The following algorithm saves $100 each week to buy SPY. Interactive Brokers charges at least $1 for each order, so small orders are expensive. Each week, the algorithm estimates the fee of an order with its savings. It places the order only when the fee is 0.1% of the order value or less.

public class EstimateFeesAlgorithm : QCAlgorithm
{
    private Symbol _spy;
    private int _weeks;

    public override void Initialize()
    {
        SetStartDate(2024, 1, 1);
        SetEndDate(2024, 12, 31);
        SetCash(5000);
        SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
        _spy = AddEquity("SPY", Resolution.Daily).Symbol;
        Schedule.On(DateRules.WeekStart(_spy), TimeRules.AfterMarketOpen(_spy, 30), Buy);
    }

    private void Buy()
    {
        _weeks++;
        var security = Securities[_spy];
        if (security.Price == 0)
        {
            return;
        }
        var quantity = Math.Floor(_weeks * 100m / security.Price);
        if (quantity == 0)
        {
            return;
        }
        var request = OrderFactory.MarketOrder(_spy, quantity);
        // Estimate the fee without submitting the order.
        var fee = security.FeeModel.GetOrderFee(new OrderFeeParameters(security, QuantConnect.Orders.Order.CreateOrder(request))).Value.Amount;
        if (fee <= 0.001m * quantity * security.Price)
        {
            Order(request);
            _weeks = 0;
        }
    }
}
class EstimateFeesAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 1, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(5000)
        self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)
        self._spy = self.add_equity("SPY", Resolution.DAILY).symbol
        self._weeks = 0
        self.schedule.on(self.date_rules.week_start(self._spy), self.time_rules.after_market_open(self._spy, 30), self._buy)

    def _buy(self) -> None:
        self._weeks += 1
        security = self.securities[self._spy]
        if not security.price:
            return
        quantity = int(self._weeks * 100 / security.price)
        if not quantity:
            return
        request = self.order_factory.market_order(self._spy, quantity)
        # Estimate the fee without submitting the order.
        fee = security.fee_model.get_order_fee(OrderFeeParameters(security, Order.create_order(request))).value.amount
        if fee <= 0.001 * quantity * security.price:
            self.order(request)
            self._weeks = 0

Other Examples

For more examples, see the following algorithms:

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: