Reality Modeling

Buying Power

Introduction

Buying power models (also known as margin models) control how much buying power or leverage your portfolio has to make trades. When you place an order, LEAN uses the buying power model to determine whether the order should be submitted so you avoid placing orders that would be rejected by the brokerage. Buying power calculations can be very complex and depend on many factors, including the brokerage or even the time of day. For example, the PatternDayTradingMarginModel lets you have 4x leverage during regular trading hours and 2x leverage during pre-market and post-market trading hours.

What is Buying Power?

Buying power is the total amount of money you can spend in your brokerage account. It depends on the security type and account type. On one hand, Option and Future contracts are leveraged securities with specific buying power rules. On the other hand, the buying power of cash accounts is just the cash in the account while the buying power of margin accounts is leveraged by the brokerage credit.

What is Margin?

Margin is a credit loan from your brokerage you receive after you deposit collateral into your margin account. You need margin to place short-biased trades and you can use margin to increase your buying power, but you incur interest fees. Margin is the dollar value of the loan that the brokerage gives you. A margin requirement of 25% means that to purchase $10,000 worth of securities, you need at least $2,500 worth of collateral in your brokerage account to open the trade and you can borrow the rest on margin. Maintenance margin is the minimum equity (equity = total portfolio value - borrowed funds) you must have in your brokerage account to stay in your positions. If the value of your portfolio falls below the maintenance margin, you receive a margin call. If you receive a margin call, you either need to add more capital to your brokerage account or the brokerage will liquidate some of your holdings to reduce your exposure and their risk.

Some securities have special margin rules. Derivatives are leveraged assets with a floating margin requirement. In particular, long Options have zero maintenance margin requirement and their initial margin requirement is only the premium that you pay upfront.

What is Leverage?

Leverage is using borrowed money to increase your buying power. Leverage has an inverse relationship with your margin requirement and maintenance margin. If you have a margin requirement of 50%, you can use up to 1 / 50% = 2 leverage. Trading with leverage can be risky. It can boost your returns on profitable trades but can make your losing trades more expensive. If you have $10,000 in your brokerage margin account and purchase $20,000 worth of securities, you are trading with 2x leverage. If the value of the securities in your portfolio drops by 50% when you have a 2x leverage position, you lose all of your equity.

What Are Position Groups?

A position group is the combination of holdings. It has lower margin requirement and maintenance margin than the sum of each position of the group. If you have a margin requirement of $29,150 to purchase an in-the-money call Option contract, and a margin requirement of $101,499 to sell an out-of-the money call Option contract, the total margin requirement is $130,649. However, these positions compose a bull call spread with a margin requirement of $0.

Set Models

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

public override void Initialize()
{
    var security = AddEquity("SPY");
    // Use 3x leverage on the selected security, which mimic the actual buying power of your broker
    security.SetBuyingPowerModel(new SecurityMarginModel(3m));
}
def initialize(self) -> None:
    security = self.add_equity("SPY")
    # Use 3x leverage on the selected security, which mimic the actual buying power of your broker
    security.set_buying_power_model(SecurityMarginModel(3))

You can also set the buying power model in a security initializer. If your algorithm has a 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.SetBuyingPowerModel(new SecurityMarginModel(3m));    }
}
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_buying_power_model(SecurityMarginModel(3))

You cannot change the position group buying power models.

Default Behavior

The brokerage model of your algorithm automatically sets the buying power model of each security. The default brokerage model is the DefaultBrokerageModel, which sets the buying power model based on the asset class of the security. The following table shows the default buying power model of each asset class:

Asset ClassModel
Equity OptionsOptionMarginModel
FuturesFutureMarginModel
Future OptionsFuturesOptionsMarginModel
Index OptionsOptionMarginModel
CryptoCashBuyingPowerModel for cash accounts or SecurityMarginModel for margin accounts
CryptoFutureCryptoFutureMarginModel
ForexCashBuyingPowerModel for cash accounts or SecurityMarginModel for margin accounts
OtherSecurityMarginModel

Model Structure

Buying power models should extend the BuyingPowerModel class. Extensions of the BuyingPowerModel class should implement the following methods:

public class CustomBuyingPowerModelExampleAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        var security = AddEquity("SPY");
        // Set the custom buying power model of the selected security to mimic its actual buying power in your used broker
        security.SetBuyingPowerModel(new MyBuyingPowerModel());
    }
}

// Define the custom buying power model
class MyBuyingPowerModel : BuyingPowerModel
{
    public MyBuyingPowerModel(
        decimal leverage = 2m,
        decimal requiredFreeBuyingPowerPercent = 0m)
        : base(leverage, requiredFreeBuyingPowerPercent)
    {
    }

    public override decimal GetLeverage(Security security)
    {
       return base.GetLeverage(security);
    }

    public override void SetLeverage(Security security, decimal leverage)
    {
        base.SetLeverage(security, leverage);
    }

    public override InitialMargin GetInitialMarginRequiredForOrder(
        InitialMarginRequiredForOrderParameters parameters)
    {
        return base.GetInitialMarginRequiredForOrder(parameters);
    }

    public override MaintenanceMargin GetMaintenanceMargin(
        MaintenanceMarginParameters parameters)
    {
        return base.GetMaintenanceMargin(parameters);
    }

    protected override decimal GetMarginRemaining(
        SecurityPortfolioManager portfolio,
        Security security,
        OrderDirection direction)
    {
        return base.GetMarginRemaining(portfolio, security, direction);
    }

    public override InitialMargin GetInitialMarginRequirement(
        InitialMarginParameters parameters)
    {
        return base.GetInitialMarginRequirement(parameters);
    }

    public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(HasSufficientBuyingPowerForOrderParameters parameters)
    {
        return base.HasSufficientBuyingPowerForOrder(parameters);
    }

    public override GetMaximumOrderQuantityResult GetMaximumOrderQuantityForDeltaBuyingPower(GetMaximumOrderQuantityForDeltaBuyingPowerParameters parameters)
    {
        return base.GetMaximumOrderQuantityForDeltaBuyingPower(parameters);
    }

    public override GetMaximumOrderQuantityResult GetMaximumOrderQuantityForTargetBuyingPower(GetMaximumOrderQuantityForTargetBuyingPowerParameters parameters)
    {
        return base.GetMaximumOrderQuantityForTargetBuyingPower(parameters);
    }

    public override ReservedBuyingPowerForPosition GetReservedBuyingPowerForPosition(
        ReservedBuyingPowerForPositionParameters parameters)
    {
        return base.GetReservedBuyingPowerForPosition(parameters);
    }

    public override BuyingPower GetBuyingPower(BuyingPowerParameters parameters)
    {
        return base.GetBuyingPower(parameters);
    }
}
class CustomBuyingPowerModelExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        security = self.add_equity("SPY")
        # Set the custom buying power model of the selected security to mimic its actual buying power in your used broker
        security.set_buying_power_model(MyBuyingPowerModel())

# Define the custom buying power model
class MyBuyingPowerModel(BuyingPowerModel):
    def __init__(self, 
         leverage: float = 2, 
         requiredFreeBuyingPowerPercent: float = 0):
        super().__init__(leverage, requiredFreeBuyingPowerPercent)

    def get_leverage(self, security: Security) -> float: 
        return super().get_leverage(security)

    def set_leverage(self, security: Security, leverage: float) -> None: 
        super().set_leverage(security, leverage)

    def get_initial_margin_required_for_order(self,
         parameters: InitialMarginRequiredForOrderParameters) -> InitialMargin:
        return super().get_initial_margin_required_for_order(parameters)

    def get_maintenance_margin(self,
         parameters: MaintenanceMarginParameters) -> MaintenanceMargin: 
        return super().get_maintenance_margin(parameters)

    def get_margin_remaining(self,
         portfolio: SecurityPortfolioManager,
         security: Security,
         direction: OrderDirection) -> float: 
        return super().get_margin_remaining(portfolio, security, direction)

    def get_initial_margin_requirement(self,
         parameters: InitialMarginParameters) -> InitialMargin:
        return super().get_initial_margin_requirement(parameters)

    def has_sufficient_buying_power_for_order(self, 
         parameters: HasSufficientBuyingPowerForOrderParameters
        ) -> HasSufficientBuyingPowerForOrderResult: 
        return super().has_sufficient_buying_power_for_order(parameters)

    def get_maximum_order_quantity_for_delta_buying_power(self, 
         parameters: GetMaximumOrderQuantityForDeltaBuyingPowerParameters
        ) -> GetMaximumOrderQuantityResult:
        return super().get_maximum_order_quantity_for_delta_buying_power(parameters)

    def get_maximum_order_quantity_for_target_buying_power(self, 
         parameters: GetMaximumOrderQuantityForTargetBuyingPowerParameters
        ) -> GetMaximumOrderQuantityResult:
        return super().get_maximum_order_quantity_for_target_buying_power(parameters)

    def get_reserved_buying_power_for_position(self, 
         parameters: ReservedBuyingPowerForPositionParameters
        ) -> ReservedBuyingPowerForPosition:
        return super().get_reserved_buying_power_for_position(parameters)

    def get_buying_power(self,
         parameters: BuyingPowerParameters) -> BuyingPower:
        return super().get_buying_power(parameters)

For a full example algorithm, see this backtestthis backtest.

Disable Buying Power Models

You can disable order margin checks and opt to let your brokerage decide to accept or reject the trades. This is helpful in live trading if you have a more permissive brokerage margin allowance that what LEAN models. The default position group buying power models are helpful for Option trading strategies. However, it can be counterproductive if it's not a supported Option strategy. To disable the validations of the default position group buying power model, use the NullSecurityPositionGroupModel. To set the NullSecurityPositionGroupModel for the portfolio, during initialization, call the SetPositionsset_positions method with the SecurityPositionGroupModel.NullNULL argument.

Portfolio.SetPositions(SecurityPositionGroupModel.Null);
self.portfolio.set_positions(SecurityPositionGroupModel.NULL)

To disable the validations of the default buying power model, use the NullBuyingPowerModel. To set the NullBuyingPowerModel for a security subscription, call the SetBuyingPowerModelset_buying_power_model method with the BuyingPowerModel.NullNULL argument.

public override void Initialize()
{
    var equity = AddEquity("SPY");
    // To disable any buying power of the selected security
    equity.SetBuyingPowerModel(BuyingPowerModel.Null);
    // Alias: 
    // equity.SetMarginModel(SecurityMarginModel.Null);
}
def initialize(self) -> None:
    equity = self.add_equity("SPY")
    # To disable any buying power of the selected security
    equity.set_buying_power_model(BuyingPowerModel.NULL)
    # Alias:
    # equity.set_margin_model(SecurityMarginModel.NULL)

You can also set the NullBuyingPowerModel in a security initializer. If your algorithm has a universe, use the security initializer technique. To set the buying power of securities in the security initializer, set the brokerage model, set the security initializer, and then create security 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 the security buying power
        security.SetBuyingPowerModel(BuyingPowerModel.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 the security buying power
        security.set_buying_power_model(BuyingPowerModel.NULL)

Set Asset Leverage

The buying power model sets the leverage for each security in your algorithm, but you can override its leverage settings after the buying power model is set.

To set the leverage when you create a security subscription, pass in a leverage argument.

public override void Initialize()
{
	// Set the leverage to 3x manually subjected to specific need
	AddEquity("SPY", leverage: 3);
}
def initialize(self) -> None:
	# Set the leverage to 3x manually subjected to specific need
	self.add_equity("SPY", leverage=3)

You can also set the asset leverage in a security initializer. In order to set the leverage of securities in the security initializer, call SetSecurityInitializerset_security_initializer before you create security subscriptions and before you call SetBrokerageModelset_brokerage_model. If you pass in a leverage argument when you create the security subscription, the leverage argument takes precedence over the SetLeverageset_leverage call in the security initializer.

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 the security leverage
        security.SetLeverage(3);    }
}
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 the security leverage
        security.set_leverage(3)

To set the leverage for all securities in a universe, set the UniverseSettings.Leverageuniverse_settings.leverage property.

public override void Initialize()
{
	// Set the leverage to 3x to all securities, it should only be used for a very narrow spectrum of security universe with the same leverage
	// E.g. US Equities universe composed of only primary stocks in NYSE exchange
	UniverseSettings.Leverage = 3;
}
def initialize(self) ->None:
	# Set the leverage to 3x to all securities, it should only be used for a very narrow spectrum of security universe with the same leverage
	# E.g. US Equities universe composed of only primary stocks in NYSE exchange
	self.universe_settings.leverage = 3

In live trading, LEAN doesn't ignore the leverage you set. However, if you set a different leverage from what your brokerage provides, it creates a mismatch between the buying power in your algorithm and the buying power the brokerage gives you. In this case, orders can pass the validations in LEAN but your brokerage may reject them.

PDT Rule

If all of the following statements are true, you are classified as a pattern day trader:

  • You reside in the United States.
  • You trade in a margin account.
  • You execute 4+ intraday US Equity trades within 5 business days.
  • Your intraday US Equity trades represent more than 6% of your total trades.

Pattern day traders must maintain a minimum equity of $25,000 in their margin account to continue trading. For more information about pattern day trading, see Am I a Pattern Day Trader? on the FINRA website.

The PatternDayTradingMarginModel doesn't enforce minimum equity rules and doesn't limit your trades, but it adjusts your available leverage based on the market state. During regular market hours, you can use up to 4x leverage. During extended market hours, you can use up to 2x leverage.

security.MarginModel = new PatternDayTradingMarginModel();
security.margin_model = PatternDayTradingMarginModel()

Get Initial Margin Requirements

The following sections explain how to check if you have enough margin remaining to cover the initial margin requirements of various order types.

Check Requirements of Regular Orders

Follow these steps to check if you have enough margin remaining to cover the initial margin requirements of an order:

  1. Create an InitialMarginParameters object with the security and quantity you want to trade.
  2. public override void OnData(Slice slice)
    {
        var security = Securities["SPY"];
        var quantity = 100m;
        var parameter = new InitialMarginParameters(security, quantity);
    def on_data(self, slice: Slice) -> None:
        security = self.securities["SPY"]
        quantity = 100
        parameter = InitialMarginParameters(security, quantity)
  3. Call the GetInitialMarginRequirementget_initial_margin_requirement method of the security's buying power model with the InitialMarginParameters.
  4.     var initialMargin = security.BuyingPowerModel.GetInitialMarginRequirement(parameter);
        initial_margin = security.buying_power_model.get_initial_margin_requirement(parameter)

    The GetInitialMarginRequirementget_initial_margin_requirement method returns an InitialMargin object, which have the following properties:

  5. Compare the margin you have remaining against the initial margin requirement of the order.
  6.     if (Portfolio.MarginRemaining >= initialMargin.Value)
        {
            MarketOrder(security.Symbol, quantity);
        }
        else
        {
            Debug("You don't have sufficient margin for this order.");
        }
    }
        if self.portfolio.margin_remaining >= initial_margin.value:
            self.market_order(security.symbol, quantity)
        else:
            self.debug("You don't have sufficient margin for this order.")

Check Requirements of Option Strategy Orders

