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"))
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: