""" Configuration Manager This module provides centralized configuration handling for the backtesting system. It handles loading, validation, and provides a clean interface for accessing configuration data. """ import json import datetime import logging from typing import Dict, List, Optional, Any from pathlib import Path class ConfigManager: """ Manages configuration loading, validation, and access. Provides a centralized way to handle configuration files with validation and convenient access methods. """ def __init__(self, config_path: Optional[str] = None): """ Initialize configuration manager. Args: config_path: Path to configuration file. If None, uses default. """ self.config_path = config_path or "configs/config_default.json" self.config = self._load_config() self._validate_config() def _load_config(self) -> Dict[str, Any]: """Load configuration from file.""" try: with open(self.config_path, 'r') as f: config = json.load(f) logging.info(f"Loaded configuration from: {self.config_path}") return config except FileNotFoundError: available_configs = list(Path("configs").glob("*.json")) raise FileNotFoundError( f"Config file '{self.config_path}' not found. " f"Available configs: {[str(c) for c in available_configs]}" ) except json.JSONDecodeError as e: raise ValueError(f"Invalid JSON in config file '{self.config_path}': {e}") def _validate_config(self) -> None: """Validate configuration structure and values.""" required_fields = ['start_date', 'initial_usd', 'timeframes', 'strategies'] for field in required_fields: if field not in self.config: raise ValueError(f"Missing required field '{field}' in configuration") # Validate strategies if not self.config['strategies']: raise ValueError("At least one strategy must be specified") for strategy in self.config['strategies']: if 'name' not in strategy: raise ValueError("Strategy must have a 'name' field") # Validate timeframes if not self.config['timeframes']: raise ValueError("At least one timeframe must be specified") logging.info("Configuration validation successful") @property def start_date(self) -> str: """Get start date.""" return self.config['start_date'] @property def stop_date(self) -> str: """Get stop date, defaulting to current date if None.""" stop_date = self.config.get('stop_date') if stop_date is None: return datetime.datetime.now().strftime("%Y-%m-%d") return stop_date @property def initial_usd(self) -> float: """Get initial USD amount.""" return self.config['initial_usd'] @property def timeframes(self) -> List[str]: """Get list of timeframes to test.""" return self.config['timeframes'] @property def strategies_config(self) -> List[Dict[str, Any]]: """Get strategies configuration.""" return self.config['strategies'] @property def combination_rules(self) -> Dict[str, Any]: """Get combination rules for strategy manager.""" return self.config.get('combination_rules', { "entry": "any", "exit": "any", "min_confidence": 0.5 }) def get_strategy_manager_config(self) -> Dict[str, Any]: """Get configuration for strategy manager.""" return { "strategies": self.strategies_config, "combination_rules": self.combination_rules } def get_timeframe_task_config(self, timeframe: str) -> Dict[str, Any]: """Get configuration for a specific timeframe task.""" return { "initial_usd": self.initial_usd, "strategies": self.strategies_config, "combination_rules": self.combination_rules } def __repr__(self) -> str: """String representation of configuration.""" return (f"ConfigManager(config_path='{self.config_path}', " f"strategies={len(self.strategies_config)}, " f"timeframes={len(self.timeframes)})")