- Introduced dynamic generation of parameter fields and callback handling for indicators, enhancing modularity and maintainability. - Updated `config_utils.py` with new utility functions to load indicator templates and generate dynamic outputs and states for parameter fields. - Refactored `indicators.py` to utilize these utilities, streamlining the callback logic and improving user experience by reducing hardcoded elements. - Modified `indicator_modal.py` to create parameter fields dynamically based on JSON templates, eliminating the need for manual updates when adding new indicators. - Added documentation outlining the new data-driven architecture for indicators, improving clarity and guidance for future development. These changes significantly enhance the flexibility and scalability of the indicator system, aligning with project goals for maintainability and performance.
10 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
- Prerequisites
- Implementation Steps
- Integration with Charts
- Best Practices
- Testing Guidelines
- Common Pitfalls
- 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
(No longer needed - UI is dynamically generated from JSON templates)
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
-
Insufficient Data Handling
- Always check if enough data points are available
- Return empty results rather than partial calculations
- Consider the impact of NaN values
-
NaN Handling
- Use appropriate pandas NaN handling methods
- Don't propagate NaN values unnecessarily
- Document NaN handling behavior
-
Memory Leaks
- Clean up temporary DataFrames
- Avoid storing large datasets
- Use efficient data structures
-
Performance Issues
- Use vectorized operations instead of loops
- Profile code with large datasets
- Consider caching strategies
-
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:
-
Modular Structure
- Separate indicator class
- Clear inheritance hierarchy
- Focused responsibility
-
Error Handling
- Input validation
- Exception handling
- Meaningful error messages
-
Performance
- Vectorized calculations
- Efficient data structures
- Memory management
-
Testing
- Comprehensive test cases
- Edge case handling
- Performance verification
Support
For questions or issues:
- Check existing documentation
- Review test cases
- Consult with team members
- Create detailed bug reports if needed