577 lines
20 KiB
Markdown
Raw Normal View History

2025-05-28 22:37:53 +08:00
# 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.*