546 lines
18 KiB
Markdown
546 lines
18 KiB
Markdown
|
|
# Volatility Indicators
|
|||
|
|
|
|||
|
|
## Overview
|
|||
|
|
|
|||
|
|
Volatility indicators measure the rate of price change and market uncertainty. IncrementalTrader provides Average True Range (ATR) implementations that help assess market volatility and set appropriate stop-loss levels.
|
|||
|
|
|
|||
|
|
## ATRState (Average True Range)
|
|||
|
|
|
|||
|
|
Full ATR implementation that maintains a moving average of True Range values.
|
|||
|
|
|
|||
|
|
### Features
|
|||
|
|
- **True Range Calculation**: Accounts for gaps between trading sessions
|
|||
|
|
- **Volatility Measurement**: Provides absolute volatility measurement
|
|||
|
|
- **Stop-Loss Guidance**: Helps set dynamic stop-loss levels
|
|||
|
|
- **Trend Strength**: Indicates trend strength through volatility
|
|||
|
|
|
|||
|
|
### Mathematical Formula
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
True Range = max(
|
|||
|
|
High - Low,
|
|||
|
|
|High - Previous_Close|,
|
|||
|
|
|Low - Previous_Close|
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
ATR = Moving_Average(True_Range, period)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Class Definition
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from IncrementalTrader.strategies.indicators import ATRState
|
|||
|
|
|
|||
|
|
class ATRState(OHLCIndicatorState):
|
|||
|
|
def __init__(self, period: int):
|
|||
|
|
super().__init__(period)
|
|||
|
|
self.true_ranges = []
|
|||
|
|
self.tr_sum = 0.0
|
|||
|
|
self.previous_close = None
|
|||
|
|
|
|||
|
|
def _process_ohlc_data(self, high: float, low: float, close: float):
|
|||
|
|
# Calculate True Range
|
|||
|
|
if self.previous_close is not None:
|
|||
|
|
tr = max(
|
|||
|
|
high - low,
|
|||
|
|
abs(high - self.previous_close),
|
|||
|
|
abs(low - self.previous_close)
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
tr = high - low
|
|||
|
|
|
|||
|
|
# Update True Range moving average
|
|||
|
|
self.true_ranges.append(tr)
|
|||
|
|
self.tr_sum += tr
|
|||
|
|
|
|||
|
|
if len(self.true_ranges) > self.period:
|
|||
|
|
old_tr = self.true_ranges.pop(0)
|
|||
|
|
self.tr_sum -= old_tr
|
|||
|
|
|
|||
|
|
self.previous_close = close
|
|||
|
|
|
|||
|
|
def get_value(self) -> float:
|
|||
|
|
if not self.is_ready():
|
|||
|
|
return 0.0
|
|||
|
|
return self.tr_sum / len(self.true_ranges)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Usage Examples
|
|||
|
|
|
|||
|
|
#### Basic ATR Calculation
|
|||
|
|
```python
|
|||
|
|
# Create 14-period ATR
|
|||
|
|
atr_14 = ATRState(period=14)
|
|||
|
|
|
|||
|
|
# 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:
|
|||
|
|
atr_14.update_ohlc(high, low, close)
|
|||
|
|
if atr_14.is_ready():
|
|||
|
|
print(f"ATR(14): {atr_14.get_value():.2f}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Dynamic Stop-Loss with ATR
|
|||
|
|
```python
|
|||
|
|
class ATRStopLoss:
|
|||
|
|
def __init__(self, atr_period: int = 14, atr_multiplier: float = 2.0):
|
|||
|
|
self.atr = ATRState(atr_period)
|
|||
|
|
self.atr_multiplier = atr_multiplier
|
|||
|
|
|
|||
|
|
def update(self, high: float, low: float, close: float):
|
|||
|
|
self.atr.update_ohlc(high, low, close)
|
|||
|
|
|
|||
|
|
def get_stop_loss(self, entry_price: float, position_type: str) -> float:
|
|||
|
|
if not self.atr.is_ready():
|
|||
|
|
return entry_price * 0.95 if position_type == "LONG" else entry_price * 1.05
|
|||
|
|
|
|||
|
|
atr_value = self.atr.get_value()
|
|||
|
|
|
|||
|
|
if position_type == "LONG":
|
|||
|
|
return entry_price - (atr_value * self.atr_multiplier)
|
|||
|
|
else: # SHORT
|
|||
|
|
return entry_price + (atr_value * self.atr_multiplier)
|
|||
|
|
|
|||
|
|
def get_position_size(self, account_balance: float, risk_percent: float, entry_price: float, position_type: str) -> float:
|
|||
|
|
"""Calculate position size based on ATR risk."""
|
|||
|
|
if not self.atr.is_ready():
|
|||
|
|
return 0.0
|
|||
|
|
|
|||
|
|
risk_amount = account_balance * (risk_percent / 100)
|
|||
|
|
stop_loss = self.get_stop_loss(entry_price, position_type)
|
|||
|
|
risk_per_share = abs(entry_price - stop_loss)
|
|||
|
|
|
|||
|
|
if risk_per_share == 0:
|
|||
|
|
return 0.0
|
|||
|
|
|
|||
|
|
return risk_amount / risk_per_share
|
|||
|
|
|
|||
|
|
# Usage
|
|||
|
|
atr_stop = ATRStopLoss(atr_period=14, atr_multiplier=2.0)
|
|||
|
|
|
|||
|
|
for high, low, close in ohlc_stream:
|
|||
|
|
atr_stop.update(high, low, close)
|
|||
|
|
|
|||
|
|
# Calculate stop loss for a long position
|
|||
|
|
entry_price = close
|
|||
|
|
stop_loss = atr_stop.get_stop_loss(entry_price, "LONG")
|
|||
|
|
position_size = atr_stop.get_position_size(10000, 2.0, entry_price, "LONG")
|
|||
|
|
|
|||
|
|
print(f"Entry: {entry_price:.2f}, Stop: {stop_loss:.2f}, Size: {position_size:.0f}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Performance Characteristics
|
|||
|
|
- **Time Complexity**: O(1) per update
|
|||
|
|
- **Space Complexity**: O(period)
|
|||
|
|
- **Memory Usage**: ~8 bytes per period + constant overhead
|
|||
|
|
|
|||
|
|
## SimpleATRState
|
|||
|
|
|
|||
|
|
Simplified ATR implementation using exponential smoothing instead of simple moving average.
|
|||
|
|
|
|||
|
|
### Features
|
|||
|
|
- **O(1) Memory**: Constant memory usage regardless of period
|
|||
|
|
- **Exponential Smoothing**: Uses Wilder's smoothing method
|
|||
|
|
- **Faster Computation**: No need to maintain historical True Range values
|
|||
|
|
- **Traditional ATR**: Follows Wilder's original ATR calculation
|
|||
|
|
|
|||
|
|
### Mathematical Formula
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
True Range = max(
|
|||
|
|
High - Low,
|
|||
|
|
|High - Previous_Close|,
|
|||
|
|
|Low - Previous_Close|
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
ATR = (Previous_ATR × (period - 1) + True_Range) / period
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Class Definition
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class SimpleATRState(OHLCIndicatorState):
|
|||
|
|
def __init__(self, period: int):
|
|||
|
|
super().__init__(period)
|
|||
|
|
self.atr_value = 0.0
|
|||
|
|
self.previous_close = None
|
|||
|
|
self.is_first_value = True
|
|||
|
|
|
|||
|
|
def _process_ohlc_data(self, high: float, low: float, close: float):
|
|||
|
|
# Calculate True Range
|
|||
|
|
if self.previous_close is not None:
|
|||
|
|
tr = max(
|
|||
|
|
high - low,
|
|||
|
|
abs(high - self.previous_close),
|
|||
|
|
abs(low - self.previous_close)
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
tr = high - low
|
|||
|
|
|
|||
|
|
# Update ATR using Wilder's smoothing
|
|||
|
|
if self.is_first_value:
|
|||
|
|
self.atr_value = tr
|
|||
|
|
self.is_first_value = False
|
|||
|
|
else:
|
|||
|
|
self.atr_value = ((self.atr_value * (self.period - 1)) + tr) / self.period
|
|||
|
|
|
|||
|
|
self.previous_close = close
|
|||
|
|
|
|||
|
|
def get_value(self) -> float:
|
|||
|
|
return self.atr_value
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Usage Examples
|
|||
|
|
|
|||
|
|
#### Memory-Efficient ATR
|
|||
|
|
```python
|
|||
|
|
# Create memory-efficient ATR
|
|||
|
|
simple_atr = SimpleATRState(period=14)
|
|||
|
|
|
|||
|
|
# Process large amounts of data with constant memory
|
|||
|
|
for i, (high, low, close) in enumerate(large_ohlc_dataset):
|
|||
|
|
simple_atr.update_ohlc(high, low, close)
|
|||
|
|
|
|||
|
|
if i % 1000 == 0: # Print every 1000 updates
|
|||
|
|
print(f"ATR after {i} updates: {simple_atr.get_value():.4f}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Volatility Breakout Strategy
|
|||
|
|
```python
|
|||
|
|
class VolatilityBreakout:
|
|||
|
|
def __init__(self, atr_period: int = 14, breakout_multiplier: float = 1.5):
|
|||
|
|
self.atr = SimpleATRState(atr_period)
|
|||
|
|
self.breakout_multiplier = breakout_multiplier
|
|||
|
|
self.previous_close = None
|
|||
|
|
|
|||
|
|
def update(self, high: float, low: float, close: float):
|
|||
|
|
self.atr.update_ohlc(high, low, close)
|
|||
|
|
self.previous_close = close
|
|||
|
|
|
|||
|
|
def get_breakout_levels(self, current_close: float) -> tuple:
|
|||
|
|
"""Get upper and lower breakout levels."""
|
|||
|
|
if not self.atr.is_ready() or self.previous_close is None:
|
|||
|
|
return current_close * 1.01, current_close * 0.99
|
|||
|
|
|
|||
|
|
atr_value = self.atr.get_value()
|
|||
|
|
breakout_distance = atr_value * self.breakout_multiplier
|
|||
|
|
|
|||
|
|
upper_breakout = self.previous_close + breakout_distance
|
|||
|
|
lower_breakout = self.previous_close - breakout_distance
|
|||
|
|
|
|||
|
|
return upper_breakout, lower_breakout
|
|||
|
|
|
|||
|
|
def check_breakout(self, current_high: float, current_low: float, current_close: float) -> str:
|
|||
|
|
"""Check if current price breaks out of volatility range."""
|
|||
|
|
upper_level, lower_level = self.get_breakout_levels(current_close)
|
|||
|
|
|
|||
|
|
if current_high > upper_level:
|
|||
|
|
return "BULLISH_BREAKOUT"
|
|||
|
|
elif current_low < lower_level:
|
|||
|
|
return "BEARISH_BREAKOUT"
|
|||
|
|
|
|||
|
|
return "NO_BREAKOUT"
|
|||
|
|
|
|||
|
|
# Usage
|
|||
|
|
breakout_detector = VolatilityBreakout(atr_period=14, breakout_multiplier=1.5)
|
|||
|
|
|
|||
|
|
for high, low, close in ohlc_data:
|
|||
|
|
breakout_detector.update(high, low, close)
|
|||
|
|
breakout_signal = breakout_detector.check_breakout(high, low, close)
|
|||
|
|
|
|||
|
|
if breakout_signal != "NO_BREAKOUT":
|
|||
|
|
print(f"Breakout detected: {breakout_signal} at {close:.2f}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Performance Characteristics
|
|||
|
|
- **Time Complexity**: O(1) per update
|
|||
|
|
- **Space Complexity**: O(1)
|
|||
|
|
- **Memory Usage**: ~32 bytes (constant)
|
|||
|
|
|
|||
|
|
## Comparison: ATRState vs SimpleATRState
|
|||
|
|
|
|||
|
|
| Aspect | ATRState | SimpleATRState |
|
|||
|
|
|--------|----------|----------------|
|
|||
|
|
| **Memory Usage** | O(period) | O(1) |
|
|||
|
|
| **Calculation Method** | Simple Moving Average | Exponential Smoothing |
|
|||
|
|
| **Accuracy** | Higher (true SMA) | Good (Wilder's method) |
|
|||
|
|
| **Responsiveness** | Moderate | Slightly more responsive |
|
|||
|
|
| **Historical Compatibility** | Modern | Traditional (Wilder's) |
|
|||
|
|
|
|||
|
|
### When to Use ATRState
|
|||
|
|
- **Precise Calculations**: When you need exact simple moving average of True Range
|
|||
|
|
- **Backtesting**: For historical analysis where memory isn't constrained
|
|||
|
|
- **Research**: When studying exact ATR behavior
|
|||
|
|
- **Small Periods**: When period is small (< 20) and memory isn't an issue
|
|||
|
|
|
|||
|
|
### When to Use SimpleATRState
|
|||
|
|
- **Memory Efficiency**: When processing large amounts of data
|
|||
|
|
- **Real-time Systems**: For high-frequency trading applications
|
|||
|
|
- **Traditional Analysis**: When following Wilder's original methodology
|
|||
|
|
- **Large Periods**: When using large ATR periods (> 50)
|
|||
|
|
|
|||
|
|
## Advanced Usage Patterns
|
|||
|
|
|
|||
|
|
### Multi-Timeframe ATR Analysis
|
|||
|
|
```python
|
|||
|
|
class MultiTimeframeATR:
|
|||
|
|
def __init__(self):
|
|||
|
|
self.atr_short = SimpleATRState(period=7) # Short-term volatility
|
|||
|
|
self.atr_medium = SimpleATRState(period=14) # Medium-term volatility
|
|||
|
|
self.atr_long = SimpleATRState(period=28) # Long-term volatility
|
|||
|
|
|
|||
|
|
def update(self, high: float, low: float, close: float):
|
|||
|
|
self.atr_short.update_ohlc(high, low, close)
|
|||
|
|
self.atr_medium.update_ohlc(high, low, close)
|
|||
|
|
self.atr_long.update_ohlc(high, low, close)
|
|||
|
|
|
|||
|
|
def get_volatility_regime(self) -> str:
|
|||
|
|
"""Determine current volatility regime."""
|
|||
|
|
if not all([self.atr_short.is_ready(), self.atr_medium.is_ready(), self.atr_long.is_ready()]):
|
|||
|
|
return "UNKNOWN"
|
|||
|
|
|
|||
|
|
short_atr = self.atr_short.get_value()
|
|||
|
|
medium_atr = self.atr_medium.get_value()
|
|||
|
|
long_atr = self.atr_long.get_value()
|
|||
|
|
|
|||
|
|
# Compare short-term to long-term volatility
|
|||
|
|
volatility_ratio = short_atr / long_atr if long_atr > 0 else 1.0
|
|||
|
|
|
|||
|
|
if volatility_ratio > 1.5:
|
|||
|
|
return "HIGH_VOLATILITY"
|
|||
|
|
elif volatility_ratio < 0.7:
|
|||
|
|
return "LOW_VOLATILITY"
|
|||
|
|
else:
|
|||
|
|
return "NORMAL_VOLATILITY"
|
|||
|
|
|
|||
|
|
def get_adaptive_stop_multiplier(self) -> float:
|
|||
|
|
"""Get adaptive stop-loss multiplier based on volatility regime."""
|
|||
|
|
regime = self.get_volatility_regime()
|
|||
|
|
|
|||
|
|
if regime == "HIGH_VOLATILITY":
|
|||
|
|
return 2.5 # Wider stops in high volatility
|
|||
|
|
elif regime == "LOW_VOLATILITY":
|
|||
|
|
return 1.5 # Tighter stops in low volatility
|
|||
|
|
else:
|
|||
|
|
return 2.0 # Standard stops in normal volatility
|
|||
|
|
|
|||
|
|
# Usage
|
|||
|
|
multi_atr = MultiTimeframeATR()
|
|||
|
|
|
|||
|
|
for high, low, close in ohlc_data:
|
|||
|
|
multi_atr.update(high, low, close)
|
|||
|
|
|
|||
|
|
regime = multi_atr.get_volatility_regime()
|
|||
|
|
stop_multiplier = multi_atr.get_adaptive_stop_multiplier()
|
|||
|
|
|
|||
|
|
print(f"Volatility Regime: {regime}, Stop Multiplier: {stop_multiplier:.1f}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ATR-Based Position Sizing
|
|||
|
|
```python
|
|||
|
|
class ATRPositionSizer:
|
|||
|
|
def __init__(self, atr_period: int = 14):
|
|||
|
|
self.atr = SimpleATRState(atr_period)
|
|||
|
|
self.price_history = []
|
|||
|
|
|
|||
|
|
def update(self, high: float, low: float, close: float):
|
|||
|
|
self.atr.update_ohlc(high, low, close)
|
|||
|
|
self.price_history.append(close)
|
|||
|
|
|
|||
|
|
# Keep only recent price history
|
|||
|
|
if len(self.price_history) > 100:
|
|||
|
|
self.price_history.pop(0)
|
|||
|
|
|
|||
|
|
def calculate_position_size(self, account_balance: float, risk_percent: float,
|
|||
|
|
entry_price: float, stop_loss_atr_multiplier: float = 2.0) -> dict:
|
|||
|
|
"""Calculate position size based on ATR risk management."""
|
|||
|
|
|
|||
|
|
if not self.atr.is_ready():
|
|||
|
|
return {"position_size": 0, "risk_amount": 0, "stop_loss": entry_price * 0.95}
|
|||
|
|
|
|||
|
|
atr_value = self.atr.get_value()
|
|||
|
|
risk_amount = account_balance * (risk_percent / 100)
|
|||
|
|
|
|||
|
|
# Calculate stop loss based on ATR
|
|||
|
|
stop_loss = entry_price - (atr_value * stop_loss_atr_multiplier)
|
|||
|
|
risk_per_share = entry_price - stop_loss
|
|||
|
|
|
|||
|
|
# Calculate position size
|
|||
|
|
if risk_per_share > 0:
|
|||
|
|
position_size = risk_amount / risk_per_share
|
|||
|
|
else:
|
|||
|
|
position_size = 0
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"position_size": position_size,
|
|||
|
|
"risk_amount": risk_amount,
|
|||
|
|
"stop_loss": stop_loss,
|
|||
|
|
"atr_value": atr_value,
|
|||
|
|
"risk_per_share": risk_per_share
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def get_volatility_percentile(self) -> float:
|
|||
|
|
"""Get current ATR percentile compared to recent history."""
|
|||
|
|
if not self.atr.is_ready() or len(self.price_history) < 20:
|
|||
|
|
return 50.0 # Default to median
|
|||
|
|
|
|||
|
|
current_atr = self.atr.get_value()
|
|||
|
|
|
|||
|
|
# Calculate ATR for recent periods
|
|||
|
|
recent_atrs = []
|
|||
|
|
for i in range(len(self.price_history) - 14):
|
|||
|
|
if i + 14 < len(self.price_history):
|
|||
|
|
# Simplified ATR calculation for comparison
|
|||
|
|
price_range = max(self.price_history[i:i+14]) - min(self.price_history[i:i+14])
|
|||
|
|
recent_atrs.append(price_range)
|
|||
|
|
|
|||
|
|
if not recent_atrs:
|
|||
|
|
return 50.0
|
|||
|
|
|
|||
|
|
# Calculate percentile
|
|||
|
|
sorted_atrs = sorted(recent_atrs)
|
|||
|
|
position = sum(1 for atr in sorted_atrs if atr <= current_atr)
|
|||
|
|
percentile = (position / len(sorted_atrs)) * 100
|
|||
|
|
|
|||
|
|
return percentile
|
|||
|
|
|
|||
|
|
# Usage
|
|||
|
|
position_sizer = ATRPositionSizer(atr_period=14)
|
|||
|
|
|
|||
|
|
for high, low, close in ohlc_data:
|
|||
|
|
position_sizer.update(high, low, close)
|
|||
|
|
|
|||
|
|
# Calculate position for a potential trade
|
|||
|
|
trade_info = position_sizer.calculate_position_size(
|
|||
|
|
account_balance=10000,
|
|||
|
|
risk_percent=2.0,
|
|||
|
|
entry_price=close,
|
|||
|
|
stop_loss_atr_multiplier=2.0
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
volatility_percentile = position_sizer.get_volatility_percentile()
|
|||
|
|
|
|||
|
|
print(f"Price: {close:.2f}, Position Size: {trade_info['position_size']:.0f}, "
|
|||
|
|
f"ATR Percentile: {volatility_percentile:.1f}%")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Integration with Strategies
|
|||
|
|
|
|||
|
|
### ATR-Enhanced Strategy Example
|
|||
|
|
```python
|
|||
|
|
class ATRTrendStrategy(IncStrategyBase):
|
|||
|
|
def __init__(self, name: str, params: dict = None):
|
|||
|
|
super().__init__(name, params)
|
|||
|
|
|
|||
|
|
# Initialize indicators
|
|||
|
|
self.atr = SimpleATRState(self.params.get('atr_period', 14))
|
|||
|
|
self.sma = MovingAverageState(self.params.get('sma_period', 20))
|
|||
|
|
|
|||
|
|
# ATR parameters
|
|||
|
|
self.atr_stop_multiplier = self.params.get('atr_stop_multiplier', 2.0)
|
|||
|
|
self.atr_entry_multiplier = self.params.get('atr_entry_multiplier', 0.5)
|
|||
|
|
|
|||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
|||
|
|
open_price, high, low, close, volume = ohlcv
|
|||
|
|
|
|||
|
|
# Update indicators
|
|||
|
|
self.atr.update_ohlc(high, low, close)
|
|||
|
|
self.sma.update(close)
|
|||
|
|
|
|||
|
|
# Wait for indicators to be ready
|
|||
|
|
if not all([self.atr.is_ready(), self.sma.is_ready()]):
|
|||
|
|
return IncStrategySignal.HOLD()
|
|||
|
|
|
|||
|
|
atr_value = self.atr.get_value()
|
|||
|
|
sma_value = self.sma.get_value()
|
|||
|
|
|
|||
|
|
# Calculate dynamic entry threshold based on ATR
|
|||
|
|
entry_threshold = atr_value * self.atr_entry_multiplier
|
|||
|
|
|
|||
|
|
# Generate signals based on trend and volatility
|
|||
|
|
if close > sma_value + entry_threshold:
|
|||
|
|
# Strong uptrend with sufficient volatility
|
|||
|
|
confidence = min(0.9, (close - sma_value) / atr_value * 0.1)
|
|||
|
|
|
|||
|
|
# Calculate stop loss
|
|||
|
|
stop_loss = close - (atr_value * self.atr_stop_multiplier)
|
|||
|
|
|
|||
|
|
return IncStrategySignal.BUY(
|
|||
|
|
confidence=confidence,
|
|||
|
|
metadata={
|
|||
|
|
'atr_value': atr_value,
|
|||
|
|
'sma_value': sma_value,
|
|||
|
|
'stop_loss': stop_loss,
|
|||
|
|
'entry_threshold': entry_threshold
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
elif close < sma_value - entry_threshold:
|
|||
|
|
# Strong downtrend with sufficient volatility
|
|||
|
|
confidence = min(0.9, (sma_value - close) / atr_value * 0.1)
|
|||
|
|
|
|||
|
|
# Calculate stop loss
|
|||
|
|
stop_loss = close + (atr_value * self.atr_stop_multiplier)
|
|||
|
|
|
|||
|
|
return IncStrategySignal.SELL(
|
|||
|
|
confidence=confidence,
|
|||
|
|
metadata={
|
|||
|
|
'atr_value': atr_value,
|
|||
|
|
'sma_value': sma_value,
|
|||
|
|
'stop_loss': stop_loss,
|
|||
|
|
'entry_threshold': entry_threshold
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
return IncStrategySignal.HOLD()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Performance Optimization Tips
|
|||
|
|
|
|||
|
|
### 1. Choose the Right ATR Implementation
|
|||
|
|
```python
|
|||
|
|
# For memory-constrained environments
|
|||
|
|
atr = SimpleATRState(period=14) # O(1) memory
|
|||
|
|
|
|||
|
|
# For precise calculations
|
|||
|
|
atr = ATRState(period=14) # O(period) memory
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Batch Processing for Multiple ATRs
|
|||
|
|
```python
|
|||
|
|
def update_multiple_atrs(atrs: list, high: float, low: float, close: float):
|
|||
|
|
"""Efficiently update multiple ATR indicators."""
|
|||
|
|
for atr in atrs:
|
|||
|
|
atr.update_ohlc(high, low, close)
|
|||
|
|
|
|||
|
|
return [atr.get_value() for atr in atrs if atr.is_ready()]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Cache ATR Values for Complex Calculations
|
|||
|
|
```python
|
|||
|
|
class CachedATR:
|
|||
|
|
def __init__(self, period: int):
|
|||
|
|
self.atr = SimpleATRState(period)
|
|||
|
|
self._cached_value = 0.0
|
|||
|
|
self._cache_valid = False
|
|||
|
|
|
|||
|
|
def update_ohlc(self, high: float, low: float, close: float):
|
|||
|
|
self.atr.update_ohlc(high, low, close)
|
|||
|
|
self._cache_valid = False
|
|||
|
|
|
|||
|
|
def get_value(self) -> float:
|
|||
|
|
if not self._cache_valid:
|
|||
|
|
self._cached_value = self.atr.get_value()
|
|||
|
|
self._cache_valid = True
|
|||
|
|
return self._cached_value
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*ATR indicators are essential for risk management and volatility analysis. Use ATRState for precise calculations or SimpleATRState for memory efficiency in high-frequency applications.*
|