Generate code in 3 minutes
Enter your idea and AI generates runnable code.
To control which assets are liquidated during a margin call in QuantConnect, you must override the on_margin_call event handler in your algorithm.
SubmitOrderRequest objects designed to liquidate positions to restore margin.on_margin_call(self, requests), you intercept these requests.The following example demonstrates a strategy that intentionally over-leverages to trigger a margin call. It implements custom logic to prioritize selling the asset with the lowest Unrealized Profit (selling losers first), rather than letting the engine decide arbitrarily.
# region imports
from AlgorithmImports import *
# endregion
class MarginCallControlAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2020, 1, 1)
self.set_end_date(2021, 1, 1)
self.set_cash(100000)
# 1. Add multiple assets to the portfolio
self.tickers = ["SPY", "TLT", "GLD", "QQQ"]
self.symbols = [self.add_equity(ticker, Resolution.DAILY).symbol for ticker in self.tickers]
# 2. Set a brokerage model that allows margin trading (e.g., Interactive Brokers)
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)
def on_data(self, data: Slice):
# 3. Intentionally over-leverage to trigger margin calls
# We invest 50% in EACH of the 4 assets (200% total leverage)
if not self.portfolio.invested:
for symbol in self.symbols:
self.set_holdings(symbol, 0.5)
def on_margin_call_warning(self):
# Optional: Log when we are close to a margin call (5% buffer remaining)
self.log(f"Margin Call Warning! Remaining Margin: {self.portfolio.margin_remaining}")
def on_margin_call(self, requests: List[SubmitOrderRequest]) -> List[SubmitOrderRequest]:
"""
This method is called when the portfolio breaches maintenance margin.
'requests' contains the orders LEAN suggests executing.
We will override this to prioritize selling assets with the worst performance.
"""
self.debug(f"Margin Call Triggered at {self.time}. Processing custom liquidation logic.")
# 1. Identify which symbols are in the default request list
# (These are the positions LEAN thinks we should close)
# 2. Sort the requests based on Unrealized Profit Percentage (Ascending)
# We want to sell the positions with the lowest (most negative) profit first.
# Logic: self.portfolio[symbol].unrealized_profit_percent
sorted_requests = sorted(
requests,
key=lambda x: self.portfolio[x.symbol].unrealized_profit_percent
)
# Log the order of liquidation for debugging
for req in sorted_requests:
pnl = self.portfolio[req.symbol].unrealized_profit_percent
self.log(f"Queuing liquidation for {req.symbol}. PnL: {pnl:.2%}")
# 3. Return the modified list.
# LEAN will execute these orders in the order provided until margin is satisfied.
return sorted_requests
def on_order_event(self, order_event):
if order_event.status == OrderStatus.FILLED:
self.debug(f"Order Filled: {order_event}")
on_margin_call Signature: It must accept a list of requests and return a list of SubmitOrderRequest.SubmitOrderRequest: This object represents a potential order. It contains the symbol, quantity, and order_type.sorted_requests, the LEAN engine iterates through them and executes them one by one. It checks the margin status after every fill. If the margin requirement is met after the first order, the subsequent orders in the list might not be executed. This is why sorting the list effectively controls the priority of liquidation.Q: What happens if I return an empty list in on_margin_call?
A: If you return an empty list, no orders are executed immediately. However, the engine will detect that the margin requirement is still not met. Depending on the brokerage model, it may throw a runtime error or immediately trigger the margin call event again, potentially leading to an infinite loop or a crash. You must reduce leverage when this event fires.
Q: Can I change the quantity of the assets being sold?
A: Yes. You can modify the quantity property of the SubmitOrderRequest objects inside the list before returning them. For example, you could double the quantity to liquidate more than necessary, creating a "safety buffer" to prevent another margin call the next day.
Q: How is on_margin_call_warning different?
A: on_margin_call_warning is a notification that triggers when you have less than 5% margin remaining. It does not pass order requests and does not execute trades automatically. It is your chance to manually reduce leverage (e.g., self.liquidate()) before the forced liquidation of the actual on_margin_call occurs.