143 lines
3.3 KiB
Markdown
143 lines
3.3 KiB
Markdown
# Indicator Development Guide
|
|
|
|
Custom indicators in Dexorder are Python functions that process OHLCV data and return signals or values.
|
|
|
|
## Indicator Structure
|
|
|
|
```python
|
|
def my_indicator(df, **params):
|
|
"""
|
|
Calculate custom indicator
|
|
|
|
Args:
|
|
df: DataFrame with columns [open, high, low, close, volume]
|
|
**params: Indicator parameters
|
|
|
|
Returns:
|
|
Series or DataFrame with indicator values
|
|
"""
|
|
# Implementation
|
|
return result
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Simple Moving Average
|
|
```python
|
|
def sma(df, period=20):
|
|
return df['close'].rolling(window=period).mean()
|
|
```
|
|
|
|
### Exponential Moving Average
|
|
```python
|
|
def ema(df, period=20):
|
|
return df['close'].ewm(span=period, adjust=False).mean()
|
|
```
|
|
|
|
### RSI (Relative Strength Index)
|
|
```python
|
|
def rsi(df, period=14):
|
|
delta = df['close'].diff()
|
|
gain = delta.where(delta > 0, 0).rolling(window=period).mean()
|
|
loss = -delta.where(delta < 0, 0).rolling(window=period).mean()
|
|
rs = gain / loss
|
|
return 100 - (100 / (1 + rs))
|
|
```
|
|
|
|
### MACD
|
|
```python
|
|
def macd(df, fast=12, slow=26, signal=9):
|
|
ema_fast = df['close'].ewm(span=fast).mean()
|
|
ema_slow = df['close'].ewm(span=slow).mean()
|
|
macd_line = ema_fast - ema_slow
|
|
signal_line = macd_line.ewm(span=signal).mean()
|
|
histogram = macd_line - signal_line
|
|
|
|
return pd.DataFrame({
|
|
'macd': macd_line,
|
|
'signal': signal_line,
|
|
'histogram': histogram
|
|
})
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Data Handling
|
|
- Always validate input DataFrame has required columns
|
|
- Handle NaN values appropriately
|
|
- Use `.copy()` to avoid modifying original data
|
|
- Consider edge cases (not enough data, etc.)
|
|
|
|
### Performance
|
|
- Vectorize operations when possible (avoid loops)
|
|
- Use pandas/numpy built-in functions
|
|
- Cache expensive calculations
|
|
- Test on large datasets
|
|
|
|
### Parameters
|
|
- Provide sensible defaults
|
|
- Document parameter ranges
|
|
- Validate parameter values
|
|
- Consider optimization bounds
|
|
|
|
### Testing
|
|
```python
|
|
def test_indicator():
|
|
# Create sample data
|
|
df = pd.DataFrame({
|
|
'close': [100, 102, 101, 103, 105]
|
|
})
|
|
|
|
# Test calculation
|
|
result = my_indicator(df, param=10)
|
|
|
|
# Validate output
|
|
assert not result.isna().all()
|
|
assert len(result) == len(df)
|
|
```
|
|
|
|
## Common Pitfalls
|
|
|
|
### Look-Ahead Bias
|
|
Never use future data:
|
|
```python
|
|
# WRONG - uses future data
|
|
df['signal'] = df['close'].shift(-1) > df['close']
|
|
|
|
# CORRECT - only past data
|
|
df['signal'] = df['close'] > df['close'].shift(1)
|
|
```
|
|
|
|
### Repainting
|
|
Indicator values should not change for closed bars:
|
|
```python
|
|
# Ensure calculations are based on closed candles
|
|
# Avoid using unstable data sources
|
|
```
|
|
|
|
### Overfitting
|
|
- Don't optimize on same data you test on
|
|
- Use separate train/validation/test sets
|
|
- Walk-forward analysis for robustness
|
|
- Simple is often better than complex
|
|
|
|
## Integration with Strategies
|
|
|
|
Indicators are used in strategy signals:
|
|
```python
|
|
def my_strategy(df):
|
|
# Calculate indicators
|
|
df['rsi'] = rsi(df, period=14)
|
|
df['sma_fast'] = sma(df, period=20)
|
|
df['sma_slow'] = sma(df, period=50)
|
|
|
|
# Generate signals
|
|
df['signal'] = 0
|
|
df.loc[(df['rsi'] < 30) & (df['sma_fast'] > df['sma_slow']), 'signal'] = 1
|
|
df.loc[(df['rsi'] > 70) & (df['sma_fast'] < df['sma_slow']), 'signal'] = -1
|
|
|
|
return df
|
|
```
|
|
|
|
Store indicators in your git repository under `indicators/` directory.
|