Problem Description
Solution
To ensure your algorithm places trades immediately after a signal is generated in QuantConnect, you must address three specific areas: Data Resolution, Order Type, and (if using the Algorithm Framework) the Execution Model.
Key Concepts for Immediate Execution
-
Data Resolution:
- If you use
Resolution.Daily,OnDatatriggers after the market closes (e.g., 16:00 or 00:00). AMarketOrderplaced then will fill at the next day's open. - Solution: Use
Resolution.MinuteorResolution.Second. This allowsOnDatato trigger during market hours, enabling immediate execution.
- If you use
-
Scheduled Events (For Daily Strategies):
- If you require Daily data logic but want to trade before the day closes (effectively "immediately" on the current bar), do not wait for
OnData. - Solution: Use
self.schedule.onwithtime_rules.before_market_close.
- If you require Daily data logic but want to trade before the day closes (effectively "immediately" on the current bar), do not wait for
-
Algorithm Framework:
- If using the framework (Alphas), the default execution model might batch orders.
- Solution: Use
self.set_execution(ImmediateExecutionModel()).
Implementation Examples
Below are two examples. Example 1 is for standard algorithms using Resolution.Minute for instant reaction. Example 2 is for the Algorithm Framework ensuring immediate execution.
Example 1: Standard Algorithm (Minute Resolution)
This strategy uses Minute resolution data. As soon as the RSI crosses a threshold inside on_data, a Market Order is placed immediately.
# region imports
from AlgorithmImports import *
# endregion
class ImmediateExecutionAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 6, 1)
self.set_cash(100000)
# 1. Set Resolution to MINUTE (or SECOND) for immediate intraday execution.
# If this were DAILY, the trade would not happen until the next day.
self.symbol = self.add_equity("SPY", Resolution.MINUTE).symbol
# Indicators
self.rsi = self.RSI(self.symbol, 14, MovingAverageType.WILDERS, Resolution.MINUTE)
# Warm up indicator
self.set_warm_up(14)
def on_data(self, data: Slice):
if self.is_warming_up or not self.rsi.is_ready:
return
# 2. Logic triggers during the day
if not self.portfolio.invested:
if self.rsi.current.value < 30:
# 3. MarketOrder executes immediately at current market price
self.market_order(self.symbol, 100)
self.debug(f"Bought immediately at {self.time}")
elif self.portfolio.invested:
if self.rsi.current.value > 70:
self.liquidate(self.symbol)
self.debug(f"Sold immediately at {self.time}")
Example 2: Algorithm Framework (Immediate Execution Model)
If you are using the AddAlpha or SetAlpha structure, you must explicitly set the Execution Model to ImmediateExecutionModel. Without this, some models may delay trading to reduce transaction costs or impact.
# region imports
from AlgorithmImports import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Selection import *
# endregion
class FrameworkImmediateExecution(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 6, 1)
self.set_cash(100000)
# Universe Selection
self.set_universe_selection(ManualUniverseSelectionModel([Symbol.create("SPY", SecurityType.EQUITY, Market.USA)]))
# Alpha Model: Generates signals (Insights)
self.set_alpha(RsiAlphaModel())
# Portfolio Construction: Determines target quantities
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
# CRITICAL STEP: Set Execution to Immediate
# This ensures that as soon as the Alpha generates an Insight,
# and Portfolio Construction creates a target, the order is sent.
self.set_execution(ImmediateExecutionModel())
class RsiAlphaModel(AlphaModel):
def __init__(self, period=14, resolution=Resolution.MINUTE):
self.period = period
self.resolution = resolution
self.rsi_dictionary = {}
def update(self, algorithm, data):
insights = []
for symbol, rsi in self.rsi_dictionary.items():
if rsi.is_ready:
if rsi.current.value < 30:
insights.append(Insight.price(symbol, timedelta(minutes=20), InsightDirection.UP))
elif rsi.current.value > 70:
insights.append(Insight.price(symbol, timedelta(minutes=20), InsightDirection.DOWN))
return insights
def on_securities_changed(self, algorithm, changes):
for security in changes.added_securities:
self.rsi_dictionary[security.symbol] = algorithm.RSI(security.symbol, self.period, MovingAverageType.WILDERS, self.resolution)
for security in changes.removed_securities:
self.rsi_dictionary.pop(security.symbol, None)
Summary of Best Practices
- Use
Resolution.Minute: This is the most reliable way to ensure logic runs and trades execute during market hours. - Avoid
LimitOrderfor immediacy: Limit orders may sit in the order book waiting for a price match. UseMarketOrderfor guaranteed immediate execution (though potentially at a slightly worse price due to slippage). - Check
IsWarmingUp: Ensure your algorithm doesn't try to trade before indicators are ready, which can cause errors, but once ready, the trade logic should be unhindered.
Q&A
Q: Why does my Daily Resolution algorithm trade the next day?
A: In QuantConnect, OnData for Resolution.Daily is triggered after the bar closes (e.g., 4:00 PM ET). Since the market is closed, any Market Order placed then will be queued and filled at the Market Open of the next trading day.
Q: How can I trade the "Close" of the current day?
A: Use self.schedule.on(self.date_rules.every_day(), self.time_rules.before_market_close("SPY", 1), self.my_rebalance_method). This runs your logic 1 minute before the market closes, allowing you to fill orders at prices very close to the daily closing price.
Q: Does ImmediateExecutionModel work with Limit Orders?
A: No. The ImmediateExecutionModel specifically uses Market Orders to ensure the portfolio targets are met instantly. If you need Limit Orders, you must write a custom Execution Model or use the StandardDeviationExecutionModel.