Follow these steps to check if you have enough margin remaining to cover the initial margin requirements of an Option strategy:

  1. Create an OptionStrategy object with the strategy you want to trade and its buying power model.
  2. For example, create a Bull Put Spread strategy.

    private Symbol _symbol;
                
    public override void Initialize()
    {
        // Subscribe to option data and cache the canonical symbol to obtain the option data
        _symbol = AddOption("SPY").Symbol;
    }
    
    public voerride void OnData(Slice slice)
    {
        // Trade on updated option chain data
        if (!slice.OptionChains.TryGetValue(_symbol, out var chain))
        {
            return;
        }
    
        var itmStrike = chain.Max(x => x.Strike);
        var otmStrike = chain.Min(x => x.Strike);
        var expiry = chain.Min(x => x.Expiry);
    
        var optionStrategy = OptionStrategies.BullPutSpread(_symbol, itmStrike, otmStrike, expiry);
    def initialize(self) -> None:
        # Subscribe to option data and cache the canonical symbol to obtain the option data
        self._symbol = self.add_option("SPY").symbol
        
    def on_data(self, slice: Slice) > None:
        # Trade on updated option chain data
        chain = slice.option_chains.get(self._symbol)
        if not chain:
            return
        
        itm_strike = max(x.strike for x in chain)
        otm_strike = min(x.strike for x in chain)
        expiry = min(x.expiry for x in chain)
    
        option_strategy = OptionStrategies.bull_put_spread(self._symbol, itm_strike, otm_strike, expiry)
  3. Create an OptionStrategyPositionGroupBuyingPowerModel object of the strategy.
  4.     var buyingPowerModel = new OptionStrategyPositionGroupBuyingPowerModel(optionStrategy);
        buying_power_model = OptionStrategyPositionGroupBuyingPowerModel(option_strategy)
  5. Create a list of Position objects from the strategy legs.
  6.     var positions = optionStrategy.OptionLegs.Select(leg =>
        {
            var symbol = QuantConnect.Symbol.CreateOption(_symbol.Underlying, _symbol.ID.Market, _symbol.ID.OptionStyle, leg.Right, leg.Strike, leg.Expiration);
            return new Position(symbol, leg.Quantity, 1);
        });
        def get_symbol(leg):
            return Symbol.CreateOption(
                self._symbol.underlying, self._symbol.id.market, self._symbol.id.option_style, 
                leg.right, leg.strike, leg.expiration
            )
    
        positions = [Position(get_symbol(leg), leg.quantity, 1) for leg in option_strategy.option_legs]
  7. Create a PositionGroupInitialMarginParameters objects with the portfolio and the position group.
  8.     var positionGroup = new PositionGroup(buyingPowerModel, 1, positions.ToArray());
        var parameters = new PositionGroupInitialMarginParameters(Portfolio, positionGroup);
        position_group = PositionGroup(buying_power_model, 1, positions)
        parameters = PositionGroupInitialMarginParameters(self.portfolio, position_group)
  9. Call the GetInitialMarginRequirementget_initial_margin_requirement method of the strategy's buying power model with the PositionGroupInitialMarginParameters.
  10.     var initialMargin = buyingPowerModel.GetInitialMarginRequirement(parameters);
        initial_margin = buying_power_model.get_initial_margin_requirement(parameters)

    The GetInitialMarginRequirementget_initial_margin_requirement method returns an InitialMargin object, which have the following properties:

  11. Compare the margin you have remaining against the initial margin requirement of the order.
  12. In this case, the initialMargin.Valueinitial_margin.value is the initial margin requirement for a trade size of 1. For different trade sizes, multiply the initialMargin.Valueinitial_margin.value by the quantity.

        var strategyQuantity = 2; 
        if (Portfolio.MarginRemaining >= strategyQuantity * initialMargin.Value)
        {
            Buy(optionStrategy, quantity);
        }
        else
        {
            Debug("You don't have sufficient margin for this order.");
        }
    }
        strategy_quantity = 2
        if self.portfolio.margin_remaining >= strategy_quantity * initial_margin.value:
            self.buy(option_strategy, strategy_quantity)
        else:
            self.debug("You don't have sufficient margin for this order.")

Examples

The following examples demonstrate some common practices for implementing a custom buying power model.

Example 1: Always Sufficient Buying Power

The following algorithm trades protective call strategy. Sometimes, the brokerage's margin requirement differs from the algorithm's default. To avoid errors on order submission at the algorithm level, we can use the SecurityPositionGroupModel.NullSecurityPositionGroupModel.NULL and NullBuyingPowerModel to disable buying power limitation so the order is sent directly to the broker.

using QuantConnect.Securities.Positions;

public class BuyingPowerModelAlgorithm : QCAlgorithm
{
    private Symbol _spy;

