580 lines
17 KiB
Markdown
580 lines
17 KiB
Markdown
|
|
# Strategy Development Guide
|
||
|
|
|
||
|
|
This guide explains how to create custom trading strategies using the IncrementalTrader framework.
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
IncrementalTrader strategies are built around the `IncStrategyBase` class, which provides a robust framework for incremental computation, timeframe aggregation, and signal generation.
|
||
|
|
|
||
|
|
## Basic Strategy Structure
|
||
|
|
|
||
|
|
```python
|
||
|
|
from IncrementalTrader.strategies.base import IncStrategyBase, IncStrategySignal
|
||
|
|
from IncrementalTrader.strategies.indicators import MovingAverageState
|
||
|
|
|
||
|
|
class MyCustomStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
super().__init__(name, params)
|
||
|
|
|
||
|
|
# Initialize indicators
|
||
|
|
self.sma_fast = MovingAverageState(period=self.params.get('fast_period', 10))
|
||
|
|
self.sma_slow = MovingAverageState(period=self.params.get('slow_period', 20))
|
||
|
|
|
||
|
|
# Strategy state
|
||
|
|
self.current_signal = IncStrategySignal.HOLD()
|
||
|
|
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
"""Process aggregated data and generate signals."""
|
||
|
|
open_price, high, low, close, volume = ohlcv
|
||
|
|
|
||
|
|
# Update indicators
|
||
|
|
self.sma_fast.update(close)
|
||
|
|
self.sma_slow.update(close)
|
||
|
|
|
||
|
|
# Generate signals
|
||
|
|
if self.sma_fast.is_ready() and self.sma_slow.is_ready():
|
||
|
|
fast_sma = self.sma_fast.get_value()
|
||
|
|
slow_sma = self.sma_slow.get_value()
|
||
|
|
|
||
|
|
if fast_sma > slow_sma and self.current_signal.signal_type != 'BUY':
|
||
|
|
self.current_signal = IncStrategySignal.BUY(
|
||
|
|
confidence=0.8,
|
||
|
|
metadata={'fast_sma': fast_sma, 'slow_sma': slow_sma}
|
||
|
|
)
|
||
|
|
elif fast_sma < slow_sma and self.current_signal.signal_type != 'SELL':
|
||
|
|
self.current_signal = IncStrategySignal.SELL(
|
||
|
|
confidence=0.8,
|
||
|
|
metadata={'fast_sma': fast_sma, 'slow_sma': slow_sma}
|
||
|
|
)
|
||
|
|
|
||
|
|
return self.current_signal
|
||
|
|
```
|
||
|
|
|
||
|
|
## Key Components
|
||
|
|
|
||
|
|
### 1. Base Class Inheritance
|
||
|
|
|
||
|
|
All strategies must inherit from `IncStrategyBase`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
class MyStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
super().__init__(name, params)
|
||
|
|
# Your initialization code here
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Required Methods
|
||
|
|
|
||
|
|
#### `_process_aggregated_data()`
|
||
|
|
|
||
|
|
This is the core method where your strategy logic goes:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
"""
|
||
|
|
Process aggregated OHLCV data and return a signal.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
timestamp: Unix timestamp
|
||
|
|
ohlcv: Tuple of (open, high, low, close, volume)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
IncStrategySignal: BUY, SELL, or HOLD signal
|
||
|
|
"""
|
||
|
|
# Your strategy logic here
|
||
|
|
return signal
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Signal Generation
|
||
|
|
|
||
|
|
Use the factory methods to create signals:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Buy signal
|
||
|
|
signal = IncStrategySignal.BUY(
|
||
|
|
confidence=0.8, # Optional: 0.0 to 1.0
|
||
|
|
metadata={'reason': 'Golden cross detected'} # Optional: additional data
|
||
|
|
)
|
||
|
|
|
||
|
|
# Sell signal
|
||
|
|
signal = IncStrategySignal.SELL(
|
||
|
|
confidence=0.9,
|
||
|
|
metadata={'reason': 'Death cross detected'}
|
||
|
|
)
|
||
|
|
|
||
|
|
# Hold signal
|
||
|
|
signal = IncStrategySignal.HOLD()
|
||
|
|
```
|
||
|
|
|
||
|
|
## Using Indicators
|
||
|
|
|
||
|
|
### Built-in Indicators
|
||
|
|
|
||
|
|
IncrementalTrader provides many built-in indicators:
|
||
|
|
|
||
|
|
```python
|
||
|
|
from IncrementalTrader.strategies.indicators import (
|
||
|
|
MovingAverageState,
|
||
|
|
ExponentialMovingAverageState,
|
||
|
|
ATRState,
|
||
|
|
SupertrendState,
|
||
|
|
RSIState,
|
||
|
|
BollingerBandsState
|
||
|
|
)
|
||
|
|
|
||
|
|
class MyStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
super().__init__(name, params)
|
||
|
|
|
||
|
|
# Moving averages
|
||
|
|
self.sma = MovingAverageState(period=20)
|
||
|
|
self.ema = ExponentialMovingAverageState(period=20, alpha=0.1)
|
||
|
|
|
||
|
|
# Volatility
|
||
|
|
self.atr = ATRState(period=14)
|
||
|
|
|
||
|
|
# Trend
|
||
|
|
self.supertrend = SupertrendState(period=10, multiplier=3.0)
|
||
|
|
|
||
|
|
# Oscillators
|
||
|
|
self.rsi = RSIState(period=14)
|
||
|
|
self.bb = BollingerBandsState(period=20, std_dev=2.0)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Indicator Usage Pattern
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
open_price, high, low, close, volume = ohlcv
|
||
|
|
|
||
|
|
# Update indicators
|
||
|
|
self.sma.update(close)
|
||
|
|
self.rsi.update(close)
|
||
|
|
self.atr.update_ohlc(high, low, close)
|
||
|
|
|
||
|
|
# Check if indicators are ready
|
||
|
|
if not (self.sma.is_ready() and self.rsi.is_ready()):
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
|
||
|
|
# Get indicator values
|
||
|
|
sma_value = self.sma.get_value()
|
||
|
|
rsi_value = self.rsi.get_value()
|
||
|
|
atr_value = self.atr.get_value()
|
||
|
|
|
||
|
|
# Your strategy logic here
|
||
|
|
# ...
|
||
|
|
```
|
||
|
|
|
||
|
|
## Advanced Features
|
||
|
|
|
||
|
|
### 1. Timeframe Aggregation
|
||
|
|
|
||
|
|
The base class automatically handles timeframe aggregation:
|
||
|
|
|
||
|
|
```python
|
||
|
|
class MyStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
# Set timeframe in params
|
||
|
|
default_params = {"timeframe": "15min"}
|
||
|
|
if params:
|
||
|
|
default_params.update(params)
|
||
|
|
super().__init__(name, default_params)
|
||
|
|
```
|
||
|
|
|
||
|
|
Supported timeframes:
|
||
|
|
- `"1min"`, `"5min"`, `"15min"`, `"30min"`
|
||
|
|
- `"1h"`, `"4h"`, `"1d"`
|
||
|
|
|
||
|
|
### 2. State Management
|
||
|
|
|
||
|
|
Track strategy state for complex logic:
|
||
|
|
|
||
|
|
```python
|
||
|
|
class TrendFollowingStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
super().__init__(name, params)
|
||
|
|
|
||
|
|
# Strategy state
|
||
|
|
self.trend_state = "UNKNOWN" # BULLISH, BEARISH, SIDEWAYS
|
||
|
|
self.position_state = "NONE" # LONG, SHORT, NONE
|
||
|
|
self.last_signal_time = 0
|
||
|
|
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
# Update trend state
|
||
|
|
self._update_trend_state(ohlcv)
|
||
|
|
|
||
|
|
# Generate signals based on trend and position
|
||
|
|
if self.trend_state == "BULLISH" and self.position_state != "LONG":
|
||
|
|
self.position_state = "LONG"
|
||
|
|
return IncStrategySignal.BUY(confidence=0.8)
|
||
|
|
elif self.trend_state == "BEARISH" and self.position_state != "SHORT":
|
||
|
|
self.position_state = "SHORT"
|
||
|
|
return IncStrategySignal.SELL(confidence=0.8)
|
||
|
|
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Multi-Indicator Strategies
|
||
|
|
|
||
|
|
Combine multiple indicators for robust signals:
|
||
|
|
|
||
|
|
```python
|
||
|
|
class MultiIndicatorStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
super().__init__(name, params)
|
||
|
|
|
||
|
|
# Trend indicators
|
||
|
|
self.supertrend = SupertrendState(period=10, multiplier=3.0)
|
||
|
|
self.sma_50 = MovingAverageState(period=50)
|
||
|
|
self.sma_200 = MovingAverageState(period=200)
|
||
|
|
|
||
|
|
# Momentum indicators
|
||
|
|
self.rsi = RSIState(period=14)
|
||
|
|
|
||
|
|
# Volatility indicators
|
||
|
|
self.bb = BollingerBandsState(period=20, std_dev=2.0)
|
||
|
|
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
open_price, high, low, close, volume = ohlcv
|
||
|
|
|
||
|
|
# Update all indicators
|
||
|
|
self.supertrend.update_ohlc(high, low, close)
|
||
|
|
self.sma_50.update(close)
|
||
|
|
self.sma_200.update(close)
|
||
|
|
self.rsi.update(close)
|
||
|
|
self.bb.update(close)
|
||
|
|
|
||
|
|
# Wait for all indicators to be ready
|
||
|
|
if not all([
|
||
|
|
self.supertrend.is_ready(),
|
||
|
|
self.sma_50.is_ready(),
|
||
|
|
self.sma_200.is_ready(),
|
||
|
|
self.rsi.is_ready(),
|
||
|
|
self.bb.is_ready()
|
||
|
|
]):
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
|
||
|
|
# Get indicator values
|
||
|
|
supertrend_signal = self.supertrend.get_signal()
|
||
|
|
sma_50 = self.sma_50.get_value()
|
||
|
|
sma_200 = self.sma_200.get_value()
|
||
|
|
rsi = self.rsi.get_value()
|
||
|
|
bb_upper, bb_middle, bb_lower = self.bb.get_bands()
|
||
|
|
|
||
|
|
# Multi-condition buy signal
|
||
|
|
buy_conditions = [
|
||
|
|
supertrend_signal == 'BUY',
|
||
|
|
sma_50 > sma_200, # Long-term uptrend
|
||
|
|
rsi < 70, # Not overbought
|
||
|
|
close < bb_upper # Not at upper band
|
||
|
|
]
|
||
|
|
|
||
|
|
# Multi-condition sell signal
|
||
|
|
sell_conditions = [
|
||
|
|
supertrend_signal == 'SELL',
|
||
|
|
sma_50 < sma_200, # Long-term downtrend
|
||
|
|
rsi > 30, # Not oversold
|
||
|
|
close > bb_lower # Not at lower band
|
||
|
|
]
|
||
|
|
|
||
|
|
if all(buy_conditions):
|
||
|
|
confidence = sum([1 for c in buy_conditions if c]) / len(buy_conditions)
|
||
|
|
return IncStrategySignal.BUY(
|
||
|
|
confidence=confidence,
|
||
|
|
metadata={
|
||
|
|
'supertrend': supertrend_signal,
|
||
|
|
'sma_trend': 'UP' if sma_50 > sma_200 else 'DOWN',
|
||
|
|
'rsi': rsi,
|
||
|
|
'bb_position': 'MIDDLE'
|
||
|
|
}
|
||
|
|
)
|
||
|
|
elif all(sell_conditions):
|
||
|
|
confidence = sum([1 for c in sell_conditions if c]) / len(sell_conditions)
|
||
|
|
return IncStrategySignal.SELL(
|
||
|
|
confidence=confidence,
|
||
|
|
metadata={
|
||
|
|
'supertrend': supertrend_signal,
|
||
|
|
'sma_trend': 'DOWN' if sma_50 < sma_200 else 'UP',
|
||
|
|
'rsi': rsi,
|
||
|
|
'bb_position': 'MIDDLE'
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
```
|
||
|
|
|
||
|
|
## Parameter Management
|
||
|
|
|
||
|
|
### Default Parameters
|
||
|
|
|
||
|
|
Define default parameters in your strategy:
|
||
|
|
|
||
|
|
```python
|
||
|
|
class MyStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
# Define defaults
|
||
|
|
default_params = {
|
||
|
|
"timeframe": "15min",
|
||
|
|
"fast_period": 10,
|
||
|
|
"slow_period": 20,
|
||
|
|
"rsi_period": 14,
|
||
|
|
"rsi_overbought": 70,
|
||
|
|
"rsi_oversold": 30
|
||
|
|
}
|
||
|
|
|
||
|
|
# Merge with provided params
|
||
|
|
if params:
|
||
|
|
default_params.update(params)
|
||
|
|
|
||
|
|
super().__init__(name, default_params)
|
||
|
|
|
||
|
|
# Use parameters
|
||
|
|
self.fast_sma = MovingAverageState(period=self.params['fast_period'])
|
||
|
|
self.slow_sma = MovingAverageState(period=self.params['slow_period'])
|
||
|
|
self.rsi = RSIState(period=self.params['rsi_period'])
|
||
|
|
```
|
||
|
|
|
||
|
|
### Parameter Validation
|
||
|
|
|
||
|
|
Add validation for critical parameters:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
super().__init__(name, params)
|
||
|
|
|
||
|
|
# Validate parameters
|
||
|
|
if self.params['fast_period'] >= self.params['slow_period']:
|
||
|
|
raise ValueError("fast_period must be less than slow_period")
|
||
|
|
|
||
|
|
if not (1 <= self.params['rsi_period'] <= 100):
|
||
|
|
raise ValueError("rsi_period must be between 1 and 100")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing Your Strategy
|
||
|
|
|
||
|
|
### Unit Testing
|
||
|
|
|
||
|
|
```python
|
||
|
|
import unittest
|
||
|
|
from IncrementalTrader.strategies.base import IncStrategySignal
|
||
|
|
|
||
|
|
class TestMyStrategy(unittest.TestCase):
|
||
|
|
def setUp(self):
|
||
|
|
self.strategy = MyCustomStrategy("test", {
|
||
|
|
"fast_period": 5,
|
||
|
|
"slow_period": 10
|
||
|
|
})
|
||
|
|
|
||
|
|
def test_initialization(self):
|
||
|
|
self.assertEqual(self.strategy.name, "test")
|
||
|
|
self.assertEqual(self.strategy.params['fast_period'], 5)
|
||
|
|
|
||
|
|
def test_signal_generation(self):
|
||
|
|
# Feed test data
|
||
|
|
test_data = [
|
||
|
|
(1000, (100, 105, 95, 102, 1000)),
|
||
|
|
(1001, (102, 108, 100, 106, 1200)),
|
||
|
|
# ... more test data
|
||
|
|
]
|
||
|
|
|
||
|
|
for timestamp, ohlcv in test_data:
|
||
|
|
signal = self.strategy.process_data_point(timestamp, ohlcv)
|
||
|
|
self.assertIsInstance(signal, IncStrategySignal)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Backtesting
|
||
|
|
|
||
|
|
```python
|
||
|
|
from IncrementalTrader import IncBacktester, BacktestConfig
|
||
|
|
|
||
|
|
# Test your strategy
|
||
|
|
config = BacktestConfig(
|
||
|
|
initial_usd=10000,
|
||
|
|
start_date="2024-01-01",
|
||
|
|
end_date="2024-03-31"
|
||
|
|
)
|
||
|
|
|
||
|
|
backtester = IncBacktester()
|
||
|
|
results = backtester.run_single_strategy(
|
||
|
|
strategy_class=MyCustomStrategy,
|
||
|
|
strategy_params={"fast_period": 10, "slow_period": 20},
|
||
|
|
config=config,
|
||
|
|
data_file="test_data.csv"
|
||
|
|
)
|
||
|
|
|
||
|
|
print(f"Total Return: {results['performance_metrics']['total_return_pct']:.2f}%")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
### 1. Incremental Design
|
||
|
|
|
||
|
|
Always design for incremental computation:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Good: Incremental calculation
|
||
|
|
class IncrementalSMA:
|
||
|
|
def __init__(self, period):
|
||
|
|
self.period = period
|
||
|
|
self.values = deque(maxlen=period)
|
||
|
|
self.sum = 0
|
||
|
|
|
||
|
|
def update(self, value):
|
||
|
|
if len(self.values) == self.period:
|
||
|
|
self.sum -= self.values[0]
|
||
|
|
self.values.append(value)
|
||
|
|
self.sum += value
|
||
|
|
|
||
|
|
def get_value(self):
|
||
|
|
return self.sum / len(self.values) if self.values else 0
|
||
|
|
|
||
|
|
# Bad: Batch calculation
|
||
|
|
def calculate_sma(prices, period):
|
||
|
|
return [sum(prices[i:i+period])/period for i in range(len(prices)-period+1)]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. State Management
|
||
|
|
|
||
|
|
Keep minimal state and ensure it's always consistent:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
# Update all indicators first
|
||
|
|
self._update_indicators(ohlcv)
|
||
|
|
|
||
|
|
# Then update strategy state
|
||
|
|
self._update_strategy_state()
|
||
|
|
|
||
|
|
# Finally generate signal
|
||
|
|
return self._generate_signal()
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Error Handling
|
||
|
|
|
||
|
|
Handle edge cases gracefully:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
try:
|
||
|
|
open_price, high, low, close, volume = ohlcv
|
||
|
|
|
||
|
|
# Validate data
|
||
|
|
if not all(isinstance(x, (int, float)) for x in ohlcv):
|
||
|
|
self.logger.warning(f"Invalid OHLCV data: {ohlcv}")
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
|
||
|
|
if high < low or close < 0:
|
||
|
|
self.logger.warning(f"Inconsistent price data: {ohlcv}")
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
|
||
|
|
# Your strategy logic here
|
||
|
|
# ...
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(f"Error processing data: {e}")
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Logging
|
||
|
|
|
||
|
|
Use the built-in logger for debugging:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
open_price, high, low, close, volume = ohlcv
|
||
|
|
|
||
|
|
# Log important events
|
||
|
|
if self.sma_fast.get_value() > self.sma_slow.get_value():
|
||
|
|
self.logger.debug(f"Fast SMA ({self.sma_fast.get_value():.2f}) > Slow SMA ({self.sma_slow.get_value():.2f})")
|
||
|
|
|
||
|
|
# Log signal generation
|
||
|
|
if signal.signal_type != 'HOLD':
|
||
|
|
self.logger.info(f"Generated {signal.signal_type} signal with confidence {signal.confidence}")
|
||
|
|
|
||
|
|
return signal
|
||
|
|
```
|
||
|
|
|
||
|
|
## Example Strategies
|
||
|
|
|
||
|
|
### Simple Moving Average Crossover
|
||
|
|
|
||
|
|
```python
|
||
|
|
class SMAStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
default_params = {
|
||
|
|
"timeframe": "15min",
|
||
|
|
"fast_period": 10,
|
||
|
|
"slow_period": 20
|
||
|
|
}
|
||
|
|
if params:
|
||
|
|
default_params.update(params)
|
||
|
|
super().__init__(name, default_params)
|
||
|
|
|
||
|
|
self.sma_fast = MovingAverageState(period=self.params['fast_period'])
|
||
|
|
self.sma_slow = MovingAverageState(period=self.params['slow_period'])
|
||
|
|
self.last_signal = 'HOLD'
|
||
|
|
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
_, _, _, close, _ = ohlcv
|
||
|
|
|
||
|
|
self.sma_fast.update(close)
|
||
|
|
self.sma_slow.update(close)
|
||
|
|
|
||
|
|
if not (self.sma_fast.is_ready() and self.sma_slow.is_ready()):
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
|
||
|
|
fast = self.sma_fast.get_value()
|
||
|
|
slow = self.sma_slow.get_value()
|
||
|
|
|
||
|
|
if fast > slow and self.last_signal != 'BUY':
|
||
|
|
self.last_signal = 'BUY'
|
||
|
|
return IncStrategySignal.BUY(confidence=0.7)
|
||
|
|
elif fast < slow and self.last_signal != 'SELL':
|
||
|
|
self.last_signal = 'SELL'
|
||
|
|
return IncStrategySignal.SELL(confidence=0.7)
|
||
|
|
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
```
|
||
|
|
|
||
|
|
### RSI Mean Reversion
|
||
|
|
|
||
|
|
```python
|
||
|
|
class RSIMeanReversionStrategy(IncStrategyBase):
|
||
|
|
def __init__(self, name: str, params: dict = None):
|
||
|
|
default_params = {
|
||
|
|
"timeframe": "15min",
|
||
|
|
"rsi_period": 14,
|
||
|
|
"oversold": 30,
|
||
|
|
"overbought": 70
|
||
|
|
}
|
||
|
|
if params:
|
||
|
|
default_params.update(params)
|
||
|
|
super().__init__(name, default_params)
|
||
|
|
|
||
|
|
self.rsi = RSIState(period=self.params['rsi_period'])
|
||
|
|
|
||
|
|
def _process_aggregated_data(self, timestamp: int, ohlcv: tuple) -> IncStrategySignal:
|
||
|
|
_, _, _, close, _ = ohlcv
|
||
|
|
|
||
|
|
self.rsi.update(close)
|
||
|
|
|
||
|
|
if not self.rsi.is_ready():
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
|
||
|
|
rsi_value = self.rsi.get_value()
|
||
|
|
|
||
|
|
if rsi_value < self.params['oversold']:
|
||
|
|
return IncStrategySignal.BUY(
|
||
|
|
confidence=min(1.0, (self.params['oversold'] - rsi_value) / 20),
|
||
|
|
metadata={'rsi': rsi_value, 'condition': 'oversold'}
|
||
|
|
)
|
||
|
|
elif rsi_value > self.params['overbought']:
|
||
|
|
return IncStrategySignal.SELL(
|
||
|
|
confidence=min(1.0, (rsi_value - self.params['overbought']) / 20),
|
||
|
|
metadata={'rsi': rsi_value, 'condition': 'overbought'}
|
||
|
|
)
|
||
|
|
|
||
|
|
return IncStrategySignal.HOLD()
|
||
|
|
```
|
||
|
|
|
||
|
|
This guide provides a comprehensive foundation for developing custom strategies with IncrementalTrader. Remember to always test your strategies thoroughly before using them in live trading!
|