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 SetSecurityInitializerset_security_initializer before you create the subscriptions.

public class BrokerageModelExampleAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        // In the Initialize method, set the security initializer to seed initial the prices and models of assets.
        SetSecurityInitializer(new MySecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices)));
    }
}

public class MySecurityInitializer : BrokerageModelSecurityInitializer
{
    public MySecurityInitializer(IBrokerageModel brokerageModel, ISecuritySeeder securitySeeder)
        : base(brokerageModel, securitySeeder) {}    
    public override void Initialize(Security security)
    {
        // First, call the superclass definition.
        // This method sets the reality models of each security using the default reality models of the brokerage model.
        base.Initialize(security);

        // Next, overwrite some of the reality models
        security.SetFeeModel(new ConstantFeeModel(0));    }
}
class BrokerageModelExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        # In the Initialize method, set the security initializer to seed initial the prices and models of assets.
        self.set_security_initializer(MySecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))

# Outside of the algorithm class
class MySecurityInitializer(BrokerageModelSecurityInitializer):

    def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder) -> None:
        super().__init__(brokerage_model, security_seeder)    
    def initialize(self, security: Security) -> None:
        # First, call the superclass definition.
        # This method sets the reality models of each security using the default reality models of the brokerage model.
        super().initialize(security)

        # Next, 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'))

For a full example algorithm, see this backtestthis backtest.

The OrderFeeParameters object has the following members:

Zero Transaction Fees

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

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

Negative Transaction Fees

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

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: