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