book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Trading and Orders

Liquidating Positions

Introduction

The Liquidateliquidate method lets you liquidate individual assets or your entire portfolio. The method creates market orders to close positions and returns the tickets of the liquidation orders. If you have pending open orders for the security when you call Liquidateliquidate, LEAN tries to cancel them. The Liquidateliquidate method works for all asset classes, except Crypto. To liquidate Crypto positions, see Crypto Trades.

Liquidate Individual Positions

To liquidate your holdings in an individual security, call the Liquidateliquidate method and provide a ticker or Symbol.

// Liquidate all IBM in your portfolio
var orderTickets = Liquidate("IBM");
# Liquidate all IBM in your portfolio
order_tickets = self.liquidate("IBM")

You can pass an order tag and properties to the Liquidateliquidate method.

var orderProperties = new OrderProperties() { TimeInForce = TimeInForce.Day };
var orderTickets = Liquidate("AAPL", tag: "Liquidated", orderProperties: orderProperties);
order_properties = OrderProperties()
order_properties.time_in_force = TimeInForce.DAY
order_tickets = self.liquidate("AAPL", tag="Liquidated", order_properties=order_properties)

Liquidate All Positions

To liquidate all of the positions in your portfolio, call the Liquidateliquidate method without any ticker of Symbol arguments.

// Liquidate your entire portfolio
var orderTickets = Liquidate();
// Liquidate your entire portfolio
order_tickets = self.liquidate()

You can pass an order tag and properties to the Liquidateliquidate method.

var orderProperties = new OrderProperties() { TimeInForce = TimeInForce.Day };
var orderTickets = Liquidate(tag: "Liquidated", orderProperties: orderProperties);
order_properties = OrderProperties()
order_properties.time_in_force = TimeInForce.DAY
order_tickets = self.liquidate(tag="Liquidated", order_properties=order_properties)

Place Asynchronous Liquidations

When you trade a large portfolio of assets, you may want to send orders in batches and not wait for the response of each one. To send asynchronous liquidation orders, set the asynchronous argument to Truetrue.

var orderTickets = Liquidate(asynchronous: true, orderProperties: orderProperties);
order_tickets = self.liquidate(asynchronous=True, order_properties=order_properties)

Enable and Disable Liquidations

By default, the Liquidateliquidate method is functional. To enable and disable it, set the LiquidateEnabledliquidate_enabled algorithm setting.

// Disable liquidations
Settings.LiquidateEnabled = false;

// Enable liquidations
Settings.LiquidateEnabled = true;
# Disable liquidations
self.settings.liquidate_enabled = False

# Enable liquidations
self.settings.liquidate_enabled = True

Market Closed Considerations

If you liquidate your positions when the market is closed, LEAN converts the orders into market on open orders. If your brokerage doesn't support market on open orders, the order is invalid.

Examples

The following examples demonstrate some common practices for liquidating positions.

Example 1: Liquidate Universe Removal

The following algorithm holds equal positions for the top 10 liquid universe to follow their hype while updating weekly. When a stock is removed from the universe, we liquidate the position in the OnSecuritiesChangedon_securities_changed method.

public class LiquidatingPositionsAlgorithm : QCAlgorithm
{
    private Universe _universe;

    public override void Initialize()
    {
        SetStartDate(2023, 1, 1);
        SetEndDate(2023, 8, 1);

        // Update the universe weekly to allow time to capitalize on the hype.
        UniverseSettings.Schedule.On(DateRules.WeekStart());
        // Filter for the 10 most popular stocks to invest the hype.
        _universe = AddUniverse(Universe.DollarVolume.Top(10));

        // Set a schedule event to rebalance weekly.
        Schedule.On(
            DateRules.WeekStart(),
            TimeRules.At(9, 31),
            Rebalance
        );
    }

    private void Rebalance()
    {
        // Evenly invest in the universe members to dissipate the capital risk equally.
        var count = _universe.Members.Count;
        var targets = _universe.Members.Select(x => new PortfolioTarget(x.Key, 1m / count)).ToList();
        SetHoldings(targets);
    }

    public override void OnSecuritiesChanged(SecurityChanges changes)
    {
        // Liquidate the ones leaving the universe since they are not the most popular.
        // Note that the liquidation will be market-on-open orders due to the time of securities removal.
        foreach (var removed in changes.RemovedSecurities)
        {
            Liquidate(removed.Symbol);
        }
    }
}
class LiquidatingPositionsAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2023, 1, 1)
        self.set_end_date(2023, 8, 1)
        
        # Update the universe weekly to allow time to capitalize on the hype.
        self.universe_settings.schedule.on(self.date_rules.week_start())
        # Filter for the 10 most popular stocks to invest the hype.
        self._universe = self.add_universe(self.universe.dollar_volume.top(10))

        # Set a schedule event to rebalance weekly.
        self.schedule.on(
            self.date_rules.week_start(),
            self.time_rules.at(9, 31),
            self.rebalance
        )

    def rebalance(self) -> None:
        # Evenly invest in the universe members to dissipate the capital risk equally.
        count = len(self._universe.members)
        targets = [PortfolioTarget(x.key, 1 / count) for x in self._universe.members]
        self.set_holdings(targets)

    def on_securities_changed(self, changes: SecurityChanges) -> None:
        # Liquidate the ones leaving the universe since they are not the most popular.
        # Note that the liquidation will be market-on-open orders due to the time of securities removal.
        for removed in changes.removed_securities:
            self.liquidate(removed.symbol)

Example 2: Liquidate Existing Positions

The example below trades the same strategy as the previous one. Still, instead of liquidating the positions when the stocks leave the universe, we liquidate the position using the liquidateExistingHoldingsliquidate_existing_holdings argument in the SetHoldingsset_holdings. Note that the time of the liquidation will be slightly delayed due to the time of the scheduled event.

public class LiquidatingPositionsAlgorithm : QCAlgorithm
{
    private Universe _universe;

    public override void Initialize()
    {
        SetStartDate(2023, 1, 1);
        SetEndDate(2023, 8, 1);

        // Update the universe weekly to allow time to capitalize on the hype.
        UniverseSettings.Schedule.On(DateRules.WeekStart());
        // Filter for the 10 most popular stocks to invest the hype.
        _universe = AddUniverse(Universe.DollarVolume.Top(10));

        // Set a schedule event to rebalance weekly.
        Schedule.On(
            DateRules.WeekStart(),
            TimeRules.At(9, 31),
            Rebalance
        );
    }

    private void Rebalance()
    {
        // Evenly invest in the universe members to dissipate the capital risk equally.
        var count = _universe.Members.Count;
        var targets = _universe.Members.Select(x => new PortfolioTarget(x.Key, 1m / count)).ToList();
        // We can use `liquidateExistingHoldings` arguments to liquidate the ones not in the universe anymore.
        SetHoldings(targets, liquidateExistingHoldings: true);
    }
}
class LiquidatingPositionsAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2023, 1, 1)
        self.set_end_date(2023, 8, 1)
        
        # Update the universe weekly to allow time to capitalize on the hype.
        self.universe_settings.schedule.on(self.date_rules.week_start())
        # Filter for the 10 most popular stocks to invest the hype.
        self._universe = self.add_universe(self.universe.dollar_volume.top(10))

        # Set a scheduled event to rebalance weekly.
        self.schedule.on(
            self.date_rules.week_start(),
            self.time_rules.at(9, 31),
            self.rebalance
        )

    def rebalance(self) -> None:
        # Evenly invest in the universe members to dissipate the capital risk equally.
        count = len(self._universe.members)
        targets = [PortfolioTarget(x.key, 1 / count) for x in self._universe.members]
        # We can use `liquidate_existing_holdings` arguments to liquidate the ones not in the universe anymore.
        self.set_holdings(targets, liquidate_existing_holdings=True)

Example 3: Liquidate Upon Assignment

The following algorithm sells SPY ATM puts weekly to earn the premium when SPY price is rising. In the case where the price of SPY drops and the put is being assigned, we liquidate the SPY position in the OnAssignmentOrderEventon_assignment_order_event.

public class LiquidatingPositionsAlgorithm : QCAlgorithm
{
    private Symbol _spy;

    public override void Initialize()
    {
        SetStartDate(2023, 1, 1);
        SetEndDate(2023, 8, 1);
        // Seed the price with the last known price to ensure the underlying price data is available on initial option contract filtering.
        SetSecurityInitializer(new BrokerageModelSecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices)));

        // Request SPY data for option selection using its price.
        // Use raw data normalization mode since we need to compare the SPY price with the strike price.
        _spy = AddEquity("SPY", dataNormalizationMode: DataNormalizationMode.Raw).Symbol;

        // Set a schedule event to rebalance weekly.
        Schedule.On(
            DateRules.WeekStart(),
            TimeRules.At(9, 31),
            Rebalance
        );
    }

    private void Rebalance()
    {
        // Select the 1-week ATM put SPY option to short.
        var optionChain = OptionChain(_spy);
        var expiry = optionChain.Where(x => x.Expiry < Time.AddDays(6))
            .Max(x => x.Expiry);
        var atmPut = optionChain.Where(x => x.Right == OptionRight.Put && x.Expiry == expiry)
            .OrderBy(x => Math.Abs(x.Strike - Securities[_spy].Price))
            .First();
        var atmPutSymbol = AddOptionContract(atmPut).Symbol;

        // Sell the ATM put option to earn the premium.
        Sell(atmPutSymbol, 2);
    }

    public override void OnAssignmentOrderEvent(OrderEvent assignmentEvent)
    {
        // Liquidate any assigned positions of SPY since we only earn the premium.
        Liquidate(_spy);
    }
}
class LiquidatingPositionsAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2023, 1, 1)
        self.set_end_date(2023, 8, 1)
        # Seed the price with the last known price to ensure the underlying price data is available on initial option contract filtering.
        self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
        
        # Request SPY data for option selection using its price.
        # Use raw data normalization mode since we need to compare the SPY price with the strike price.
        self.spy = self.add_equity("SPY", data_normalization_mode=DataNormalizationMode.RAW).symbol

        # Set a scheduled event to rebalance weekly.
        self.schedule.on(
            self.date_rules.week_start(),
            self.time_rules.at(9, 31),
            self.rebalance
        )

    def rebalance(self) -> None:
        # Select the 1-week ATM put SPY option to short.
        option_chain = self.option_chain(self.spy)
        expiry = max(x.expiry for x in option_chain if x.expiry < self.time + timedelta(6))
        atm_put = sorted([x for x in option_chain if x.expiry == expiry and x.right == OptionRight.PUT],
                        key=lambda x: abs(x.strike - self.securities[self.spy].price))[0]
        atm_put_symbol = self.add_option_contract(atm_put).symbol

        # Sell the ATM put option to earn the premium.
        self.sell(atm_put_symbol, 2)

    def on_assignment_order_event(self, assignment_event: OrderEvent) -> None:
        # Liquidate any assigned positions of SPY since we only earn the premium.
        self.liquidate(self.spy)

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: