Margin Interest Rate

Key Concepts

Introduction

Margin interest is a cost associated with trading on margin. Margin interest rate models model margin interest cash flows by directly adding or removing cash from your portfolio.

Set Models

The brokerage model of your algorithm automatically sets the margin interest rate model for each security, but you can override it. To manually set the margin interest rate model of a security, assign a model to the MarginInterestRateModel property of the Security object.

public override void Initialize()
{
    var security = AddEquity("SPY");
    // Null margin interest rate model is non-realistic, you should set it according to your broker information
    security.MarginInterestRateModel = MarginInterestRateModel.Null;
}
def initialize(self) -> None:
    security = self.add_equity("SPY")
    # Null margin interest rate model is non-realistic, you should set it according to your broker information
    security.set_margin_interest_rate_model(MarginInterestRateModel.NULL)

You can also set the margin interest rate 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.MarginInterestRateModel = MarginInterestRateModel.Null;    }
}
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_margin_interest_rate_model(MarginInterestRateModel.NULL)

To view all the pre-built margin interest rate models, see Supported Models.

Default Behavior

The brokerage model of your algorithm automatically sets the margin interest rate model of each security. The default brokerage model is the DefaultBrokerageModel, which sets the NullMarginInterestRateModel.

Model Structure

Margin interest rate models should implement the IMarginInterestRateModel interface. Extensions of the IMarginInterestRateModel interface must implement the ApplyMarginInterestRateapply_margin_interest_rate method, which applies margin interest payments to the portfolio.

public class CustomMarginInterestRateModelExampleAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        var security = AddEquity("SPY");
        // You should set it according to your broker information for the least actual-expected discrepancies
        security.SetMarginInterestRateModel(new MyMarginInterestRateModel());
    }
}

// Define the custom margin interest rate model
public class MyMarginInterestRateModel : IMarginInterestRateModel 
{
    public void ApplyMarginInterestRate(MarginInterestRateParameters marginInterestRateParameters) 
    {
        var holdings = marginInterestRateParameters.Security.Holdings;
        var positionValue = holdings.GetQuantityValue(holdings.Quantity);
        positionValue.Cash.AddAmount(-1);
    }
}
class CustomMarginInterestRateModelExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        security = self.add_equity("SPY")
        # You should set it according to your broker information for the least actual-expected discrepancies
        security.set_margin_interest_rate_model(MyMarginInterestRateModel())

# Define the custom margin interest rate model
class MyMarginInterestRateModel:

    def apply_margin_interest_rate(self, margin_interest_rate_parameters: MarginInterestRateParameters) -> None:
        holdings = margin_interest_rate_parameters.security.holdings
        position_value = holdings.get_quantity_value(holdings.quantity)
        position_value.cash.add_amount(-1)

For a full example algorithm, see this backtestthis backtest.

The ApplyMarginInterestRateapply_margin_interest_rate method is automatically called at the top of each hour.

Examples

The following examples demonstrate some common practices for implementing a custom margin interest rate model.

Example 1: Based On Interest Rate

The following algorithm buys and holds SPY, with 2x leverage and holds 1.5x of portfolio value. Hence, funds will be borrowed from the broker and are susceptible to margin interest. We implement a custom margin interest rate model to simulate the Interactive Brokers margin interest deduction. While following daily accrual and monthly accounting of the margin interest, we simplify the rate from tiered to fixed at 1% above the current interest rate.

public class MarginInterestRateModelAlgorithm : QCAlgorithm
{
    private Symbol _spy;

    public override void Initialize()
    {
        SetStartDate(2023, 6, 1);
        SetEndDate(2024, 6, 1);
        SetCash(1000000);

        // Request SPY data for trading.
        var equity = AddEquity("SPY");
        _spy = equity.Symbol;
        // Set custom margin interest rate model for the SPY security to mimic IB margin interest deduction.
        equity.SetMarginInterestRateModel(new MyMarginInterestRateModel(this, _spy));
    }

    public override void OnData(Slice slice)
    {
        if (!Portfolio.Invested)
        {
            // Place a position that exceeds available cash to buy with borrowed funds.
            SetHoldings(_spy, 1.5m);
        }
    }

    // Define the custom margin interest rate model
    public class MyMarginInterestRateModel : IMarginInterestRateModel 
    {
        private QCAlgorithm _algorithm;
        private Dictionary<Cash, decimal> _accruedMarginInterest = new();

