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.8 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"].LastTradeProfit 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 OnData(self, slice: Slice) -> None:
    def OnEndOfDay(self, symbol: Symbol) -> None:
    def OnEndOfAlgorithm(self) -> None:

    # Indicator Helpers
    def SMA(self, symbol: Symbol, period: int) -> SimpleMovingAverage:

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 Initialize method. In live mode, the engine loads your holdings and open orders from your brokerage account to add data subscriptions and populate the Securities, Portfolio, and 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 Train 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 OnEndOfDay 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 CurrentSlice.
  5. Cancel all open orders for securities that changed their ticker.
  6. Add a Security object to the Securities collection for each new security in the universe.
  7. Update the Security objects with the latest data.
  8. Update the Cash objects in the CashBook with the latest data.
  9. Process fill models for non-market orders.
  10. Submit market on open orders to liquidate Equity Option contracts if the underlying Equity has a split warning.
  11. Process margin calls.
  12. If it's time to settle unsettled cash, perform settlement.
  13. Call the OnSecuritiesChanged method with the latest security changes.
  14. Apply dividends to the portfolio.
  15. For securities that have a split warning, update their portfolio holdings and adjust their open orders to account for the split.
  16. Update consolidators with the latest data.
  17. Pass custom data to the OnData method.
  18. Pass Dividends to the OnData method.
  19. Pass Splits to the OnData method.
  20. Pass Delistings to the OnData method.
  21. Pass TradeBars to the OnData method.
  22. Pass QuoteBars to the OnData method.
  23. Pass OptionChains to the OnData method.
  24. Pass Ticks to the OnData method.
  25. Pass the Slice to the OnData method.
  26. Perform universe selection.
  27. Pass the Slice to the Update method of each Alpha model.
  28. Pass the Insight objects from the Alpha model to the Portfolio Construction model.
  29. Pass the PortfolioTarget objects from the Portfolio Construction model to each Risk Management model.
  30. Pass the risk-adjusted PortfolioTarget objects from the Risk Management models to the Execution model.

When your algorithm stops executing, LEAN calls the OnEndOfAlgorithm method.

def OnEndOfAlgorithm(self) -> None:
    self.Debug("Algorithm done")
public override void OnEndOfAlgorithm()
{
    Debug("Algorithm done");
}

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. As a result of the bridge, QCAlgorithm class members are in camel case, not snake case. 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.

# Do this:
security_holding = self.Portfolio[self.symbol]
avg_price = security_holding.AveragePrice
quantity = security_holding.Quantity

# Avoid this:
avg_price = self.Portfolio[self.symbol].AveragePrice
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: