diff --git a/data/common/indicators/technical.py b/data/common/indicators/technical.py index 2fd7b4c..a1cc4d2 100644 --- a/data/common/indicators/technical.py +++ b/data/common/indicators/technical.py @@ -177,6 +177,7 @@ class TechnicalIndicators: def calculate_multiple_indicators(self, df: pd.DataFrame, indicators_config: Dict[str, Dict[str, Any]]) -> Dict[str, List[IndicatorResult]]: """ + TODO: need make more procedural without hardcoding indicators type and so on Calculate multiple indicators at once for efficiency. Args: diff --git a/docs/guides/adding-new-indicators.md b/docs/guides/adding-new-indicators.md new file mode 100644 index 0000000..14b5509 --- /dev/null +++ b/docs/guides/adding-new-indicators.md @@ -0,0 +1,381 @@ +# 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 + +Update `dashboard/components/indicator_modal.py`: + +```python +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`: + +```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) \ No newline at end of file diff --git a/docs/modules/charts/adding-new-indicators.md b/docs/modules/charts/adding-new-indicators.md deleted file mode 100644 index 25b9e2d..0000000 --- a/docs/modules/charts/adding-new-indicators.md +++ /dev/null @@ -1,249 +0,0 @@ -# Quick Guide: Adding New Indicators - -## Overview - -This guide provides a step-by-step checklist for adding new technical indicators to the Crypto Trading Bot Dashboard, updated for the new modular dashboard structure. - -## Prerequisites - -- Understanding of Python and technical analysis -- Familiarity with the project structure and Dash callbacks -- Knowledge of the indicator type (overlay vs subplot) - -## Step-by-Step Checklist - -### ✅ Step 1: Plan Your Indicator - -- [ ] Determine indicator type (overlay or subplot) -- [ ] Define required parameters -- [ ] Choose default styling -- [ ] Research calculation formula - -### ✅ Step 2: Create Indicator Class - -**File**: `components/charts/layers/indicators.py` (overlay) or `components/charts/layers/subplots.py` (subplot) - -Create a class for your indicator that inherits from `IndicatorLayer`. - -```python -class StochasticLayer(IndicatorLayer): - def __init__(self, config: Dict[str, Any]): - super().__init__(config) - self.name = "stochastic" - self.display_type = "subplot" - - def calculate_values(self, df: pd.DataFrame) -> Dict[str, pd.Series]: - k_period = self.config.get('k_period', 14) - d_period = self.config.get('d_period', 3) - lowest_low = df['low'].rolling(window=k_period).min() - highest_high = df['high'].rolling(window=k_period).max() - k_percent = 100 * ((df['close'] - lowest_low) / (highest_high - lowest_low)) - d_percent = k_percent.rolling(window=d_period).mean() - return {'k_percent': k_percent, 'd_percent': d_percent} - - 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 -``` - -### ✅ Step 3: Register Indicator - -**File**: `components/charts/layers/__init__.py` - -Register your new indicator class in the appropriate registry. - -```python -from .subplots import StochasticLayer - -SUBPLOT_REGISTRY = { - 'rsi': RSILayer, - 'macd': MACDLayer, - 'stochastic': StochasticLayer, -} - -INDICATOR_REGISTRY = { - 'sma': SMALayer, - 'ema': EMALayer, - 'bollinger_bands': BollingerBandsLayer, -} -``` - -### ✅ Step 4: Add UI Dropdown Option - -**File**: `dashboard/components/indicator_modal.py` - -Add your new indicator to the `indicator-type-dropdown` options. - -```python -dcc.Dropdown( - id='indicator-type-dropdown', - options=[ - {'label': 'Simple Moving Average (SMA)', 'value': 'sma'}, - {'label': 'Exponential Moving Average (EMA)', 'value': 'ema'}, - {'label': 'Relative Strength Index (RSI)', 'value': 'rsi'}, - {'label': 'MACD', 'value': 'macd'}, - {'label': 'Bollinger Bands', 'value': 'bollinger_bands'}, - {'label': 'Stochastic Oscillator', 'value': 'stochastic'}, - ], - placeholder='Select indicator type', -) -``` - -### ✅ Step 5: Add Parameter Fields to Modal - -**File**: `dashboard/components/indicator_modal.py` - -In `create_parameter_fields`, add the `dcc.Input` components for your indicator's parameters. - -```python -def create_parameter_fields(): - return html.Div([ - # ... existing parameter 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 for %K and %D lines") - ], id='stochastic-parameters', style={'display': 'none'}, className="mb-3") - ]) -``` - -### ✅ Step 6: Update Parameter Visibility Callback - -**File**: `dashboard/callbacks/indicators.py` - -In `update_parameter_fields`, add an `Output` and logic to show/hide your new parameter fields. - -```python -@app.callback( - [Output('indicator-parameters-message', 'style'), - Output('sma-parameters', 'style'), - Output('ema-parameters', 'style'), - Output('rsi-parameters', 'style'), - Output('macd-parameters', 'style'), - Output('bb-parameters', 'style'), - Output('stochastic-parameters', 'style')], - Input('indicator-type-dropdown', 'value'), -) -def update_parameter_fields(indicator_type): - styles = { 'sma': {'display': 'none'}, 'ema': {'display': 'none'}, 'rsi': {'display': 'none'}, 'macd': {'display': 'none'}, 'bb': {'display': 'none'}, 'stochastic': {'display': 'none'} } - message_style = {'display': 'block'} if not indicator_type else {'display': 'none'} - if indicator_type: - styles[indicator_type] = {'display': 'block'} - return [message_style] + list(styles.values()) -``` - -### ✅ Step 7: Update Save Indicator Callback - -**File**: `dashboard/callbacks/indicators.py` - -In `save_new_indicator`, add `State` inputs for your parameters and logic to collect them. - -```python -@app.callback( - # ... Outputs ... - Input('save-indicator-btn', 'n_clicks'), - [# ... States ... - State('stochastic-k-period-input', 'value'), - State('stochastic-d-period-input', 'value'), - State('edit-indicator-store', 'data')], -) -def save_new_indicator(n_clicks, name, indicator_type, ..., stochastic_k, stochastic_d, edit_data): - # ... - elif indicator_type == 'stochastic': - parameters = {'k_period': stochastic_k or 14, 'd_period': stochastic_d or 3} - # ... -``` - -### ✅ Step 8: Update Edit Callback Parameters - -**File**: `dashboard/callbacks/indicators.py` - -In `edit_indicator`, add `Output`s for your parameter fields and logic to load values. - -```python -@app.callback( - [# ... Outputs ... - Output('stochastic-k-period-input', 'value'), - Output('stochastic-d-period-input', 'value')], - Input({'type': 'edit-indicator-btn', 'index': dash.ALL}, 'n_clicks'), -) -def edit_indicator(edit_clicks, button_ids): - # ... - stochastic_k, stochastic_d = 14, 3 - if indicator: - # ... - elif indicator.type == 'stochastic': - stochastic_k = params.get('k_period', 14) - stochastic_d = params.get('d_period', 3) - return (..., stochastic_k, stochastic_d) -``` - -### ✅ Step 9: Update Reset Callback - -**File**: `dashboard/callbacks/indicators.py` - -In `reset_modal_form`, add `Output`s for your parameter fields and their default values. - -```python -@app.callback( - [# ... Outputs ... - Output('stochastic-k-period-input', 'value', allow_duplicate=True), - Output('stochastic-d-period-input', 'value', allow_duplicate=True)], - Input('cancel-indicator-btn', 'n_clicks'), -) -def reset_modal_form(cancel_clicks): - # ... - return ..., 14, 3 -``` - -### ✅ Step 10: Create Default Template - -**File**: `components/charts/indicator_defaults.py` - -Create a default template for your indicator. - -```python -def create_stochastic_template() -> UserIndicator: - return UserIndicator( - id=f"stochastic_{generate_short_id()}", - name="Stochastic 14,3", - type="stochastic", - display_type="subplot", - parameters={"k_period": 14, "d_period": 3}, - styling=IndicatorStyling(color="#9c27b0", line_width=2) - ) - -DEFAULT_TEMPLATES = { - # ... - "stochastic": create_stochastic_template, -} -``` - -### ✅ Step 11: Add Calculation Function (Optional) - -**File**: `data/common/indicators.py` - -Add a standalone calculation function. - -```python -def calculate_stochastic(df: pd.DataFrame, k_period: int = 14, d_period: int = 3) -> tuple: - lowest_low = df['low'].rolling(window=k_period).min() - highest_high = df['high'].rolling(window=k_period).max() - k_percent = 100 * ((df['close'] - lowest_low) / (highest_high - lowest_low)) - d_percent = k_percent.rolling(window=d_period).mean() - return k_percent, d_percent -``` - -## File Change Summary - -When adding a new indicator, you'll typically modify these files: -1. **`components/charts/layers/indicators.py`** or **`subplots.py`** -2. **`components/charts/layers/__init__.py`** -3. **`dashboard/components/indicator_modal.py`** -4. **`dashboard/callbacks/indicators.py`** -5. **`components/charts/indicator_defaults.py`** -6. **`data/common/indicators.py`** (optional) \ No newline at end of file diff --git a/docs/modules/technical-indicators.md b/docs/modules/technical-indicators.md index b79607d..0ebd752 100644 --- a/docs/modules/technical-indicators.md +++ b/docs/modules/technical-indicators.md @@ -1,28 +1,228 @@ # Technical Indicators Module -The Technical Indicators module provides a suite of common technical analysis tools. It is designed to work efficiently with pandas DataFrames, which is the standard data structure for time-series analysis in the TCP Trading Platform. - ## Overview -The module has been refactored into a dedicated package structure under `data/common/indicators/`. All calculation methods now expect a pandas DataFrame with a `DatetimeIndex` and the required OHLCV columns (`open`, `high`, `low`, `close`, `volume`). This change simplifies the data pipeline, improves performance through vectorization, and ensures consistency across the platform. +The Technical Indicators module provides a modular, extensible system for calculating technical analysis indicators. It is designed to handle sparse OHLCV data efficiently, making it ideal for real-time trading applications. + +## Architecture ### Package Structure - ``` data/common/indicators/ -├── __init__.py # Package exports -├── technical.py # TechnicalIndicators class implementation -├── result.py # IndicatorResult dataclass -└── utils.py # Utility functions for configuration +├── __init__.py # Package exports +├── technical.py # Main facade class +├── base.py # Base indicator class +├── result.py # Result container class +├── utils.py # Utility functions +└── implementations/ # Individual indicator implementations + ├── __init__.py + ├── sma.py # Simple Moving Average + ├── ema.py # Exponential Moving Average + ├── rsi.py # Relative Strength Index + ├── macd.py # MACD + └── bollinger.py # Bollinger Bands ``` -The module implements five core technical indicators: +### Key Components -- **Simple Moving Average (SMA)** -- **Exponential Moving Average (EMA)** -- **Relative Strength Index (RSI)** -- **Moving Average Convergence Divergence (MACD)** -- **Bollinger Bands** +#### 1. Base Classes +- **BaseIndicator**: Abstract base class providing common functionality + - Data preparation + - Validation + - Error handling + - Logging + +#### 2. Individual Indicators +Each indicator is implemented as a separate class inheriting from `BaseIndicator`: +- Focused responsibility +- Independent testing +- Easy maintenance +- Clear documentation + +#### 3. TechnicalIndicators Facade +Main entry point providing: +- Unified interface +- Batch calculations +- Consistent error handling +- Data preparation + +## Supported Indicators + +### Simple Moving Average (SMA) +```python +from data.common.indicators import TechnicalIndicators + +indicators = TechnicalIndicators() +results = indicators.sma(df, period=20, price_column='close') +``` +- **Parameters**: + - `period`: Number of periods (default: 20) + - `price_column`: Column to average (default: 'close') + +### Exponential Moving Average (EMA) +```python +results = indicators.ema(df, period=12, price_column='close') +``` +- **Parameters**: + - `period`: Number of periods (default: 20) + - `price_column`: Column to average (default: 'close') + +### Relative Strength Index (RSI) +```python +results = indicators.rsi(df, period=14, price_column='close') +``` +- **Parameters**: + - `period`: Number of periods (default: 14) + - `price_column`: Column to analyze (default: 'close') + +### Moving Average Convergence Divergence (MACD) +```python +results = indicators.macd( + df, + fast_period=12, + slow_period=26, + signal_period=9, + price_column='close' +) +``` +- **Parameters**: + - `fast_period`: Fast EMA period (default: 12) + - `slow_period`: Slow EMA period (default: 26) + - `signal_period`: Signal line period (default: 9) + - `price_column`: Column to analyze (default: 'close') + +### Bollinger Bands +```python +results = indicators.bollinger_bands( + df, + period=20, + std_dev=2.0, + price_column='close' +) +``` +- **Parameters**: + - `period`: SMA period (default: 20) + - `std_dev`: Standard deviation multiplier (default: 2.0) + - `price_column`: Column to analyze (default: 'close') + +## Usage Examples + +### Basic Usage +```python +from data.common.indicators import TechnicalIndicators + +# Initialize calculator +indicators = TechnicalIndicators(logger=my_logger) + +# Calculate single indicator +sma_results = indicators.sma(df, period=20) + +# Access results +for result in sma_results: + print(f"Time: {result.timestamp}, SMA: {result.values['sma']}") +``` + +### Batch Calculations +```python +# Configure multiple indicators +config = { + 'sma_20': {'type': 'sma', 'period': 20}, + 'ema_12': {'type': 'ema', 'period': 12}, + 'rsi_14': {'type': 'rsi', 'period': 14}, + 'macd': { + 'type': 'macd', + 'fast_period': 12, + 'slow_period': 26, + 'signal_period': 9 + } +} + +# Calculate all at once +results = indicators.calculate_multiple_indicators(df, config) +``` + +### Dynamic Indicator Selection +```python +# Calculate any indicator by name +result = indicators.calculate( + 'macd', + df, + fast_period=12, + slow_period=26, + signal_period=9 +) +``` + +## Data Structures + +### IndicatorResult +```python +@dataclass +class IndicatorResult: + timestamp: datetime # Right-aligned timestamp + symbol: str # Trading symbol + timeframe: str # Candle timeframe + values: Dict[str, float] # Indicator values + metadata: Optional[Dict[str, Any]] = None # Calculation metadata +``` + +## Error Handling + +The module provides comprehensive error handling: +- Input validation +- Data sufficiency checks +- Calculation error handling +- Detailed error logging + +Example: +```python +try: + results = indicators.rsi(df, period=14) +except Exception as e: + logger.error(f"RSI calculation failed: {e}") + results = [] +``` + +## Performance Considerations + +1. **Data Preparation** + - Uses pandas for vectorized calculations + - Handles sparse data efficiently + - Maintains timestamp alignment + +2. **Memory Usage** + - Avoids unnecessary data copies + - Cleans up temporary calculations + - Uses efficient data structures + +3. **Calculation Optimization** + - Vectorized operations where possible + - Minimal data transformations + - Efficient algorithm implementations + +## Testing + +The module includes comprehensive tests: +- Unit tests for each indicator +- Integration tests for the facade +- Edge case handling +- Performance benchmarks + +Run tests with: +```bash +uv run pytest tests/test_indicators.py +``` + +## Contributing + +When adding new indicators: +1. Create a new class in `implementations/` +2. Inherit from `BaseIndicator` +3. Implement the `calculate` method +4. Add tests +5. Update documentation + +See [Adding New Indicators](./adding-new-indicators.md) for detailed instructions. ## Key Features @@ -136,153 +336,4 @@ The following details the parameters and the columns returned in the result Data ### Bollinger Bands - **Parameters**: `period` (int), `std_dev` (float), `price_column` (str, default: 'close') -- **Returned Columns**: `upper_band`, `middle_band`, `lower_band` - -## Data Structures - -### IndicatorResult - -The `IndicatorResult` class (from `data.common.indicators.result`) contains technical indicator calculation results: - -```python -@dataclass -class IndicatorResult: - timestamp: datetime # Right-aligned candle timestamp - symbol: str # Trading symbol (e.g., 'BTC-USDT') - timeframe: str # Candle timeframe (e.g., '1m', '5m') - values: Dict[str, float] # Indicator values - metadata: Optional[Dict[str, Any]] = None # Calculation metadata -``` - -### Configuration Management - -The module provides utilities for managing indicator configurations (from `data.common.indicators.utils`): - -```python -# Create default configurations -config = create_default_indicators_config() - -# Validate a configuration -is_valid = validate_indicator_config({ - 'type': 'sma', - 'period': 20, - 'price_column': 'close' -}) -``` - -### Integration with TCP Platform - -The indicators module is designed to work seamlessly with the platform's components: - -```python -from data.common.indicators import TechnicalIndicators -from data.common.data_types import OHLCVCandle -from components.charts.utils import prepare_chart_data - -# Initialize calculator -indicators = TechnicalIndicators() - -# Calculate indicators -results = indicators.calculate_multiple_indicators(df, { - 'sma_20': {'type': 'sma', 'period': 20}, - 'rsi_14': {'type': 'rsi', 'period': 14} -}) - -# Access results -for indicator_name, indicator_results in results.items(): - for result in indicator_results: - print(f"{indicator_name}: {result.values}") -``` - -## Integration with the TCP Platform - -The refactored `TechnicalIndicators` module is now tightly integrated with the `ChartBuilder`, which handles all data preparation and calculation automatically when indicators are added to a chart. For custom analysis or strategy development, you can use the class directly as shown in the examples above. The key is to always start with a properly prepared DataFrame using `prepare_chart_data`. - -## Performance Considerations - -### Memory Usage -- Process indicators in batches for large datasets -- Use appropriate period lengths to balance accuracy and performance -- Consider data retention policies for historical indicator values - -### Calculation Frequency -- Calculate indicators only when new complete candles are available -- Cache recent indicator values to avoid recalculation -- Use incremental updates for real-time scenarios - -### Optimization Tips -- Use `calculate_multiple_indicators()` for efficiency when computing multiple indicators -- Limit the number of historical candles to what's actually needed -- Consider using different timeframes for different indicators - -## Error Handling - -The module includes comprehensive error handling: - -- **Insufficient Data**: Returns empty results when not enough data is available -- **Invalid Configuration**: Validates configuration parameters before calculation -- **Data Quality Issues**: Handles NaN values and missing data gracefully -- **Type Errors**: Converts data types safely with fallback values - -## Testing - -The module includes comprehensive unit tests covering: - -- All indicator calculations with known expected values -- Sparse data handling scenarios -- Edge cases (insufficient data, invalid parameters) -- Configuration validation -- Multiple indicator batch processing - -Run tests with: -```bash -uv run pytest tests/test_indicators.py -v -``` - -## Future Enhancements - -Potential future additions to the indicators module: - -- **Additional Indicators**: Stochastic, Williams %R, Commodity Channel Index -- **Custom Indicators**: Framework for user-defined indicators -- **Performance Metrics**: Calculation timing and memory usage statistics -- **Streaming Updates**: Incremental indicator updates for real-time scenarios -- **Parallel Processing**: Multi-threaded calculation for large datasets - -## See Also - -- [Aggregation Strategy Documentation](aggregation-strategy.md) -- [Data Types Documentation](data-types.md) -- [Database Schema Documentation](database-schema.md) -- [API Reference](api-reference.md) - -## `TechnicalIndicators` Class - -The main class for calculating technical indicators. - -- **RSI**: `rsi(df, period=14, price_column='close')` -- **MACD**: `macd(df, fast_period=12, slow_period=26, signal_period=9, price_column='close')` -- **Bollinger Bands**: `bollinger_bands(df, period=20, std_dev=2.0, price_column='close')` - -### `calculate_multiple_indicators` - -Calculates multiple indicators in a single pass for efficiency. - -```python -# Configuration for multiple indicators -indicators_config = { - 'sma_20': {'type': 'sma', 'period': 20}, - 'ema_50': {'type': 'ema', 'period': 50}, - 'rsi_14': {'type': 'rsi', 'period': 14} -} - -# Calculate all indicators -all_results = ti.calculate_multiple_indicators(candles, indicators_config) - -print(f"SMA results: {len(all_results['sma_20'])}") -print(f"RSI results: {len(all_results['rsi_14'])}") -``` - -## Sparse Data Handling - -The `TechnicalIndicators` class is designed to handle sparse OHLCV data, which is a common scenario in real-time data collection. \ No newline at end of file +- **Returned Columns**: `upper_band`, ` \ No newline at end of file