        public MyMarginInterestRateModel(QCAlgorithm algorithm, Symbol symbol)
            : base()
        {
            // Save the algorithm instance since we need to access the at-the-time interest rate to calculate the margin rate.
            _algorithm = algorithm;
            // IBKR accrues interest daily and posts actual interest monthly on the third business day of the following month.
            algorithm.Schedule.On(
                algorithm.DateRules.MonthStart(symbol, 3),
                algorithm.TimeRules.At(0, 0),
                MarginDeduction
            );
        }

        private void MarginDeduction()
        {
            foreach (var (cash, accruedInterest) in _accruedMarginInterest)
            {
                // Monthly deduction of cash position with accrued interests.
                cash.AddAmount(accruedInterest);
                // Reset the accrued interest accumulator.
                _accruedMarginInterest[cash] = 0m;
            }
        }

        public void ApplyMarginInterestRate(MarginInterestRateParameters marginInterestRateParameters) 
        {
            // IBKR accrues interest daily for overnight positions only.
            if (_algorithm.Time.Hour == 16)
            {
                var holdings = marginInterestRateParameters.Security.Holdings;
                // Calculate the borrowed capital. The margin interest will only applied to the borrowed capital.
                var borrowedValue = holdings.UnleveredAbsoluteHoldingsCost - holdings.AbsoluteHoldingsCost;
                // Margin interest rate is 1% above the current risk-free interest rate.
                var marginRate = _algorithm.RiskFreeInterestRateModel.GetInterestRate(_algorithm.Time) + 0.01m;
                // Deduct the margin interest on the account cash position by the margin rate, multiplying the borrowed sum.
                var cash = holdings.GetQuantityValue(holdings.Quantity).Cash;
                _accruedMarginInterest.TryAdd(cash, 0m);
                // Daily rate will be divided by 365.
                _accruedMarginInterest[cash] += borrowedValue * marginRate / 365m;
                
            }
        }
    }
}
class MarginInterestRateModelAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2023, 6, 1)
        self.set_end_date(2024, 6, 1)
        self.set_cash(1000000)

        # Request SPY data for trading.
        equity = self.add_equity("SPY")
        self.spy = equity.symbol
        # Set a custom margin interest rate model for the SPY security to mimic IB margin interest deduction.
        equity.set_margin_interest_rate_model(MyMarginInterestRateModel(self, self.spy))
        
    def on_data(self, slice: Slice) -> None:
        if not self.portfolio.invested:
            # Place a position that exceeds available cash to buy with borrowed funds.
            self.set_holdings(self.spy, 1.5)

# Define the custom margin interest rate model
class MyMarginInterestRateModel:
    accrued_margin_interest = {}

    def __init__(self, algorithm: QCAlgorithm, symbol: Symbol) -> None:
        # Save the algorithm instance since we must access the at-the-time interest rate to calculate the margin rate.
        self.algorithm = algorithm
        # IBKR accrues interest daily and posts actual interest monthly on the third business day of the following month.
        algorithm.schedule.on(
            algorithm.date_rules.month_start(symbol, 3),
            algorithm.time_rules.at(0, 0),
            self.margin_deduction
        )

    def margin_deduction(self) -> None:
        for cash, accrued_interest in self.accrued_margin_interest.items():
            # Monthly deduction of cash position with accrued interests.
            cash.add_amount(accrued_interest)
            # Reset the accrued interest accumulator.
            self.accrued_margin_interest[cash] = 0

    def apply_margin_interest_rate(self, margin_interest_rate_parameters: MarginInterestRateParameters) -> None:
        # IBKR accrues interest daily for overnight positions only.
        if self.algorithm.time.hour == 16:
            holdings = margin_interest_rate_parameters.security.holdings
            # Calculate the borrowed capital. The margin interest will only applied to the borrowed capital.
            borrowed_value = holdings.unlevered_absolute_holdings_cost - holdings.absolute_holdings_cost
            # Margin interest rate is 1% above the current risk-free interest rate.
            margin_rate = self.algorithm.risk_free_interest_rate_model.get_interest_rate(self.algorithm.time) + 0.01
            # Deduct the margin interest on the account cash position by the margin rate, multiplying the borrowed sum.
            cash = holdings.get_quantity_value(holdings.quantity).cash
            if cash not in self.accrued_margin_interest:
                self.accrued_margin_interest[cash] = 0
            # Daily rate will be divided by 365.
            self.accrued_margin_interest[cash] += borrowed_value * margin_rate / 365

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: