# 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.