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"].Priceself.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"].LastTradeProfitself.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 Securitiessecurities, Portfolioportfolio, and Transactionstransactions 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.

  1. 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 OnEndOfDayon_end_of_day method at the end of each day.
  2. In live mode, Scheduled Events occur in a separate thread from the algorithm manager, so they run at the correct time.

  3. Update the algorithm time.
  4. Update the CurrentSlicecurrent_slice.
  5. Pass the SymbolChangedEvents to the OnSymbolChangedEventson_symbol_changed_events method.
  6. Cancel all open orders for securities that changed their ticker.
  7. Add a Security object to the Securitiessecurities collection for each new security in the universe.
  8. Update the Security objects with the latest data.
  9. Update the Cash objects in the CashBook with the latest data.
  10. Process fill models for non-market orders.
  11. Submit market on open orders to liquidate Equity Option contracts if the underlying Equity has a split warning.
  12. Process margin calls.
  13. If it's time to settle unsettled cash, perform settlement.
  14. Call the OnSecuritiesChangedon_securities_changed method with the latest security changes.
  15. Apply dividends to the portfolio.
  16. For securities that have a split warning, update their portfolio holdings and adjust their open orders to account for the split.
  17. Update consolidators with the latest data.
  18. Pass custom data to the OnDataon_data method.
  19. Pass the Dividends to the OnDividendson_dividends method.
  20. Pass the Splits to the OnSplitson_splits method.
  21. Pass the Delistings to the OnDelistingson_delistings method.
  22. Pass the Slice to the OnDataon_data method.
  23. Perform universe selection.
  24. Pass the Slice to the Updateupdate method of each Alpha model.
  25. Pass the Insight objects from the Alpha model to the Portfolio Construction model.
  26. Pass the PortfolioTarget objects from the Portfolio Construction model to each Risk Management model.
  27. Pass the risk-adjusted PortfolioTarget objects from the Risk Management models to the Execution model.

When your algorithm stops executing, LEAN calls the OnEndOfAlgorithmon_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

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: