Generate code in 3 minutes
Enter your idea and AI generates runnable code.
To automatically reduce your position size to avoid a margin call in QuantConnect, you should utilize two specific event handlers provided by the LEAN engine:
on_margin_call_warning: This is your proactive defense. It triggers when your remaining margin drops below 5%. You can use this to liquidate a portion of your portfolio to free up buying power before a forced liquidation occurs.on_margin_call: This is your reactive defense. It triggers when a margin call is actually issued. By default, LEAN liquidates positions to satisfy the margin requirement. You can override this to control which assets are sold (e.g., selling losing positions first).The following algorithm demonstrates how to implement both handlers. It simulates a high-leverage scenario to demonstrate how the bot reacts by reducing positions.
# region imports
from AlgorithmImports import *
# endregion
class MarginManagementAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2020, 1, 1)
self.set_end_date(2021, 1, 1)
self.set_cash(100000)
# Add a volatile asset and a stable asset
self.spy = self.add_equity("SPY", Resolution.MINUTE).symbol
self.tsla = self.add_equity("TSLA", Resolution.MINUTE).symbol
# Set leverage high to increase the probability of margin issues for demonstration
self.securities[self.spy].set_leverage(2.0)
self.securities[self.tsla].set_leverage(2.0)
# Schedule a buy event
self.schedule.on(self.date_rules.every_day(self.spy),
self.time_rules.after_market_open(self.spy, 30),
self.rebalance)
def rebalance(self):
# Intentionally use high leverage (near 100% of buying power)
if not self.portfolio.invested:
self.set_holdings([
PortfolioTarget(self.spy, 0.95),
PortfolioTarget(self.tsla, 0.95)
])
def on_data(self, data: Slice):
# Standard data processing
pass
def on_margin_call_warning(self):
"""
Triggered when there is less than 5% margin remaining.
We proactively reduce leverage here to avoid the actual margin call.
"""
self.log(f"MARGIN WARNING at {self.time}: Margin remaining < 5%. Reducing positions.")
# Strategy: Liquidate 10% of all invested positions to free up cash
for symbol, holding in self.portfolio.items():
if holding.invested:
# Calculate 10% of the current quantity
quantity_to_reduce = int(holding.quantity * 0.10)
# Ensure we don't submit an order for 0 shares
if quantity_to_reduce != 0:
self.market_order(symbol, -quantity_to_reduce)
self.log(f"Proactive Reduction: Sold {quantity_to_reduce} of {symbol}")
def on_margin_call(self, requests: List[SubmitOrderRequest]) -> List[SubmitOrderRequest]:
"""
Triggered when the portfolio has crossed the margin threshold.
LEAN generates a list of 'requests' (orders) to satisfy the margin call.
We can modify this list to choose WHICH assets to sell.
"""
self.log(f"MARGIN CALL at {self.time}: Executing forced liquidation logic.")
# Strategy: Sort the liquidation requests to sell the positions with the
# highest Unrealized Loss first (Stop the bleeding).
# Helper function to get unrealized profit percent
def get_unrealized_profit(request):
if request.symbol in self.portfolio:
return self.portfolio[request.symbol].unrealized_profit_percent
return 0
# Sort requests: Lowest profit (highest loss) first
sorted_requests = sorted(requests, key=get_unrealized_profit)
# Log our decision
for order in sorted_requests:
self.log(f"Margin Call Liquidation Priority: {order.symbol}")
# Return the sorted list. LEAN will execute these orders until margin is satisfied.
return sorted_requests
on_margin_call_warning Logic:
self.portfolio.items() to find invested securities.holding.quantity * 0.10) and places a market_order to close that portion.on_margin_call Logic:
requests, which is a list of SubmitOrderRequest objects that the LEAN engine has calculated are necessary to fix the margin deficit.sorted(requests, key=get_unrealized_profit) ensures that the algorithm sells the assets performing the worst first.on_margin_call orders are executed synchronously immediately after the method returns.BuyingPowerModel of the securities you are trading (e.g., PatternDayTradingMarginModel vs SecurityMarginModel), as this dictates when these events are triggered.on_margin_call_warning is a soft warning generated by LEAN's internal tracking, not necessarily a message from the broker. It relies on LEAN's data matching the broker's state accurately.Q: Can I use on_margin_call_warning to deposit virtual cash in a backtest?
A: No, you cannot inject cash during a backtest to solve a margin call. You must reduce holdings. However, in live trading, you could theoretically use the on_margin_call_warning to send an alert (email/SMS) to yourself to deposit funds, though the algorithm itself must still handle the immediate risk.
Q: What happens if I return an empty list in on_margin_call?
A: If you return an empty list or do not satisfy the margin requirement, LEAN will likely throw a runtime error or forcefully liquidate positions in subsequent steps to protect the brokerage model integrity. You must return enough orders to satisfy the margin debt.
Q: Does on_margin_call_warning work for crypto futures?
A: Yes, provided the BuyingPowerModel is correctly set for the crypto future. However, crypto markets are highly volatile; the gap between a 5% warning and liquidation can happen in milliseconds. It is safer to manage position sizing strictly in on_data rather than relying solely on the warning handler.