Algorithm Framework

Hybrid Algorithms

Introduction

Classic style algorithms can also use Algorithm Framework modules. This allows you to get the best of both styles of algorithm design, combining easily pluggable modules with the superior control of classic format.

Examples of popular use-cases of a hybrid approach are:

  • Using framework universe selection models as the assets to select.
  • Using portfolio construction models to decide portfolio allocations.
  • Using a risk control model for free portfolio risk monitoring.
  • Passing data between modules freely without concerns of the module interface.

Universe Selection

You can add one or more Framework Universe Selection Models to your algorithm and it will operate normally. You can also combine it with the AddUniverseadd_universe method of the classic approach. The following example initializes a classic algorithm with framework universe selection models:

public override void Initialize()
{
    UniverseSettings.Asynchronous = true;
    SetUniverseSelection(new ManualUniverseSelectionModel(QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA)));
    AddUniverseSelection(new ManualUniverseSelectionModel(QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA)));
    AddUniverse(Universe.DollarVolume.Top(5));
}
def initialize(self):
    self.universe_settings.asynchronous = True       
    self.set_universe_selection(ManualUniverseSelectionModel([ Symbol.create("SPY", SecurityType.EQUITY, Market.USA) ]))
    self.add_universe_selection(ManualUniverseSelectionModel([ Symbol.create("AAPL", SecurityType.EQUITY, Market.USA) ]))
    self.add_universe(self.universe.dollar_volume.top(5))

Alpha

You can add one or multiple Alpha models to your classic algorithm and place the orders using Insight objects without a Portfolio Construction model. To receive the collection of Insight objects in a classic algorithm, implement the InsightsGenerated event handler:

public override void Initialize()
{
    AddAlpha(new EmaCrossAlphaModel());
    InsightsGenerated += OnInsightsGenerated;
}
private void OnInsightsGenerated(IAlgorithm algorithm, GeneratedInsightsCollection insightsCollection)
{
    var insights = insightsCollection.Insights;
}
def initialize(self):
    self.add_alpha(EmaCrossAlphaModel())
    self.insights_generated += self.on_insights_generated

def on_insights_generated(self, algorithm: IAlgorithm, insights_collection: GeneratedInsightsCollection) -> None:
    insights = insights_collection.insights

Portfolio Construction

You can add a Portfolio Construction model to your classic algorithm and have it place orders without returning Insight objects from an Alpha model. To emit insights without an Alpha model, in the OnDataon_data method, call the EmitInsights method.

The following example uses a Portfolio Construction Framework model with EmitInsights method in a classic algorithm:

public override void Initialize()
{
    SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
}

public override void OnData(Slice slice)
{
	EmitInsights(new Insight("GOOG", TimeSpan.FromMinutes(20), InsightType.Price, InsightDirection.Up));
	EmitInsights(new []{
		new Insight("AAPL", TimeSpan.FromMinutes(20), InsightType.Price, InsightDirection.Up),
		new Insight("MSFT", TimeSpan.FromMinutes(20), InsightType.Price, InsightDirection.Up)
	});
}
def initialize(self):
    self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())

def on_data(self, slice):
	self.emit_insights(Insight("GOOG", TimeSpan.from_minutes(20), InsightType.PRICE, InsightDirection.UP))
	self.emit_insights([
		Insight("AAPL", TimeSpan.from_minutes(20), InsightType.PRICE, InsightDirection.UP),
		Insight("MSFT", TimeSpan.from_minutes(20), InsightType.PRICE, InsightDirection.UP)
	])

Risk Management

Some Risk Management Models don't require a Portfolio Construction model to provide PortfolioTarget objects, allowing them to directly monitor the portfolio holdings and liquidate positions when neccessary. To see which pre-built Risk Management models don't need the Portfolio Construction model to provide PortfolioTarget objects, see Supported Models.

You can add one or more Risk Management Models to your algorithm and it will operate normally. The following example initializes a classic algorithm with framework risk management models:

public override void Initialize()
{
    AddRiskManagement(new MaximumDrawdownPercentPerSecurity(0.05m));
    AddRiskManagement(new MaximumUnrealizedProfitPercentPerSecurity(0.1m));
}
def initialize(self):
    self.add_risk_management(MaximumDrawdownPercentPerSecurity(0.05))
    self.add_risk_management(MaximumUnrealizedProfitPercentPerSecurity(0.1))

Execution

Execution models can place orders for your strategy instead of placing them manually. To do so, PortfolioTargets are routed to the Execute method of the Execution Model to place orders.

The following example uses a Framework Execution Model in a classic style algorithm:

public override void Initialize()
{
    SetExecution(new ImmediateExecutionModel());
}
def initialize(self):
    self.set_execution(ImmediateExecutionModel())

Universe Timing Considerations

If the Alpha, Portfolio Construction, Risk Management, or Execution 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: