Key Concepts
Algorithm Engine
Introduction
LEAN Engine is an open-source algorithmic trading engine built for easy strategy research, backtesting, and live trading. We integrate with common data providers and brokerages, so you can quickly deploy algorithmic trading strategies. The core of the LEAN Engine is written in C#, but it operates seamlessly on Linux, Mac and Windows operating systems. To use it, you can write algorithms in Python 3.11 or C#. QuantConnect maintains the LEAN project and uses it to drive the web-based algorithmic trading platform on the website.
Since LEAN is open-source, you aren't locked-in to the QuantConnect platform. You can run LEAN with your own infrastructure, data, and brokerage connections. If you use QuantConnect, our team of engineers manage the infrastructure, you can run the latest version of LEAN with all of its brokerage connections, and you can utilize the datasets in the Dataset Market.
Your Algorithm and LEAN
To create a trading algorithm with LEAN, define a subclass of the QCAlgorithm
class. LEAN loads your algorithm into the engine during the building and compilation process. LEAN runs an algorithm manager that synchronizes the data your algorithm requests, injects the data into your algorithm so you can place trades, processes your orders, and then updates your algorithm state.
The LEAN engine manages your portfolio and data feeds, so you focus on your algorithm strategy and execution. We automatically provide basic portfolio management and reality modeling underneath the hood. The QCAlgorithm
class provides some key helper properties for you to use, including the Security Manager, Portfolio Manager, Transactions Manager, Notification Manager, and Scheduling Manager. The class also has hundreds of helper methods to make the API easy to use.
The Securities property is a dictionary of Security
objects. Each asset (Equity, Forex pair, etc) in your algorithm has a Security
object. All the models for a security live on these objects. For example, Securities["IBM"].FeeModel
and Securities["IBM"].Price
self.Securities["IBM"].FeeModel
and self.Securities["IBM"].Price
return the fee model and price of IBM, respectively.
The Portfolio is a dictionary of SecurityHolding
objects. These classes track the profits, losses, fees, and quantity of individual portfolio holdings. For example, Portfolio["IBM"].LastTradeProfit
self.portfolio["IBM"].last_trade_profit
returns the profit of your last IBM trade.
Other helpers like Transactions, Schedule, Notify, and Universe have their own helper methods.
public class QCAlgorithm { SecurityManager Securities; // Array of Security objects. SecurityPortfolioManager Portfolio; // Array of SecurityHolding objects SecurityTransactionManager Transactions; // Transactions helper ScheduleManager Schedule; // Scheduling helper NotificationManager Notify; // Email, SMS helper UniverseManager Universe; // Universe helper // Set up Requested Data, Cash, Time Period. public virtual void Initialize() { ... }; // Event Handlers: public virtual void OnData(Slice slice) { ... }; public virtual void OnEndOfDay(Symbol symbol) { ... }; public virtual void OnEndOfAlgorithm() { ... }; // Indicator Helpers public SimpleMovingAverage SMA(Symbol symbol, int period) { ... }; }
class QCAlgorithm: securities # Array of Security objects. portfolio # Array of SecurityHolding objects transactions # Transactions helper schedule # Scheduling helper notify # Email, SMS helper universe # Universe helper # Set up Requested Data, Cash, Time Period. def initialize(self) -> None: # Other Event Handlers def on_data(self, slice: Slice) -> None: def on_end_of_day(self, symbol: Symbol) -> None: def on_end_of_algorithm(self) -> None: # Indicator Helpers def sma(self, symbol: Symbol, period: int) -> SimpleMovingAverage:
Only define one algorithm class per project. To trade multiple strategies in a single algorithm, implement an Algorithm Framework design with multiple Alpha models. To be notified when you can define more than one algorithm class per project, subscribe to GitHub Issue #6968.
Threads in LEAN
LEAN is multi-threaded and attempts to consume as much CPU as possible to perform given work as quickly as possible. The analysis engine loads data parallel and synchronizes it to generate the requested security information.
The client algorithm is plugged into LEAN, and has its events triggered synchronously in backtesting. The primary bottle neck to LEAN execution is executing client code.
In live trading, most events are synchronous as in backtesting, however order events are triggered immediately from the brokerage thread (asynchronously) to ensure the lowest latency. For example; a strategy using hourly data, with a limit order could fill between data bars. This fill event is triggered when it occurs in live trading.
We recommend using thread safe collections when possible, or locks around collections to ensure they are thread-safe.
Batch vs Stream Analysis
Backtesting platforms come in two general varieties, batch processing and event streaming. Batch processing backtesting is much simpler. It loads all data into an array and passes it to your algorithm for analysis. Because your algorithm has access to future data points, it is easy to introduce look-ahead bias. Most home-grown analysis tools are batch systems.
QuantConnect/LEAN is a streaming analysis system. In live trading, your algorithm receives data points one after another over time. QuantConnect models this in backtesting, streaming data to your algorithm in fast-forward mode. Because of this, you can't access price data beyond the Time Frontier. Although streaming analysis is slightly trickier to understand, it allows your algorithm to seamlessly work in backtests and live trading with no code changes.
Event Flow
When you deploy an algorithm, LEAN first calls the Initializeinitialize method. In live mode, the engine loads your holdings and open orders from your brokerage account to add data subscriptions and populate the Securities
securities
, Portfolio
portfolio
, and Transactions
transactions
objects. LEAN receives the data from the subscriptions, synchronizes the data to create a timeslice, and then performs the following steps. Your algorithm can spend up to 10 minutes on each timeslice unless you call the Traintrain method.
- If it's a backtest, check if there are Scheduled Events in the past that didn't fire because there was no data between the previous slice and the current slice. LEAN automatically creates a Scheduled Event to call the
OnEndOfDay
on_end_of_day
method at the end of each day. - Update the algorithm time.
- Update the
CurrentSlice
current_slice
. - Pass the
SymbolChangedEvents
to theOnSymbolChangedEvents
on_symbol_changed_events
method. - Cancel all open orders for securities that changed their ticker.
- Add a
Security
object to theSecurities
securities
collection for each new security in the universe. - Update the
Security
objects with the latest data. - Update the
Cash
objects in the CashBook with the latest data. - Process fill models for non-market orders.
- Submit market on open orders to liquidate Equity Option contracts if the underlying Equity has a split warning.
- Process margin calls.
- If it's time to settle unsettled cash, perform settlement.
- Call the OnSecuritiesChangedon_securities_changed method with the latest security changes.
- Apply dividends to the portfolio.
- For securities that have a split warning, update their portfolio holdings and adjust their open orders to account for the split.
- Update consolidators with the latest data.
- Pass custom data to the
OnData
on_data
method. - Pass the
Dividends
to theOnDividends
on_dividends
method. - Pass the
Splits
to theOnSplits
on_splits
method. - Pass the
Delistings
to theOnDelistings
on_delistings
method. - Pass the
Slice
to theOnData
on_data
method. - Perform universe selection.
- Pass the
Slice
to theUpdate
update
method of each Alpha model. - Pass the
Insight
objects from the Alpha model to the Portfolio Construction model. - Pass the
PortfolioTarget
objects from the Portfolio Construction model to each Risk Management model. - Pass the risk-adjusted
PortfolioTarget
objects from the Risk Management models to the Execution model.
In live mode, Scheduled Events occur in a separate thread from the algorithm manager, so they run at the correct time.
When your algorithm stops executing, LEAN calls the OnEndOfAlgorithm
on_end_of_algorithm
method.
Python Support
The LEAN engine is written in C#, but you can create algorithms in C# or Python. If you program in Python, LEAN uses Python.Net to bridge between the C# engine and your algorithm. It can be slow to move from Python to C#. If you access C# objects in your Python algorithm, it's fastest to only access them once and save a reference if you need to access them again.
# Store the security holding in a variable for better readability and performance. security_holding = self.portfolio[self._symbol] avg_price = security_holding.average_price quantity = security_holding.quantity # Avoid indexing the portfolio object multiple times for each attribute. avg_price = self.portfolio[self._symbol].average_price quantity = self.portfolio[self._symbol].quantity