339 lines
9.1 KiB
Markdown
339 lines
9.1 KiB
Markdown
# Technical Indicators Module
|
|
|
|
## Overview
|
|
|
|
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 # 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
|
|
```
|
|
|
|
### Key Components
|
|
|
|
#### 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
|
|
|
|
- **DataFrame-Centric Design**: Operates directly on pandas DataFrames for performance and simplicity.
|
|
- **Vectorized Calculations**: Leverages pandas and numpy for high-speed computation.
|
|
- **Flexible `calculate` Method**: A single entry point for calculating any supported indicator by name.
|
|
- **Standardized Output**: All methods return a DataFrame containing the calculated indicator values, indexed by timestamp.
|
|
- **Modular Architecture**: Clear separation between calculation logic, result types, and utilities.
|
|
|
|
## Usage Examples
|
|
|
|
### Importing the Required Components
|
|
|
|
```python
|
|
from data.common.indicators import (
|
|
TechnicalIndicators,
|
|
IndicatorResult,
|
|
create_default_indicators_config,
|
|
validate_indicator_config
|
|
)
|
|
from data.common.data_types import OHLCVCandle
|
|
```
|
|
|
|
### Preparing the DataFrame
|
|
|
|
Before you can calculate indicators, you need a properly formatted pandas DataFrame. The `prepare_chart_data` utility is the recommended way to create one from a list of candle dictionaries.
|
|
|
|
```python
|
|
from components.charts.utils import prepare_chart_data
|
|
from data.common.indicators import TechnicalIndicators
|
|
|
|
# Assume 'candles' is a list of OHLCV dictionaries from the database
|
|
# candles = fetch_market_data(...)
|
|
|
|
# Prepare the DataFrame
|
|
df = prepare_chart_data(candles)
|
|
|
|
# df is now ready for indicator calculations
|
|
# It has a DatetimeIndex and the necessary OHLCV columns.
|
|
```
|
|
|
|
### Basic Indicator Calculation
|
|
|
|
Once you have a prepared DataFrame, you can calculate indicators directly.
|
|
|
|
```python
|
|
# Initialize the calculator
|
|
indicators = TechnicalIndicators()
|
|
|
|
# Calculate a Simple Moving Average
|
|
sma_df = indicators.sma(df, period=20)
|
|
|
|
# Calculate an Exponential Moving Average
|
|
ema_df = indicators.ema(df, period=12)
|
|
|
|
# sma_df and ema_df are pandas DataFrames containing the results.
|
|
```
|
|
|
|
### Using the `calculate` Method
|
|
|
|
The most flexible way to compute an indicator is with the `calculate` method, which accepts the indicator type as a string.
|
|
|
|
```python
|
|
# Calculate RSI using the generic method
|
|
rsi_pkg = indicators.calculate('rsi', df, period=14)
|
|
if rsi_pkg:
|
|
rsi_df = rsi_pkg['data']
|
|
|
|
# Calculate MACD with custom parameters
|
|
macd_pkg = indicators.calculate('macd', df, fast_period=10, slow_period=30, signal_period=8)
|
|
if macd_pkg:
|
|
macd_df = macd_pkg['data']
|
|
```
|
|
|
|
### Using Different Price Columns
|
|
|
|
You can specify which price column (`open`, `high`, `low`, or `close`) to use for the calculation.
|
|
|
|
```python
|
|
# Calculate SMA on the 'high' price
|
|
sma_high_df = indicators.sma(df, period=20, price_column='high')
|
|
|
|
# Calculate RSI on the 'open' price
|
|
rsi_open_pkg = indicators.calculate('rsi', df, period=14, price_column='open')
|
|
```
|
|
|
|
## Indicator Details
|
|
|
|
The following details the parameters and the columns returned in the result DataFrame for each indicator.
|
|
|
|
### Simple Moving Average (SMA)
|
|
|
|
- **Parameters**: `period` (int), `price_column` (str, default: 'close')
|
|
- **Returned Columns**: `sma`
|
|
|
|
### Exponential Moving Average (EMA)
|
|
|
|
- **Parameters**: `period` (int), `price_column` (str, default: 'close')
|
|
- **Returned Columns**: `ema`
|
|
|
|
### Relative Strength Index (RSI)
|
|
|
|
- **Parameters**: `period` (int), `price_column` (str, default: 'close')
|
|
- **Returned Columns**: `rsi`
|
|
|
|
### MACD (Moving Average Convergence Divergence)
|
|
|
|
- **Parameters**: `fast_period` (int), `slow_period` (int), `signal_period` (int), `price_column` (str, default: 'close')
|
|
- **Returned Columns**: `macd`, `signal`, `histogram`
|
|
|
|
### Bollinger Bands
|
|
|
|
- **Parameters**: `period` (int), `std_dev` (float), `price_column` (str, default: 'close')
|
|
- **Returned Columns**: `upper_band`, ` |