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 method, call the AddAlpha method.

AddAlpha(new EmaCrossAlphaModel());
self.AddAlpha(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.

AddAlpha(new RsiAlphaModel());
AddAlpha(new EmaCrossAlphaModel());
self.AddAlpha(RsiAlphaModel())
self.AddAlpha(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 method, which receives a Slice object and returns an array of Insight objects. Extensions should also implement the OnSecuritiesChanged 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 OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        # Security additions and removals are pushed here.
        # This can be used for setting up algorithm state.
        # changes.AddedSecurities
        # changes.RemovedSecurities
        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.

The 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.

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 event. In the OnSecuritiesChanged 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.

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 = SMA(security.Symbol, 20);
            algorithm.WarmUpIndicator(security.Symbol, dynamicSecurity.Sma);
            _securities.Add(security);
        }

        foreach (var security in changes.RemovedSecurities)
        {
            if (_securities.Contains(security.Symbol))
            {
                algorithm.DeregisterIndicator((security as dynamic).Sma);
                _securities.Remove(security.Symbol);
            }
        }
    }
}
class MyAlphaModel(AlphaModel):
    securities = []

    def OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        for security in changes.AddedSecurities:
            security.indicator = algorithm.SMA(security.Symbol, 20)
            algorithm.WarmUpIndicator(security.Symbol, security.indicator)
            self.securities.append(security)

        for security in changes.RemovedSecurities:
            if security.Symbol in self.securities:
                algorithm.DeregisterIndicator(security.indicator)
                self.securities.remove(security)

Insights

An Insight is a single prediction for an asset. The 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, sourceModel=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 timedeltaTimeSpan object or a function that receives a DateTimedatetime object and returns the expiry DateTimedatetime. 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, sourceModel=None, weight=None, tag='') 
insight = Insight.Price("IBM", timedelta(minutes = 20), InsightDirection.Up)

# Insight.Price(symbol, resolution, barCount, direction, magnitude=None, confidence=None, sourceModel=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 method, the period argument can be a timedeltaTimeSpan object, a DateTimedatetime object, or a function that receives a DateTimedatetime object and returns the expiry DateTimedatetime.

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.

return Insight.Group(insight1, insight2, insight3);
return Insight.Group([insight1, insight2,insight3])

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/Expire method with the algorithm's Coordinated Universal Time (UTC).

self.insight.Cancel(algorithm.UtcTime)
_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 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.

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: