Problem Description
Solution
In QuantConnect, SPAN (Standard Portfolio Analysis of Risk) margining is enabled by default for Futures. The LEAN engine automatically sources CME SPAN margin data to model buying power and margin requirements for supported futures contracts.
You do not need to implement the SPAN logic manually. Instead, you simply add the Future security, and the BuyingPowerModel attached to that security will utilize the historical SPAN margin data available in the LEAN data library.
Below is a complete algorithm demonstrating how to trade Futures with SPAN margining and how to programmatically check the margin requirements before placing a trade.
Futures Strategy with SPAN Margining
# region imports
from AlgorithmImports import *
# endregion
class SpanMarginingExampleAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 6, 1)
self.set_cash(100000)
# 1. Add the Future.
# LEAN automatically attaches a FutureMarginModel which uses SPAN data.
self._future = self.add_future(
Futures.Indices.SP_500_E_MINI,
Resolution.MINUTE,
data_normalization_mode=DataNormalizationMode.RAW
)
# Filter for contracts expiring within 90 days
self._future.set_filter(0, 90)
self._contract_symbol = None
def on_data(self, slice: Slice):
# Wait for the chain to populate
if self._contract_symbol is None:
for chain in slice.future_chains:
# Select the front month contract
contracts = list(chain.value)
if len(contracts) == 0:
continue
self._contract_symbol = sorted(contracts, key=lambda x: x.expiry)[0].symbol
return
# Ensure we have data for the specific contract
if not slice.contains_key(self._contract_symbol):
return
# Simple logic: Buy if not invested
if not self.portfolio.invested:
# 2. Check SPAN Margin Requirements before trading
# We can query the Buying Power Model to see the required margin for 1 contract
security = self.securities[self._contract_symbol]
# Create parameters for 1 unit (Long)
# InitialMarginParameters(security, quantity)
margin_params = InitialMarginParameters(security, 1)
# Get the requirement
margin_req = security.buying_power_model.get_initial_margin_requirement(margin_params)
self.log(f"Contract: {self._contract_symbol}")
self.log(f"Price: {security.price}")
self.log(f"SPAN Initial Margin Requirement (1 contract): ${margin_req.value}")
# Check if we have enough available buying power
if self.portfolio.margin_remaining > margin_req.value:
self.market_order(self._contract_symbol, 1)
else:
self.log("Insufficient margin to place trade.")
def on_securities_changed(self, changes: SecurityChanges):
# Handle contract rollovers or expirations here if necessary
for removed in changes.removed_securities:
if removed.symbol == self._contract_symbol:
self.liquidate(removed.symbol)
self._contract_symbol = None
Key Implementation Details
- Default Behavior: When you use
self.add_future(...), LEAN assigns theFutureMarginModel. This model looks up the historical SPAN margin requirements provided by the data feed (e.g., CME data) for the specific date and contract. - Checking Margin: You can inspect the specific margin requirement using
security.buying_power_model.get_initial_margin_requirement(parameters). This is useful for dynamic position sizing based on actual risk rather than notional value. - Intraday vs. Overnight: The SPAN data provided is typically the overnight maintenance and initial margin. Some brokerages offer reduced intraday margin, but LEAN's default data reflects the exchange-mandated SPAN margins.
Q&A: Futures Margining in QuantConnect
Q: Do I need to import a specific library to calculate SPAN?
A: No. The LEAN engine handles the calculation internally using its data libraries. You only need to add the Future security using add_future.
Q: Does this work for all Futures contracts?
A: It works for major futures contracts supported by QuantConnect (e.g., CME, CBOT, NYMEX, COMEX) where margin data is available in the data library.
Q: Can I override the SPAN margin with a custom fixed margin?
A: Yes. If you want to simulate a brokerage that offers fixed intraday margins (e.g., $500 per contract), you can set a custom buying power model:
self.securities[symbol].set_buying_power_model(SecurityMarginModel(30.0)) # 30x leverage
However, for accurate backtesting of institutional strategies, keeping the default SPAN model is recommended.
Q: How does the algorithm handle margin calls with SPAN?
A: If your portfolio equity drops below the Maintenance Margin (derived from SPAN data), LEAN triggers a margin call. You can manage this via the on_margin_call_warning and on_margin_call event handlers.