- 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.
353 lines
10 KiB
Markdown
353 lines
10 KiB
Markdown
# 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](#prerequisites)
|
|
2. [Implementation Steps](#implementation-steps)
|
|
3. [Integration with Charts](#integration-with-charts)
|
|
4. [Best Practices](#best-practices)
|
|
5. [Testing Guidelines](#testing-guidelines)
|
|
6. [Common Pitfalls](#common-pitfalls)
|
|
7. [Example Implementation](#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`):
|
|
|
|
```python
|
|
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`:
|
|
|
|
```python
|
|
from .stochastic import StochasticIndicator
|
|
|
|
__all__ = [
|
|
'SMAIndicator',
|
|
'EMAIndicator',
|
|
'RSIIndicator',
|
|
'MACDIndicator',
|
|
'BollingerBandsIndicator',
|
|
'StochasticIndicator'
|
|
]
|
|
```
|
|
|
|
### 3. Add to TechnicalIndicators Class
|
|
|
|
Update `data/common/indicators/technical.py`:
|
|
|
|
```python
|
|
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):
|
|
|
|
```python
|
|
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`:
|
|
|
|
```python
|
|
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`:
|
|
|
|
```python
|
|
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
|
|
```bash
|
|
# 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
|
|
|
|
## Related Documentation
|
|
|
|
- [Technical Indicators Overview](../modules/technical-indicators.md)
|
|
- [Chart System Documentation](../modules/charts/README.md)
|
|
- [Data Types Documentation](../modules/data-types.md) |