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 AddUniverse
add_universe
method of the classic approach. The following example initializes a classic algorithm with framework universe selection models:
// Use a hybrid approach by combining Framework Universe Selection Models with classic methods to leverage both the modular flexibility of Framework modules and the control of classic design. This allows for better universe selection, portfolio allocation, and risk management. 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)); }
# Use a hybrid approach by combining Framework Universe Selection Models with classic methods to leverage both the modular flexibility of Framework modules and the control of classic design. This allows for better universe selection, portfolio allocation, and risk management. 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
insights_generated
event handler:
// Add one or multiple Alpha models to a classic algorithm allowing for direct trading based on generated Insight objects, bypassing the need for a Portfolio Construction model. public override void Initialize() { AddAlpha(new EmaCrossAlphaModel()); // The InsightsGenerated event handler is used to receive and process the collection of insights, facilitating flexible and dynamic trading strategies. InsightsGenerated += OnInsightsGenerated; } private void OnInsightsGenerated(IAlgorithm algorithm, GeneratedInsightsCollection insightsCollection) { var insights = insightsCollection.Insights; }
# Add one or multiple Alpha models to a classic algorithm allowing for direct trading based on generated Insight objects, bypassing the need for a Portfolio Construction model. def initialize(self): self.add_alpha(EmaCrossAlphaModel()) # The insights_generated event handler is used to receive and process the collection of insights, facilitating flexible and dynamic trading strategies. 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 OnData
on_data
method, call the EmitInsights
method.
The following example uses a Portfolio Construction Framework model with EmitInsights
method in a classic algorithm:
// Add a Portfolio Construction model to place orders directly without relying on Alpha-generated insights. public override void Initialize() { SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel()); } // Use the EmitInsights method in the OnData method to provide trading signals for executing trades, ensuring balanced investment with the chosen Portfolio Construction model. 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) }); }
# Add a Portfolio Construction model to place orders directly without relying on Alpha-generated insights. def initialize(self): self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel()) # Use the emit_insights method in the on_data method to provide trading signals for executing trades, ensuring balanced investment with the chosen Portfolio Construction model. 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:
// Add Risk Management models to monitor portfolio risk directly, without needing PortfolioTargets from a Portfolio Construction model. These models can independently manage holdings and trigger liquidations as needed. public override void Initialize() { AddRiskManagement(new MaximumDrawdownPercentPerSecurity(0.05m)); AddRiskManagement(new MaximumUnrealizedProfitPercentPerSecurity(0.1m)); }
# Add Risk Management models to monitor portfolio risk directly, without needing PortfolioTargets from a Portfolio Construction model. These models can independently manage holdings and trigger liquidations as needed. 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.
LEAN routes PortfolioTarget objects to the Execute
execute
method of the Execution Model to place orders.
The following example uses a Framework Execution Model in a classic style algorithm:
// Use SetExecution to place orders via the Execution Model, which routes PortfolioTarget objects to its // execute method, instead of placing orders manually. This ensures automated and consistent order execution. public override void Initialize() { // Execute the market order imediately to fill the portfolio targets. SetExecution(new ImmediateExecutionModel()); }
# Use set_execution to place orders via the Execution Model, which routes PortfolioTarget objects to its # execute method, instead of placing orders manually. This ensures automated and consistent order execution. def initialize(self): # Execute the market order imediately to fill the portfolio targets. 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.