3.4 Enhance logging and modular chart system for Crypto Trading Bot Dashboard
- Suppressed SQLAlchemy logging in `app.py` and `main.py` to reduce console verbosity. - Introduced a new modular chart system in `components/charts/` with a `ChartBuilder` class for flexible chart creation. - Added utility functions for data processing and validation in `components/charts/utils.py`. - Implemented indicator definitions and configurations in `components/charts/config/indicator_defs.py`. - Created a comprehensive documentation structure for the new chart system, ensuring clarity and maintainability. - Added unit tests for the `ChartBuilder` class to verify functionality and robustness. - Updated existing components to integrate with the new chart system, enhancing overall architecture and user experience.
This commit is contained in:
38
components/charts/config/__init__.py
Normal file
38
components/charts/config/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
Chart Configuration Package
|
||||
|
||||
This package contains configuration management for the modular chart system,
|
||||
including indicator definitions, strategy-specific configurations, and defaults.
|
||||
"""
|
||||
|
||||
from .indicator_defs import (
|
||||
INDICATOR_DEFINITIONS,
|
||||
ChartIndicatorConfig,
|
||||
calculate_indicators,
|
||||
convert_database_candles_to_ohlcv,
|
||||
get_indicator_display_config,
|
||||
get_available_indicators,
|
||||
get_overlay_indicators,
|
||||
get_subplot_indicators,
|
||||
get_default_indicator_params
|
||||
)
|
||||
|
||||
# Package metadata
|
||||
__version__ = "0.1.0"
|
||||
__package_name__ = "config"
|
||||
|
||||
# Public exports
|
||||
__all__ = [
|
||||
"INDICATOR_DEFINITIONS",
|
||||
"ChartIndicatorConfig",
|
||||
"calculate_indicators",
|
||||
"convert_database_candles_to_ohlcv",
|
||||
"get_indicator_display_config",
|
||||
"get_available_indicators",
|
||||
"get_overlay_indicators",
|
||||
"get_subplot_indicators",
|
||||
"get_default_indicator_params"
|
||||
]
|
||||
|
||||
# Legacy function names for backward compatibility
|
||||
validate_indicator_config = get_default_indicator_params # Will be properly implemented in future tasks
|
||||
266
components/charts/config/indicator_defs.py
Normal file
266
components/charts/config/indicator_defs.py
Normal file
@@ -0,0 +1,266 @@
|
||||
"""
|
||||
Indicator Definitions and Configuration
|
||||
|
||||
This module defines indicator configurations and provides integration
|
||||
with the existing data/common/indicators.py technical indicators module.
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Any, Optional, Union
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
|
||||
from data.common.indicators import TechnicalIndicators, IndicatorResult, create_default_indicators_config, validate_indicator_config
|
||||
from data.common.data_types import OHLCVCandle
|
||||
from utils.logger import get_logger
|
||||
|
||||
# Initialize logger
|
||||
logger = get_logger("indicator_defs")
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChartIndicatorConfig:
|
||||
"""
|
||||
Configuration for chart indicators with display properties.
|
||||
|
||||
Extends the base indicator config with chart-specific properties
|
||||
like colors, line styles, and subplot placement.
|
||||
"""
|
||||
name: str
|
||||
indicator_type: str
|
||||
parameters: Dict[str, Any]
|
||||
display_type: str # 'overlay', 'subplot'
|
||||
color: str
|
||||
line_style: str = 'solid' # 'solid', 'dash', 'dot'
|
||||
line_width: int = 2
|
||||
opacity: float = 1.0
|
||||
visible: bool = True
|
||||
subplot_height_ratio: float = 0.3 # For subplot indicators
|
||||
|
||||
def to_indicator_config(self) -> Dict[str, Any]:
|
||||
"""Convert to format expected by TechnicalIndicators."""
|
||||
config = {'type': self.indicator_type}
|
||||
config.update(self.parameters)
|
||||
return config
|
||||
|
||||
|
||||
# Built-in indicator definitions with chart display properties
|
||||
INDICATOR_DEFINITIONS = {
|
||||
'sma_20': ChartIndicatorConfig(
|
||||
name='SMA (20)',
|
||||
indicator_type='sma',
|
||||
parameters={'period': 20, 'price_column': 'close'},
|
||||
display_type='overlay',
|
||||
color='#007bff',
|
||||
line_width=2
|
||||
),
|
||||
'sma_50': ChartIndicatorConfig(
|
||||
name='SMA (50)',
|
||||
indicator_type='sma',
|
||||
parameters={'period': 50, 'price_column': 'close'},
|
||||
display_type='overlay',
|
||||
color='#28a745',
|
||||
line_width=2
|
||||
),
|
||||
'ema_12': ChartIndicatorConfig(
|
||||
name='EMA (12)',
|
||||
indicator_type='ema',
|
||||
parameters={'period': 12, 'price_column': 'close'},
|
||||
display_type='overlay',
|
||||
color='#ff6b35',
|
||||
line_width=2
|
||||
),
|
||||
'ema_26': ChartIndicatorConfig(
|
||||
name='EMA (26)',
|
||||
indicator_type='ema',
|
||||
parameters={'period': 26, 'price_column': 'close'},
|
||||
display_type='overlay',
|
||||
color='#dc3545',
|
||||
line_width=2
|
||||
),
|
||||
'rsi_14': ChartIndicatorConfig(
|
||||
name='RSI (14)',
|
||||
indicator_type='rsi',
|
||||
parameters={'period': 14, 'price_column': 'close'},
|
||||
display_type='subplot',
|
||||
color='#20c997',
|
||||
line_width=2,
|
||||
subplot_height_ratio=0.25
|
||||
),
|
||||
'macd_default': ChartIndicatorConfig(
|
||||
name='MACD',
|
||||
indicator_type='macd',
|
||||
parameters={'fast_period': 12, 'slow_period': 26, 'signal_period': 9, 'price_column': 'close'},
|
||||
display_type='subplot',
|
||||
color='#fd7e14',
|
||||
line_width=2,
|
||||
subplot_height_ratio=0.3
|
||||
),
|
||||
'bollinger_bands': ChartIndicatorConfig(
|
||||
name='Bollinger Bands',
|
||||
indicator_type='bollinger_bands',
|
||||
parameters={'period': 20, 'std_dev': 2.0, 'price_column': 'close'},
|
||||
display_type='overlay',
|
||||
color='#6f42c1',
|
||||
line_width=1,
|
||||
opacity=0.7
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def convert_database_candles_to_ohlcv(candles: List[Dict[str, Any]]) -> List[OHLCVCandle]:
|
||||
"""
|
||||
Convert database candle dictionaries to OHLCVCandle objects.
|
||||
|
||||
Args:
|
||||
candles: List of candle dictionaries from database operations
|
||||
|
||||
Returns:
|
||||
List of OHLCVCandle objects for technical indicators
|
||||
"""
|
||||
ohlcv_candles = []
|
||||
|
||||
for candle in candles:
|
||||
try:
|
||||
# Handle timestamp conversion
|
||||
timestamp = candle['timestamp']
|
||||
if isinstance(timestamp, str):
|
||||
timestamp = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
||||
elif timestamp.tzinfo is None:
|
||||
timestamp = timestamp.replace(tzinfo=timezone.utc)
|
||||
|
||||
# For database candles, start_time and end_time are the same
|
||||
# as we store right-aligned timestamps
|
||||
ohlcv_candle = OHLCVCandle(
|
||||
symbol=candle['symbol'],
|
||||
timeframe=candle['timeframe'],
|
||||
start_time=timestamp,
|
||||
end_time=timestamp,
|
||||
open=Decimal(str(candle['open'])),
|
||||
high=Decimal(str(candle['high'])),
|
||||
low=Decimal(str(candle['low'])),
|
||||
close=Decimal(str(candle['close'])),
|
||||
volume=Decimal(str(candle.get('volume', 0))),
|
||||
trade_count=candle.get('trades_count', 0),
|
||||
exchange=candle.get('exchange', 'okx'),
|
||||
is_complete=True
|
||||
)
|
||||
ohlcv_candles.append(ohlcv_candle)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error converting candle to OHLCV: {e}")
|
||||
continue
|
||||
|
||||
logger.debug(f"Converted {len(ohlcv_candles)} database candles to OHLCV format")
|
||||
return ohlcv_candles
|
||||
|
||||
|
||||
def calculate_indicators(candles: List[Dict[str, Any]],
|
||||
indicator_configs: List[str],
|
||||
custom_configs: Optional[Dict[str, ChartIndicatorConfig]] = None) -> Dict[str, List[IndicatorResult]]:
|
||||
"""
|
||||
Calculate technical indicators for chart display.
|
||||
|
||||
Args:
|
||||
candles: List of candle dictionaries from database
|
||||
indicator_configs: List of indicator names to calculate
|
||||
custom_configs: Optional custom indicator configurations
|
||||
|
||||
Returns:
|
||||
Dictionary mapping indicator names to their calculation results
|
||||
"""
|
||||
if not candles:
|
||||
logger.warning("No candles provided for indicator calculation")
|
||||
return {}
|
||||
|
||||
# Convert to OHLCV format
|
||||
ohlcv_candles = convert_database_candles_to_ohlcv(candles)
|
||||
if not ohlcv_candles:
|
||||
logger.error("Failed to convert candles to OHLCV format")
|
||||
return {}
|
||||
|
||||
# Initialize technical indicators calculator
|
||||
indicators_calc = TechnicalIndicators(logger)
|
||||
|
||||
# Prepare configurations
|
||||
configs_to_calculate = {}
|
||||
all_configs = {**INDICATOR_DEFINITIONS}
|
||||
if custom_configs:
|
||||
all_configs.update(custom_configs)
|
||||
|
||||
for indicator_name in indicator_configs:
|
||||
if indicator_name in all_configs:
|
||||
chart_config = all_configs[indicator_name]
|
||||
configs_to_calculate[indicator_name] = chart_config.to_indicator_config()
|
||||
else:
|
||||
logger.warning(f"Unknown indicator configuration: {indicator_name}")
|
||||
|
||||
if not configs_to_calculate:
|
||||
logger.warning("No valid indicator configurations found")
|
||||
return {}
|
||||
|
||||
# Calculate indicators
|
||||
try:
|
||||
results = indicators_calc.calculate_multiple_indicators(ohlcv_candles, configs_to_calculate)
|
||||
logger.debug(f"Calculated {len(results)} indicators successfully")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating indicators: {e}")
|
||||
return {}
|
||||
|
||||
|
||||
def get_indicator_display_config(indicator_name: str) -> Optional[ChartIndicatorConfig]:
|
||||
"""
|
||||
Get display configuration for an indicator.
|
||||
|
||||
Args:
|
||||
indicator_name: Name of the indicator
|
||||
|
||||
Returns:
|
||||
Chart indicator configuration or None if not found
|
||||
"""
|
||||
return INDICATOR_DEFINITIONS.get(indicator_name)
|
||||
|
||||
|
||||
def get_available_indicators() -> Dict[str, str]:
|
||||
"""
|
||||
Get list of available indicators with descriptions.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping indicator names to descriptions
|
||||
"""
|
||||
return {name: config.name for name, config in INDICATOR_DEFINITIONS.items()}
|
||||
|
||||
|
||||
def get_overlay_indicators() -> List[str]:
|
||||
"""Get list of indicators that display as overlays on the price chart."""
|
||||
return [name for name, config in INDICATOR_DEFINITIONS.items()
|
||||
if config.display_type == 'overlay']
|
||||
|
||||
|
||||
def get_subplot_indicators() -> List[str]:
|
||||
"""Get list of indicators that display in separate subplots."""
|
||||
return [name for name, config in INDICATOR_DEFINITIONS.items()
|
||||
if config.display_type == 'subplot']
|
||||
|
||||
|
||||
def get_default_indicator_params(indicator_type: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get default parameters for an indicator type.
|
||||
|
||||
Args:
|
||||
indicator_type: Type of indicator ('sma', 'ema', 'rsi', etc.)
|
||||
|
||||
Returns:
|
||||
Dictionary of default parameters
|
||||
"""
|
||||
defaults = {
|
||||
'sma': {'period': 20, 'price_column': 'close'},
|
||||
'ema': {'period': 20, 'price_column': 'close'},
|
||||
'rsi': {'period': 14, 'price_column': 'close'},
|
||||
'macd': {'fast_period': 12, 'slow_period': 26, 'signal_period': 9, 'price_column': 'close'},
|
||||
'bollinger_bands': {'period': 20, 'std_dev': 2.0, 'price_column': 'close'}
|
||||
}
|
||||
|
||||
return defaults.get(indicator_type, {})
|
||||
Reference in New Issue
Block a user