3.3 KiB
3.3 KiB
Indicator Development Guide
Custom indicators in Dexorder are Python functions that process OHLCV data and return signals or values.
Indicator Structure
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
def sma(df, period=20):
return df['close'].rolling(window=period).mean()
Exponential Moving Average
def ema(df, period=20):
return df['close'].ewm(span=period, adjust=False).mean()
RSI (Relative Strength Index)
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
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
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:
# 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:
# 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:
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.