# Trend Indicators ## Overview Trend indicators help identify the direction and strength of market trends. IncrementalTrader provides Supertrend implementations that combine price action with volatility to generate clear trend signals. ## SupertrendState Individual Supertrend indicator that tracks trend direction and provides support/resistance levels. ### Features - **Trend Direction**: Clear bullish/bearish trend identification - **Dynamic Support/Resistance**: Adaptive levels based on volatility - **ATR-Based**: Uses Average True Range for volatility adjustment - **Real-time Updates**: Incremental calculation for live trading ### Mathematical Formula ``` Basic Upper Band = (High + Low) / 2 + (Multiplier × ATR) Basic Lower Band = (High + Low) / 2 - (Multiplier × ATR) Final Upper Band = Basic Upper Band < Previous Final Upper Band OR Previous Close > Previous Final Upper Band ? Basic Upper Band : Previous Final Upper Band Final Lower Band = Basic Lower Band > Previous Final Lower Band OR Previous Close < Previous Final Lower Band ? Basic Lower Band : Previous Final Lower Band Supertrend = Close <= Final Lower Band ? Final Lower Band : Final Upper Band Trend = Close <= Final Lower Band ? DOWN : UP ``` ### Class Definition ```python from IncrementalTrader.strategies.indicators import SupertrendState class SupertrendState(OHLCIndicatorState): def __init__(self, period: int, multiplier: float): super().__init__(period) self.multiplier = multiplier self.atr = SimpleATRState(period) # Supertrend state self.supertrend_value = 0.0 self.trend = 1 # 1 for up, -1 for down self.final_upper_band = 0.0 self.final_lower_band = 0.0 self.previous_close = 0.0 def _process_ohlc_data(self, high: float, low: float, close: float): # Update ATR self.atr.update_ohlc(high, low, close) if not self.atr.is_ready(): return # Calculate basic bands hl2 = (high + low) / 2.0 atr_value = self.atr.get_value() basic_upper_band = hl2 + (self.multiplier * atr_value) basic_lower_band = hl2 - (self.multiplier * atr_value) # Calculate final bands if self.data_count == 1: self.final_upper_band = basic_upper_band self.final_lower_band = basic_lower_band else: # Final upper band logic if basic_upper_band < self.final_upper_band or self.previous_close > self.final_upper_band: self.final_upper_band = basic_upper_band # Final lower band logic if basic_lower_band > self.final_lower_band or self.previous_close < self.final_lower_band: self.final_lower_band = basic_lower_band # Determine trend and supertrend value if close <= self.final_lower_band: self.trend = -1 # Downtrend self.supertrend_value = self.final_lower_band else: self.trend = 1 # Uptrend self.supertrend_value = self.final_upper_band self.previous_close = close def get_value(self) -> float: return self.supertrend_value def get_trend(self) -> int: """Get current trend direction: 1 for up, -1 for down.""" return self.trend def is_bullish(self) -> bool: """Check if current trend is bullish.""" return self.trend == 1 def is_bearish(self) -> bool: """Check if current trend is bearish.""" return self.trend == -1 ``` ### Usage Examples #### Basic Supertrend Usage ```python # Create Supertrend with 10-period ATR and 3.0 multiplier supertrend = SupertrendState(period=10, multiplier=3.0) # OHLC data: (high, low, close) ohlc_data = [ (105.0, 102.0, 104.0), (106.0, 103.0, 105.5), (107.0, 104.0, 106.0), (108.0, 105.0, 107.5) ] for high, low, close in ohlc_data: supertrend.update_ohlc(high, low, close) if supertrend.is_ready(): trend_direction = "BULLISH" if supertrend.is_bullish() else "BEARISH" print(f"Supertrend: {supertrend.get_value():.2f}, Trend: {trend_direction}") ``` #### Trend Change Detection ```python class SupertrendSignals: def __init__(self, period: int = 10, multiplier: float = 3.0): self.supertrend = SupertrendState(period, multiplier) self.previous_trend = None def update(self, high: float, low: float, close: float): self.supertrend.update_ohlc(high, low, close) def get_signal(self) -> str: if not self.supertrend.is_ready(): return "HOLD" current_trend = self.supertrend.get_trend() # Check for trend change if self.previous_trend is not None and self.previous_trend != current_trend: if current_trend == 1: signal = "BUY" # Trend changed to bullish else: signal = "SELL" # Trend changed to bearish else: signal = "HOLD" self.previous_trend = current_trend return signal def get_support_resistance(self) -> float: """Get current support/resistance level.""" return self.supertrend.get_value() # Usage signals = SupertrendSignals(period=10, multiplier=3.0) for high, low, close in ohlc_data: signals.update(high, low, close) signal = signals.get_signal() support_resistance = signals.get_support_resistance() if signal != "HOLD": print(f"Signal: {signal} at {close:.2f}, S/R: {support_resistance:.2f}") ``` ### Performance Characteristics - **Time Complexity**: O(1) per update - **Space Complexity**: O(ATR_period) - **Memory Usage**: ~8 bytes per ATR period + constant overhead ## SupertrendCollection Collection of multiple Supertrend indicators for meta-trend analysis. ### Features - **Multiple Timeframes**: Combines different Supertrend configurations - **Consensus Signals**: Requires agreement among multiple indicators - **Trend Strength**: Measures trend strength through consensus - **Flexible Configuration**: Customizable periods and multipliers ### Class Definition ```python class SupertrendCollection: def __init__(self, configs: list): """ Initialize with list of (period, multiplier) tuples. Example: [(10, 3.0), (14, 2.0), (21, 1.5)] """ self.supertrendss = [] for period, multiplier in configs: self.supertrendss.append(SupertrendState(period, multiplier)) self.configs = configs def update_ohlc(self, high: float, low: float, close: float): """Update all Supertrend indicators.""" for st in self.supertrendss: st.update_ohlc(high, low, close) def is_ready(self) -> bool: """Check if all indicators are ready.""" return all(st.is_ready() for st in self.supertrendss) def get_consensus_trend(self) -> int: """Get consensus trend: 1 for bullish, -1 for bearish, 0 for mixed.""" if not self.is_ready(): return 0 trends = [st.get_trend() for st in self.supertrendss] bullish_count = sum(1 for trend in trends if trend == 1) bearish_count = sum(1 for trend in trends if trend == -1) if bullish_count > bearish_count: return 1 elif bearish_count > bullish_count: return -1 else: return 0 def get_trend_strength(self) -> float: """Get trend strength as percentage of indicators agreeing.""" if not self.is_ready(): return 0.0 consensus_trend = self.get_consensus_trend() if consensus_trend == 0: return 0.0 trends = [st.get_trend() for st in self.supertrendss] agreeing_count = sum(1 for trend in trends if trend == consensus_trend) return agreeing_count / len(trends) def get_supertrend_values(self) -> list: """Get all Supertrend values.""" return [st.get_value() for st in self.supertrendss if st.is_ready()] def get_average_supertrend(self) -> float: """Get average Supertrend value.""" values = self.get_supertrend_values() return sum(values) / len(values) if values else 0.0 ``` ### Usage Examples #### Multi-Timeframe Trend Analysis ```python # Create collection with different configurations configs = [ (10, 3.0), # Fast Supertrend (14, 2.5), # Medium Supertrend (21, 2.0) # Slow Supertrend ] supertrend_collection = SupertrendCollection(configs) for high, low, close in ohlc_data: supertrend_collection.update_ohlc(high, low, close) if supertrend_collection.is_ready(): consensus = supertrend_collection.get_consensus_trend() strength = supertrend_collection.get_trend_strength() avg_supertrend = supertrend_collection.get_average_supertrend() trend_name = {1: "BULLISH", -1: "BEARISH", 0: "MIXED"}[consensus] print(f"Consensus: {trend_name}, Strength: {strength:.1%}, Avg S/R: {avg_supertrend:.2f}") ``` #### Meta-Trend Strategy ```python class MetaTrendStrategy: def __init__(self): # Multiple Supertrend configurations self.supertrend_collection = SupertrendCollection([ (10, 3.0), # Fast (14, 2.5), # Medium (21, 2.0), # Slow (28, 1.5) # Very slow ]) self.previous_consensus = None def update(self, high: float, low: float, close: float): self.supertrend_collection.update_ohlc(high, low, close) def get_meta_signal(self) -> dict: if not self.supertrend_collection.is_ready(): return {"signal": "HOLD", "confidence": 0.0, "strength": 0.0} current_consensus = self.supertrend_collection.get_consensus_trend() strength = self.supertrend_collection.get_trend_strength() # Check for consensus change signal = "HOLD" if self.previous_consensus is not None and self.previous_consensus != current_consensus: if current_consensus == 1: signal = "BUY" elif current_consensus == -1: signal = "SELL" # Calculate confidence based on strength and consensus confidence = strength if current_consensus != 0 else 0.0 self.previous_consensus = current_consensus return { "signal": signal, "confidence": confidence, "strength": strength, "consensus": current_consensus, "avg_supertrend": self.supertrend_collection.get_average_supertrend() } # Usage meta_strategy = MetaTrendStrategy() for high, low, close in ohlc_data: meta_strategy.update(high, low, close) result = meta_strategy.get_meta_signal() if result["signal"] != "HOLD": print(f"Meta Signal: {result['signal']}, Confidence: {result['confidence']:.1%}") ``` ### Performance Characteristics - **Time Complexity**: O(n) per update (where n is number of Supertrends) - **Space Complexity**: O(sum of all ATR periods) - **Memory Usage**: Scales with number of indicators ## Advanced Usage Patterns ### Adaptive Supertrend ```python class AdaptiveSupertrend: def __init__(self, base_period: int = 14, base_multiplier: float = 2.0): self.base_period = base_period self.base_multiplier = base_multiplier # Volatility measurement for adaptation self.atr_short = SimpleATRState(period=5) self.atr_long = SimpleATRState(period=20) # Current adaptive Supertrend self.current_supertrend = SupertrendState(base_period, base_multiplier) # Adaptation parameters self.min_multiplier = 1.0 self.max_multiplier = 4.0 def update_ohlc(self, high: float, low: float, close: float): # Update volatility measurements self.atr_short.update_ohlc(high, low, close) self.atr_long.update_ohlc(high, low, close) # Calculate adaptive multiplier if self.atr_long.is_ready() and self.atr_short.is_ready(): volatility_ratio = self.atr_short.get_value() / self.atr_long.get_value() # Adjust multiplier based on volatility adaptive_multiplier = self.base_multiplier * volatility_ratio adaptive_multiplier = max(self.min_multiplier, min(self.max_multiplier, adaptive_multiplier)) # Update Supertrend if multiplier changed significantly if abs(adaptive_multiplier - self.current_supertrend.multiplier) > 0.1: self.current_supertrend = SupertrendState(self.base_period, adaptive_multiplier) # Update current Supertrend self.current_supertrend.update_ohlc(high, low, close) def get_value(self) -> float: return self.current_supertrend.get_value() def get_trend(self) -> int: return self.current_supertrend.get_trend() def is_ready(self) -> bool: return self.current_supertrend.is_ready() def get_current_multiplier(self) -> float: return self.current_supertrend.multiplier # Usage adaptive_st = AdaptiveSupertrend(base_period=14, base_multiplier=2.0) for high, low, close in ohlc_data: adaptive_st.update_ohlc(high, low, close) if adaptive_st.is_ready(): trend = "BULLISH" if adaptive_st.get_trend() == 1 else "BEARISH" multiplier = adaptive_st.get_current_multiplier() print(f"Adaptive Supertrend: {adaptive_st.get_value():.2f}, " f"Trend: {trend}, Multiplier: {multiplier:.2f}") ``` ### Supertrend with Stop Loss Management ```python class SupertrendStopLoss: def __init__(self, period: int = 14, multiplier: float = 2.0, buffer_percent: float = 0.5): self.supertrend = SupertrendState(period, multiplier) self.buffer_percent = buffer_percent / 100.0 self.current_position = None # "LONG", "SHORT", or None self.entry_price = 0.0 self.stop_loss = 0.0 def update(self, high: float, low: float, close: float): previous_trend = self.supertrend.get_trend() if self.supertrend.is_ready() else None self.supertrend.update_ohlc(high, low, close) if not self.supertrend.is_ready(): return current_trend = self.supertrend.get_trend() supertrend_value = self.supertrend.get_value() # Check for trend change (entry signal) if previous_trend is not None and previous_trend != current_trend: if current_trend == 1: # Bullish trend self.enter_long(close, supertrend_value) else: # Bearish trend self.enter_short(close, supertrend_value) # Update stop loss for existing position if self.current_position: self.update_stop_loss(supertrend_value) def enter_long(self, price: float, supertrend_value: float): self.current_position = "LONG" self.entry_price = price self.stop_loss = supertrend_value * (1 - self.buffer_percent) print(f"LONG entry at {price:.2f}, Stop: {self.stop_loss:.2f}") def enter_short(self, price: float, supertrend_value: float): self.current_position = "SHORT" self.entry_price = price self.stop_loss = supertrend_value * (1 + self.buffer_percent) print(f"SHORT entry at {price:.2f}, Stop: {self.stop_loss:.2f}") def update_stop_loss(self, supertrend_value: float): if self.current_position == "LONG": new_stop = supertrend_value * (1 - self.buffer_percent) if new_stop > self.stop_loss: # Only move stop up self.stop_loss = new_stop elif self.current_position == "SHORT": new_stop = supertrend_value * (1 + self.buffer_percent) if new_stop < self.stop_loss: # Only move stop down self.stop_loss = new_stop def check_stop_loss(self, current_price: float) -> bool: """Check if stop loss is hit.""" if not self.current_position: return False if self.current_position == "LONG" and current_price <= self.stop_loss: print(f"LONG stop loss hit at {current_price:.2f}") self.current_position = None return True elif self.current_position == "SHORT" and current_price >= self.stop_loss: print(f"SHORT stop loss hit at {current_price:.2f}") self.current_position = None return True return False # Usage st_stop_loss = SupertrendStopLoss(period=14, multiplier=2.0, buffer_percent=0.5) for high, low, close in ohlc_data: st_stop_loss.update(high, low, close) # Check stop loss on each update if st_stop_loss.check_stop_loss(close): print("Position closed due to stop loss") ``` ## Integration with Strategies ### Supertrend Strategy Example ```python class SupertrendStrategy(IncStrategyBase): def __init__(self, name: str, params: dict = None): super().__init__(name, params) # Initialize Supertrend collection configs = self.params.get('supertrend_configs', [(10, 3.0), (14, 2.5), (21, 2.0)]) self.supertrend_collection = SupertrendCollection(configs) # Strategy parameters self.min_strength = self.params.get('min_strength', 0.75) self.previous_consensus = None def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal: open_price, high, low, close, volume = ohlcv # Update Supertrend collection self.supertrend_collection.update_ohlc(high, low, close) # Wait for indicators to be ready if not self.supertrend_collection.is_ready(): return IncStrategySignal.HOLD() # Get consensus and strength current_consensus = self.supertrend_collection.get_consensus_trend() strength = self.supertrend_collection.get_trend_strength() # Check for strong consensus change if (self.previous_consensus is not None and self.previous_consensus != current_consensus and strength >= self.min_strength): if current_consensus == 1: # Strong bullish consensus return IncStrategySignal.BUY( confidence=strength, metadata={ 'consensus': current_consensus, 'strength': strength, 'avg_supertrend': self.supertrend_collection.get_average_supertrend() } ) elif current_consensus == -1: # Strong bearish consensus return IncStrategySignal.SELL( confidence=strength, metadata={ 'consensus': current_consensus, 'strength': strength, 'avg_supertrend': self.supertrend_collection.get_average_supertrend() } ) self.previous_consensus = current_consensus return IncStrategySignal.HOLD() ``` ## Performance Optimization Tips ### 1. Choose Appropriate Configurations ```python # For fast signals (more noise) fast_configs = [(7, 3.0), (10, 2.5)] # For balanced signals balanced_configs = [(10, 3.0), (14, 2.5), (21, 2.0)] # For slow, reliable signals slow_configs = [(14, 2.0), (21, 1.5), (28, 1.0)] ``` ### 2. Optimize Memory Usage ```python # Use SimpleATRState for memory efficiency class MemoryEfficientSupertrend(SupertrendState): def __init__(self, period: int, multiplier: float): super().__init__(period, multiplier) # Replace ATRState with SimpleATRState self.atr = SimpleATRState(period) ``` ### 3. Batch Processing ```python def update_multiple_supertrends(supertrends: list, high: float, low: float, close: float): """Efficiently update multiple Supertrend indicators.""" for st in supertrends: st.update_ohlc(high, low, close) return [(st.get_value(), st.get_trend()) for st in supertrends if st.is_ready()] ``` --- *Supertrend indicators provide clear trend direction and dynamic support/resistance levels. Use single Supertrend for simple trend following or SupertrendCollection for robust meta-trend analysis.*