    public override void Initialize()
    {
        SetStartDate(2023, 10, 1);
        SetEndDate(2024, 10, 1);
        SetSecurityInitializer(new CustomSecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices)));

        // Disable the validations of the default position group buying power model
        // that evaluates the buying power of multi-leg strategies 
        Portfolio.SetPositions(SecurityPositionGroupModel.Null);

        // Request SPY data for trading. Set the data normalization mode to raw to allow for a fair comparison in strike price.
        _spy = AddEquity("SPY", dataNormalizationMode: DataNormalizationMode.Raw).Symbol;

        // Set a scheduled event to rebalance the protective put strategy.
        Schedule.On(
            DateRules.WeekStart(_spy),
            TimeRules.AfterMarketOpen(_spy, 0),
            Rebalance
        );
    }

    private void Rebalance()
    {
        // Select the weekend expiring ATM put for the protective put strategy.
        var atmPutContract = OptionChain(_spy)
            .Where(x => x.Right == OptionRight.Put && x.Expiry < Time.AddDays(6))
            .OrderByDescending(x => x.Expiry)
            .ThenBy(x => Math.Abs(x.Strike - x.UnderlyingLastPrice))
            .First();
        // Request the ATM put data for trading.
        var atmPut = AddOptionContract(atmPutContract).Symbol;

        if (Portfolio[_spy].Invested)
        {
            // If SPY is not assigned, we only need to buy the put.
            MarketOrder(atmPut, 1, tag: "Protective Put");
            return;
        }

        // Order the protective put strategy by combo order.
        ComboMarketOrder(
            new List<Leg> () {
                Leg.Create(_spy, 100),
                Leg.Create(atmPut, 1)
            },
            1,
            tag: "Protective Put"
        );
    }
}
public class CustomSecurityInitializer : BrokerageModelSecurityInitializer
{
    public CustomSecurityInitializer(IBrokerageModel brokerageModel, ISecuritySeeder securitySeeder)
        : base(brokerageModel, securitySeeder)
    {
    }

    public override void Initialize(Security security)
    {
        base.Initialize(security);
        // Do not allow buying power for options; only a hedging strategy with 0 margin is allowed.
        security.SetBuyingPowerModel(new NullBuyingPowerModel());
    }
}
class BuyingPowerModelAlgorithm(QCAlgorithm):
    def Initialize(self) -> None:
        self.set_start_date(2023, 10, 1)
        self.set_end_date(2024, 10, 1)
        self.set_security_initializer(CustomSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
    
        # Disable the validations of the default position group buying power model
        # that evaluates the buying power of multi-leg strategies 
        self.portfolio.set_positions(SecurityPositionGroupModel.NULL)

        # Request SPY data for trading. Set the data normalization mode to raw to allow for a fair comparison in strike price.
        self.spy = self.add_equity("SPY", data_normalization_mode=DataNormalizationMode.RAW).symbol
    
        # Set a scheduled event to rebalance the protective put strategy.
        self.schedule.on(
            self.date_rules.week_start(self.spy),
            self.time_rules.after_market_open(self.spy, 0),
            self.rebalance
        )

    def rebalance(self) -> None:
        # Select the weekend expiring ATM put for the protective put strategy.
        chain = self.option_chain(self.spy)
        filtered = [x for x in chain if x.right == OptionRight.PUT and x.expiry <= self.time + timedelta(6)]
        if not filtered: 
            return
        atm_put_contract = sorted(filtered,
            key=lambda x: (-abs(x.strike - x.underlying_last_price), x.expiry),
            reverse=True)[0]
        # Request the ATM put data for trading.
        atm_put = self.add_option_contract(atm_put_contract).symbol
    
        if self.portfolio[self.spy].invested:
            # If SPY is not assigned, we only need to buy the put.
            self.market_order(atm_put, 1, tag="Protective Put")
        else:
            # Order the protective put strategy by combo order.
            self.combo_market_order([
                    Leg.create(self.spy, 100),
                    Leg.create(atm_put, 1)
                ],
                1,
                tag="Protective Put"
            )

class CustomSecurityInitializer(BrokerageModelSecurityInitializer):
    def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder):
        super().__init__(brokerage_model, security_seeder)
    def initialize(self, security: Security):
        super().initialize(security)
        # Do not allow buying power for options; only a hedging strategy with 0 margin is allowed.
        security.SetBuyingPowerModel(NullBuyingPowerModel())

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: