Files
ai/gateway/knowledge/indicators/indicator-development.md

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.