# Question About Best Practices for Batch Rotation and Indicator Management
Hello,
I'm working on an intraday trading strategy on QuantConnect that uses a **Batch Rotation System** to efficiently manage a large number of stocks. I've encountered an issue related to the timing of unsubscribing from stocks and indicator readiness, and I need to understand the best practices to solve this problem.
---
## Current Strategy Description
### System Architecture
The strategy uses a **Two-Phase System**:
1. **Phase 1**: Lightweight indicators for initial screening
- Number of indicators: 3 (RDV, CMF, OBV)
- Purpose: Quick scan to detect early accumulation signals
- Number of stocks: 100 stocks per batch
2. **Phase 2**: Full indicators for detailed analysis
- Number of indicators: 6 (RDV, CMF, OBV, VWAP, BOP, Volume SMA)
- Purpose: Detailed analysis of promising stocks
- Promotion: Stocks showing strong signals are promoted from Phase 1 to Phase 2
### Batch Rotation System
```python
# Current settings
self.BATCH_SIZE = 100 # Number of stocks per batch
self.ROTATION_INTERVAL_MINUTES = 5 # Rotate every 5 minutes
self.PROMOTION_THRESHOLD = 2 # Number of indicators required for promotion
self.PROMOTION_BARS = 3 # Number of consecutive bars required
```
**How it works:**
1. Strategy starts with a batch of 100 stocks
2. Creates Phase 1 indicators for each stock (RDV, CMF, OBV)
3. Uses `warm_up_indicator()` to warm up the indicators
4. Every 5 minutes, it:
- Unsubscribes from the current batch
- Loads a new batch of 100 stocks
- Keeps promoted stocks (from Phase 1 to Phase 2)
---
## The Problem Discovered
When reviewing the log messages, I noticed the following sequence:
### Time: 21:50 - Indicator Creation
```
[IndicatorCreation] MPB | RDV created (period=2), CMF created (period=3), OBV created | WarmUp completed
[IndicatorCreation] KFRC | RDV created (period=2), CMF created (period=3), OBV created | WarmUp completed
```
### Time: 21:51-22:00 - Indicators Still Not Ready
```
[IndicatorReady-Diag] MPB | InDict: RDV=True CMF=True OBV=True |
Ready: RDV=False CMF=False OBV=False |
Samples: RDV=0 CMF=0 OBV=0
[IndicatorReady-Diag] LEGH | InDict: RDV=True CMF=True OBV=True |
Ready: RDV=False CMF=False OBV=False |
Samples: RDV=0 CMF=0 OBV=0
```
### Time: 22:00 - Unsubscribe (After Only 5 Minutes!)
```
[DIAG] rotate_batch START
[Extended-Hours-Unsub] KFRC | Removed extended hours subscription
[Extended-Hours-Unsub] LPG | Removed extended hours subscription
[Extended-Hours-Unsub] HVT | Removed extended hours subscription
[Extended-Hours-Unsub] IGIC | Removed extended hours subscription
```
**Key Observation:**
- Indicators are created and `warm_up_indicator()` is called
- Despite this, indicators show `Samples: 0` even after several minutes
- Stocks are unsubscribed before indicators receive any data
- Result: We get no signals from these stocks
---
## Current Code
### Indicator Creation Function
```python
def create_phase1_indicators(self, symbol):
"""Create Phase 1 indicators (3 lightweight indicators)"""
try:
self.phase1_rdv_indicators[symbol] = self.rdv(symbol, self.RDV_PERIOD, Resolution.MINUTE)
self.phase1_cmf_indicators[symbol] = self.cmf(symbol, self.CMF_PERIOD, Resolution.MINUTE)
self.phase1_obv_indicators[symbol] = self.obv(symbol, Resolution.MINUTE)
# Warm up indicators
self.warm_up_indicator(symbol, self.phase1_rdv_indicators[symbol], Resolution.MINUTE)
self.warm_up_indicator(symbol, self.phase1_cmf_indicators[symbol], Resolution.MINUTE)
self.warm_up_indicator(symbol, self.phase1_obv_indicators[symbol], Resolution.MINUTE)
if symbol not in self._indicator_creation_logged:
self._indicator_creation_logged.add(symbol)
self.debug(f"[IndicatorCreation] {symbol.value} | " +
f"RDV created (period={self.RDV_PERIOD}), " +
f"CMF created (period={self.CMF_PERIOD}), " +
f"OBV created | WarmUp completed")
except Exception as e:
self.error(f"[ERROR] Error in create_phase1_indicators for {symbol.value}: {str(e)}")
```
### Readiness Check Function
```python
def are_indicators_ready(self, symbol):
"""Check if all indicators are ready"""
try:
in_rdv = symbol in self.phase1_rdv_indicators
in_cmf = symbol in self.phase1_cmf_indicators
in_obv = symbol in self.phase1_obv_indicators
rdv_ready = self.phase1_rdv_indicators[symbol].is_ready if in_rdv else False
cmf_ready = self.phase1_cmf_indicators[symbol].is_ready if in_cmf else False
obv_ready = self.phase1_obv_indicators[symbol].is_ready if in_obv else False
# Diagnostic messages
if self._phase1_log_counter.get(symbol, 0) % 100 == 1:
rdv_samples = self.phase1_rdv_indicators[symbol].samples if in_rdv else 0
cmf_samples = self.phase1_cmf_indicators[symbol].samples if in_cmf else 0
obv_samples = self.phase1_obv_indicators[symbol].samples if in_obv else 0
self.debug(f"[IndicatorReady-Diag] {symbol.value} | " +
f"InDict: RDV={in_rdv} CMF={in_cmf} OBV={in_obv} | " +
f"Ready: RDV={rdv_ready} CMF={cmf_ready} OBV={obv_ready} | " +
f"Samples: RDV={rdv_samples} CMF={cmf_samples} OBV={obv_samples}")
return all([in_rdv and rdv_ready, in_cmf and cmf_ready, in_obv and obv_ready])
except Exception as e:
return False
```
### Rotation Function
```python
def rotate_batch(self):
"""Rotate the current batch"""
try:
self.debug("[DIAG] rotate_batch START")
# Save promoted stocks
promoted_symbols = list(self.promoted_symbols)
# Remove old batch
old_batch_symbols = [s for s in self.active_symbols if s not in promoted_symbols]
for symbol in old_batch_symbols:
self.cleanup_phase1_indicators(symbol)
try:
if self.securities.contains_key(symbol):
self.remove_security(symbol)
self.debug(f"[Extended-Hours-Unsub] {symbol.value} | Removed extended hours subscription")
except Exception as unsub_ex:
self.error(f"[ERROR] Failed to unsubscribe from {symbol.value}: {str(unsub_ex)}")
# Load new batch
start_idx = self.current_batch_index * self.BATCH_SIZE
end_idx = min(start_idx + self.BATCH_SIZE, len(self.universe_stocks))
new_batch = self.universe_stocks[start_idx:end_idx]
# Add new stocks
self.active_symbols = promoted_symbols + new_batch
# Create indicators for new stocks
for symbol in new_batch:
if symbol not in self.phase1_rdv_indicators:
self.add_equity(symbol, Resolution.MINUTE, extendedMarketHours=True)
self.create_phase1_indicators(symbol)
# Update batch
self.current_batch_index = (self.current_batch_index + 1) % self.total_batches
self.rotation_count += 1
self.debug("[DIAG] rotate_batch completed successfully")
except Exception as e:
self.error(f"[ERROR] Error in rotate_batch: {str(e)}")
```
---
## Questions
### 1. Why aren't indicators receiving data despite using `warm_up_indicator()`?
- Does `warm_up_indicator()` only work with historical data?
- Does a stock need additional time after `add_equity()` before indicators start receiving data?
- Is there a delay between indicator creation and receiving first live data?
### 2. What are the best practices for managing indicator lifecycle in a batch rotation system?
- How do I ensure indicators are ready before unsubscribing from a stock?
- What is the appropriate time period between indicator creation and unsubscription?
- Should I wait until `is_ready == True` before rotating?
### 3. How should I handle low-liquidity stocks?
- Some stocks don't trade every minute, how do I ensure indicators get enough data?
- Should I use a lower resolution (like 5 minutes) for low-liquidity stocks?
- How do I distinguish between a low-liquidity stock and a stock whose data hasn't loaded yet?
### 4. What is the best way to implement a batch rotation system with indicators?
- Should I keep stocks whose indicators haven't completed when rotating?
- How do I balance between covering more stocks and signal quality?
- Is there a way to speed up the indicator warm-up process for live data?
### 5. Is there a better alternative to `warm_up_indicator()` in the current context?
- Should I use `set_warm_up()` at the algorithm level instead of individual indicators?
- Can I manually load historical data and update indicators with it before starting?
- What is the most efficient way to ensure indicator readiness as quickly as possible?
---
## Additional Information
- **Strategy Type**: Intraday trading
- **Universe Size**: 200 stocks
- **Batch Size**: 100 stocks
- **Rotation Period**: 5 minutes
- **Resolution**: Minute
- **Extended Market Hours**: Enabled
- **Indicators Used**:
- RDV (period=2)
- CMF (period=3)
- OBV
- VWAP (period=5)
- BOP
- Volume SMA (period=10)
---
## What I'm Looking For
I need to understand the **best practices** in QuantConnect for:
1. Ensuring indicator readiness before making trading decisions
2. Managing indicator lifecycle in a batch rotation system
3. Handling low-liquidity stocks
4. Optimizing the timing of adding and removing stocks from the strategy
5. Achieving a balance between stock coverage and signal quality
Thank you for your help!
Mia Alissi
Here are actionable QuantConnect best practices for robust batch indicator warm-up and management in your scenario:
1. Ensuring Indicator Readiness Before Trading
.is_readybefore using an indicator value.QCAlgorithm.warm_up_indicator()works via history requests once the symbol is added withadd_equity(). However, it may not finish if data is missing or your batch is too large for provider quotas.is_ready/samplescount as you do now for clear diagnostics.2. Managing Indicator Lifecycle in Batch Rotations
Pseudocode pattern:
Detach and forget:
3. Handling Low-Liquidity Stocks
dollar_volume) to avoid non-trading stocks.Resolution.FiveMinuteorResolution.Hour) to increase warm-up success.4. Batch Rotation and Warmup Optimization
5. Warm-up APIs: Comparison and Usage
warm_up_indicator(symbol, indicator, resolution): automatic and convenient, but waits on historical data availability; can silently fail if data is sparse.set_warmup(TimeSpan, resolution): global for the algorithm; all algorithm subscriptions (not just indicators) get warmed up.Example: Robust Batch Rotation with Warmup/Readiness Check
Diagnostics for Slow/Failed Warmups
Summary:
Let me know if you want annotated code for any specific batch rotation or warm-up logic!
Sources: Warm Up Periods - QuantConnect.com, Automatic Indicators - QuantConnect.com, Indicator Warmup Algorithm, Key Concepts - QuantConnect.com, Warmup Algorithm
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
مطلق الشمري
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!