Alpha
Key Concepts
Introduction
The Alpha model predicts market trends and signals the best moments to trade. These signals, or Insight
objects, contain the Direction
, Magnitude
, and Confidence
of a market prediction and the suggested portfolio Weight
. You should generate insights on the set of assets provided by the Universe Selection model and only generate them when your predictions change.
Add Models
To add an Alpha model, in the Initialize
initialize
method, call the AddAlpha
method.
// Add EmaCrossAlphaModel to generate market trend insights and trading signals based on EMA crossovers, providing Direction, Magnitude, and Confidence for the universe-selected assets. AddAlpha(new EmaCrossAlphaModel());
# Add EmaCrossAlphaModel to generate market trend insights and trading signals based on EMA crossovers, providing Direction, Magnitude, and Confidence for the universe-selected assets. self.add_alpha(EmaCrossAlphaModel())
To view all the pre-built Alpha models, see Supported Models.
Multi-Alpha Algorithms
You can add multiple Alpha models to a single algorithm and generate Insight objects with all of them.
// Add RsiAlphaModel and EmaCrossAlphaModel to generate trading signals based on RSI and EMA crossovers, utilizing multiple alpha models to capture different market signals and trends for robust predictions. AddAlpha(new RsiAlphaModel()); AddAlpha(new EmaCrossAlphaModel());
# Add RsiAlphaModel and EmaCrossAlphaModel to generate trading signals based on RSI and EMA crossovers, utilizing multiple alpha models to capture different market signals and trends for robust predictions. self.add_alpha(RsiAlphaModel()) self.add_alpha(EmaCrossAlphaModel())
Each Alpha model has a unique name. The Insight objects they generate are automatically named according to the Alpha model name.
If you add multiple Alpha models, each alpha model receives the current slice in the order that you add the Alphas. The combined stream of Insight objects is passed to the Portfolio Construction model that defines how the Insight collection is combined. If you have a hybrid algorithm, the combined stream of Insight objects is passed to the event handler.
Model Structure
Alpha models should extend the AlphaModel
class. Extensions of the AlphaModel
class must implement the Update
update
method, which receives a Slice object and returns an array of Insight
objects. Extensions should also implement the OnSecuritiesChanged
on_securities_changed
method to track security changes in the universe.
// Algorithm framework model that produces insights class MyAlphaModel : AlphaModel { // Updates this Alpha model with the latest data from the algorithm. // This is called each time the algorithm receives data for subscribed securities public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data) { var insights = new List<Insight>(); return insights; } public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { // Security additions and removals are pushed here. // This can be used for setting up algorithm state. // changes.AddedSecurities // changes.RemovedSecurities } }
# Algorithm framework model that produces insights class MyAlphaModel(AlphaModel): def update(self, algorithm: QCAlgorithm, data: Slice) -> List[Insight]: # Updates this Alpha model with the latest data from the algorithm. # This is called each time the algorithm receives data for subscribed securities # Generate insights on the securities in the universe. insights = [] return insights def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None: # Security additions and removals are pushed here. # This can be used for setting up algorithm state. # changes.added_securities # changes.removed_securities pass
Method Parameters
The algorithm
parameter that the methods receive is an instance of the base QCAlgorithm
class, not your subclass of it. To access members of your algorithm object, either use a global variable or pass an algorithm reference to Alpha model constructor. Both of these approaches violate the separation of concerns principle.
The data
parameter contains the latest data available to the algorithm.
changes
parameter contains the universe changes. Model Names
To ensure your Alpha model is compatible with all Portfolio Construction models, assign a unique name to your Alpha model. Some Portfolio Construction models can combine multiple Alpha models together, so it can be important to distinguish between the Alpha models.
// Assign a unique name to RsiAlphaModel to ensure compatibility and distinguish it when combining with other Alpha models in portfolio construction. public class RsiAlphaModel : AlphaModel { // Give your alpha a name (perhaps based on its constructor args?) public override string Name { get; } }
By default, LEAN uses the string representation of a Guid object to set the Alpha model name. An example default name is "0f8fad5b-d9cb-469f-a165-70867728950e". This default name is different for every backtest you run and every live algorithm you deploy. For consistent behavior, manually set the Alpha model name.
Example
To view a full example of an AlphaModel
subclass, see the ConstantAlphaModelConstantAlphaModel in the LEAN GitHub repository.
Track Security Changes
The Universe Selection model may select a dynamic universe of assets, so you should not assume a fixed set of assets in the Alpha model. When the Universe Selection model adds and removes assets from the universe, it triggers an OnSecuritiesChanged
on_securities_changed
event. In the OnSecuritiesChanged
on_securities_changed
event handler, you can initialize the security-specific state or load any history required for your Alpha model. If you need to save data for individual securities, add custom members to the respective Security
objectcast the Security
object to a dynamic
object and then save custom members to it.
// Handle dynamic asset changes by initializing indicators for new securities and removing them for removed securities to keep the Alpha model adaptive and accurate. class MyAlphaModel : AlphaModel { private List<Security> _securities = new List<Security>(); public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { foreach (var security in changes.AddedSecurities) { var dynamicSecurity = security as dynamic; dynamicSecurity.Sma = algorithm.SMA(security.Symbol, 20); algorithm.WarmUpIndicator(security.Symbol, dynamicSecurity.Sma); _securities.Add(security); } foreach (var security in changes.RemovedSecurities) { if (_securities.Contains(security)) { algorithm.DeregisterIndicator((security as dynamic).Sma); _securities.Remove(security); } } } }
# Handle dynamic asset changes by initializing indicators for new securities and removing them for removed securities to keep the Alpha model adaptive and accurate. class MyAlphaModel(AlphaModel): securities = [] def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None: for security in changes.added_securities: security.indicator = algorithm.SMA(security.symbol, 20) algorithm.warm_up_indicator(security.symbol, security.indicator) self.securities.append(security) for security in changes.removed_securities: if security in self.securities: algorithm.deregister_indicator(security.indicator) self.securities.remove(security)
Insights
An Insight is a single prediction for an asset. The Update
update
method returns an array of Insight objects. You can think of these as actionable trading signals, indicating the asset direction, magnitude, and confidence in the near future. All insights can take a weight parameter to set the desired weighting for the insight. If you change the cash of the algorithm, it will affect the orders, but not necessarily the insights.
The Portfolio Construction model consumes the Insight objects from the Alpha model. It's up to the Portfolio Construction model to define how Insight objects are converted into PortfolioTarget
objects. In the pre-built Portfolio Construction models, down insights translate to short-biased trades, up insights translate to long-biased trades, and flat insights usually close open positions, but this doesn't have to be the case in custom Portfolio Construction models.
Insight Properties
Insight
objects have the following properties:
Create Insights
To create an Insight
, call the Insight
constructor.
# Insight(symbol, period, type, direction, magnitude=None, confidence=None, source_model=None, weight=None, tag='') insight = Insight("IBM", timedelta(minutes=20), InsightType.PRICE, InsightDirection.UP)
// new Insight(symbol, period, type, direction, magnitude=null, confidence=null, sourceModel=null, weight=null, tag=""); var insight = new Insight("IBM", TimeSpan.FromMinutes(20), InsightType.Price, InsightDirection.Up);
In the Insight
constructor, the period
argument can be a timedelta
TimeSpan
object or a function that receives a DateTime
datetime
object and returns the expiry DateTime
datetime
. Some of the constructor arguments are optional to create the Insight
object, but some of the Portfolio Construction models may require these properties. To check which Insight
properties a Portfolio Construction model requires, see Supported Models.
You can also use the helper method to create Insight objects of the Price type.
# Insight.price(symbol, period, direction, magnitude=None, confidence=None, source_model=None, weight=None, tag='') insight = Insight.price("IBM", timedelta(minutes = 20), InsightDirection.UP) # Insight.price(symbol, resolution, barCount, direction, magnitude=None, confidence=None, source_model=None, weight=None, tag='') insight = Insight.price("IBM", Resolution.MINUTE, 20, InsightDirection.UP)
// new Insight(symbol, period, direction, magnitude=null, confidence=null, sourceModel=null, weight=null, tag="") var insight = Insight.Price("IBM", TimeSpan.FromMinutes(20), InsightDirection.Up); // new Insight(symbol, resolution, barCount, direction, magnitude=null, confidence=null, sourceModel=null, weight=null, tag="") var insight = Insight.Price("IBM", Resolution.Minute, 20, InsightDirection.Up);
In the Price
price
method, the period
argument can be a timedelta
TimeSpan
object, a DateTime
datetime
object, or a function that receives a DateTime
datetime
object and returns the expiry DateTime
datetime
.
Group Insights
Sometimes an algorithm's performance relies on multiple insights being traded together, like in pairs trading and options straddles. These types insights should be grouped. Insight groups signal to the Execution model that the insights need to be acted on as a single unit to maximize the alpha created.
To mark insights as a group, call the Insight.Group
method.
// Generate and group insights for a set of tickers with a 20-minute prediction horizon; each Insight object provides directional signals, magnitudes, and confidence for price movement. return Insight.Group( Insight.price("IBM", TimeSpan.FromMinutes(20), InsightDirection.UP), Insight.price("SPY", TimeSpan.FromMinutes(20), InsightDirection.UP), Insight.price("AAPL", TimeSpan.FromMinutes(20), InsightDirection.UP) );
# Generate and group insights for a set of tickers with a 20-minute prediction horizon; each Insight object provides directional signals, magnitudes, and confidence for price movement. tickers = ["IBM", "SPY", "AAPL"] insights = [Insight.price(ticker, timedelta(minutes = 20), InsightDirection.UP) for ticker in tickers] return Insight.group(insights)
Cancel Insights
If an Alpha model in your algorithm emits an Insight to enter a position but it determines the trading opportunity has pre-maturely ended, you should cancel the Insight. For example, say you want your algorithm to enter a long position in a security when its Relative Strength Index (RSI) moves below 20 and then liquidate the position when the security's RSI moves above 30. If you emit an Insight that has a duration of 30 days when the RSI moves below 20 but the RSI moves above 30 in 10 days, you should cancel the Insight.
To cancel an Insight, call its Cancel
cancel
/Expire
method with the algorithm's Coordinated Universal Time (UTC).
# Cancel any outstanding insights to ensure only current predictions are considered and to avoid outdated signals influencing trading decisions. self.insight.cancel(algorithm.utc_time)
// Cancel any outstanding insights to ensure only current predictions are considered and to avoid outdated signals influencing trading decisions. _insight.Cancel(algorithm.UtcTime);
If you don't have a reference to the Insight you want to cancel, get it from the InsightManager.
When you cancel an insight, it's CloseTimeUtc
close_time_utc
property is set to one second into the past.
Stop Loss Orders Workaround
In some cases, if you add a Risk Management model that uses stop loss logic, the Risk Management model generates PortfolioTarget
objects with a 0 quantity to make the Execution model liquidate your positions, but then the Portfolio Construction model generates PortfolioTarget
objects with a non-zero quantity to make the Execution model re-enter the same position. This issue can occur if your Portfolio Construction model rebalances and your Alpha model still has an active insight for the liquidated securities. To avoid this issue, add the stop loss order logic to the Alpha model. When the stop loss is hit, emit a flat insight for the security. Note that this is a workaround, but it violates the separation of concerns principle since the Alpha Model shouldn't react to open positions.
Universe Timing Considerations
If the Alpha model manages some indicators or consolidators for securities in the universe and the universe selection runs during the indicator sampling period or the consolidator aggregation period, the indicators and consolidators might be missing some data. For example, take the following scenario:
- The security resolution is minute
- You have a consolidator that aggregates the security data into daily bars to update the indicator
- The universe selection runs at noon
In this scenario, you create and warm-up the indicator at noon. Since it runs at noon, the history request that gathers daily data to warm up the indicator won't contain any data from the current day and the consolidator that updates the indicator also won't aggregate any data from before noon. This process doesn't cause issues if the indicator only uses the close price to calculate the indicator value (like the simple moving average indicator) because the first consolidated bar that updates the indicator will have the correct close price. However, if the indicator uses more than just the close price to calculate its value (like the True Range indicator), the open, high, and low values of the first consolidated bar may be incorrect, causing the initial indicator values to be incorrect.