TCPDashboard/docs/guides/adding-new-indicators.md
2025-06-07 14:12:37 +08:00

11 KiB

Adding New Indicators Guide

Overview

This guide provides comprehensive instructions for adding new technical indicators to the Crypto Trading Bot Dashboard. The system uses a modular approach where each indicator is implemented as a separate class inheriting from BaseIndicator.

Table of Contents

  1. Prerequisites
  2. Implementation Steps
  3. Integration with Charts
  4. Best Practices
  5. Testing Guidelines
  6. Common Pitfalls
  7. Example Implementation

Prerequisites

  • Python knowledge with pandas/numpy
  • Understanding of technical analysis concepts
  • Familiarity with the project structure
  • Knowledge of the indicator's mathematical formula
  • Understanding of the dashboard's chart system

Implementation Steps

1. Create Indicator Class

Create a new file in data/common/indicators/implementations/ named after your indicator (e.g., stochastic.py):

from typing import Dict, Any, List
import pandas as pd
from ..base import BaseIndicator
from ..result import IndicatorResult

class StochasticIndicator(BaseIndicator):
    """
    Stochastic Oscillator implementation.
    
    The Stochastic Oscillator is a momentum indicator comparing a particular closing price 
    of a security to a range of its prices over a certain period of time.
    """
    
    def __init__(self, logger=None):
        super().__init__(logger)
        self.name = "stochastic"
        
    def calculate(self, df: pd.DataFrame, k_period: int = 14, 
                 d_period: int = 3, price_column: str = 'close') -> List[IndicatorResult]:
        """
        Calculate Stochastic Oscillator.
        
        Args:
            df: DataFrame with OHLCV data
            k_period: The K period (default: 14)
            d_period: The D period (default: 3)
            price_column: Column to use for calculations (default: 'close')
            
        Returns:
            List of IndicatorResult objects containing %K and %D values
        """
        try:
            # Validate inputs
            self._validate_dataframe(df)
            self._validate_period(k_period, min_value=2)
            self._validate_period(d_period, min_value=2)
            
            # Calculate %K
            lowest_low = df['low'].rolling(window=k_period).min()
            highest_high = df['high'].rolling(window=k_period).max()
            k_percent = 100 * ((df[price_column] - lowest_low) / 
                             (highest_high - lowest_low))
            
            # Calculate %D (signal line)
            d_percent = k_percent.rolling(window=d_period).mean()
            
            # Create results
            results = []
            for idx, row in df.iterrows():
                if pd.notna(k_percent[idx]) and pd.notna(d_percent[idx]):
                    results.append(IndicatorResult(
                        timestamp=idx,
                        symbol=self._get_symbol(df),
                        timeframe=self._get_timeframe(df),
                        values={
                            'k_percent': float(k_percent[idx]),
                            'd_percent': float(d_percent[idx])
                        },
                        metadata={
                            'k_period': k_period,
                            'd_period': d_period
                        }
                    ))
            
            return results
            
        except Exception as e:
            self._handle_error(f"Error calculating Stochastic: {str(e)}")
            return []

2. Register the Indicator

Add your indicator to data/common/indicators/implementations/__init__.py:

from .stochastic import StochasticIndicator

__all__ = [
    'SMAIndicator',
    'EMAIndicator',
    'RSIIndicator',
    'MACDIndicator',
    'BollingerBandsIndicator',
    'StochasticIndicator'
]

3. Add to TechnicalIndicators Class

Update data/common/indicators/technical.py:

class TechnicalIndicators:
    def __init__(self, logger=None):
        self.logger = logger
        # ... existing indicators ...
        self._stochastic = StochasticIndicator(logger)
    
    def stochastic(self, df: pd.DataFrame, k_period: int = 14,
                  d_period: int = 3, price_column: str = 'close') -> List[IndicatorResult]:
        """
        Calculate Stochastic Oscillator.
        
        Args:
            df: DataFrame with OHLCV data
            k_period: The K period (default: 14)
            d_period: The D period (default: 3)
            price_column: Column to use (default: 'close')
            
        Returns:
            List of indicator results with %K and %D values
        """
        return self._stochastic.calculate(
            df,
            k_period=k_period,
            d_period=d_period,
            price_column=price_column
        )

Integration with Charts

1. Create Chart Layer

Create a new layer class in components/charts/layers/indicators.py (overlay) or components/charts/layers/subplots.py (subplot):

class StochasticLayer(IndicatorLayer):
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.name = "stochastic"
        self.display_type = "subplot"
    
    def create_traces(self, df: pd.DataFrame, values: Dict[str, pd.Series]) -> List[go.Scatter]:
        traces = []
        traces.append(go.Scatter(
            x=df.index, 
            y=values['k_percent'], 
            mode='lines', 
            name=f"%K ({self.config.get('k_period', 14)})",
            line=dict(
                color=self.config.get('color', '#007bff'),
                width=self.config.get('line_width', 2)
            )
        ))
        traces.append(go.Scatter(
            x=df.index, 
            y=values['d_percent'], 
            mode='lines', 
            name=f"%D ({self.config.get('d_period', 3)})",
            line=dict(
                color=self.config.get('secondary_color', '#ff6b35'),
                width=self.config.get('line_width', 2)
            )
        ))
        return traces

2. Register in Layer Registry

Update components/charts/layers/__init__.py:

SUBPLOT_REGISTRY = {
    'rsi': RSILayer,
    'macd': MACDLayer,
    'stochastic': StochasticLayer,
}

3. Add UI Components

Update dashboard/components/indicator_modal.py:

def create_parameter_fields():
    return html.Div([
        # ... existing fields ...
        html.Div([
            dbc.Row([
                dbc.Col([
                    dbc.Label("%K Period:"),
                    dcc.Input(
                        id='stochastic-k-period-input',
                        type='number',
                        value=14
                    )
                ], width=6),
                dbc.Col([
                    dbc.Label("%D Period:"),
                    dcc.Input(
                        id='stochastic-d-period-input',
                        type='number',
                        value=3
                    )
                ], width=6),
            ]),
            dbc.FormText("Stochastic oscillator periods")
        ], id='stochastic-parameters', style={'display': 'none'})
    ])

Best Practices

Code Quality

  • Follow the project's coding style
  • Add comprehensive docstrings
  • Include type hints
  • Handle edge cases gracefully
  • Use vectorized operations where possible

Error Handling

  • Validate all input parameters
  • Check for sufficient data
  • Handle NaN values appropriately
  • Log errors with meaningful messages
  • Return empty results for invalid inputs

Performance

  • Use vectorized operations
  • Avoid unnecessary loops
  • Clean up temporary calculations
  • Consider memory usage
  • Cache results when appropriate

Documentation

  • Document all public methods
  • Include usage examples
  • Explain parameter ranges
  • Document any assumptions
  • Keep documentation up-to-date

Testing Guidelines

Test File Structure

Create tests/indicators/test_stochastic.py:

import pytest
import pandas as pd
import numpy as np
from data.common.indicators import TechnicalIndicators

@pytest.fixture
def sample_data():
    return pd.DataFrame({
        'open': [10, 11, 12, 13, 14],
        'high': [12, 13, 14, 15, 16],
        'low': [8, 9, 10, 11, 12],
        'close': [11, 12, 13, 14, 15],
        'volume': [100, 110, 120, 130, 140]
    }, index=pd.date_range('2023-01-01', periods=5))

def test_stochastic_calculation(sample_data):
    indicators = TechnicalIndicators()
    results = indicators.stochastic(sample_data, k_period=3, d_period=2)
    
    assert len(results) > 0
    for result in results:
        assert 0 <= result.values['k_percent'] <= 100
        assert 0 <= result.values['d_percent'] <= 100

Testing Checklist

  • Basic functionality with ideal data
  • Edge cases (insufficient data, NaN values)
  • Performance with large datasets
  • Error handling
  • Parameter validation
  • Integration with TechnicalIndicators class
  • Chart layer rendering
  • UI interaction

Running Tests

# Run all indicator tests
uv run pytest tests/indicators/

# Run specific indicator tests
uv run pytest tests/indicators/test_stochastic.py

# Run with coverage
uv run pytest tests/indicators/ --cov=data.common.indicators

Common Pitfalls

  1. Insufficient Data Handling

    • Always check if enough data points are available
    • Return empty results rather than partial calculations
    • Consider the impact of NaN values
  2. NaN Handling

    • Use appropriate pandas NaN handling methods
    • Don't propagate NaN values unnecessarily
    • Document NaN handling behavior
  3. Memory Leaks

    • Clean up temporary DataFrames
    • Avoid storing large datasets
    • Use efficient data structures
  4. Performance Issues

    • Use vectorized operations instead of loops
    • Profile code with large datasets
    • Consider caching strategies
  5. UI Integration

    • Handle all parameter combinations
    • Provide meaningful validation
    • Give clear user feedback

Example Implementation

See the complete Stochastic Oscillator implementation above as a reference. Key points:

  1. Modular Structure

    • Separate indicator class
    • Clear inheritance hierarchy
    • Focused responsibility
  2. Error Handling

    • Input validation
    • Exception handling
    • Meaningful error messages
  3. Performance

    • Vectorized calculations
    • Efficient data structures
    • Memory management
  4. Testing

    • Comprehensive test cases
    • Edge case handling
    • Performance verification

Support

For questions or issues:

  1. Check existing documentation
  2. Review test cases
  3. Consult with team members
  4. Create detailed bug reports if needed