- Introduced a comprehensive framework for incremental trading strategies, including modules for strategy execution, backtesting, and data processing. - Added key components such as `IncTrader`, `IncBacktester`, and various trading strategies (e.g., `MetaTrendStrategy`, `BBRSStrategy`, `RandomStrategy`) to facilitate real-time trading and backtesting. - Implemented a robust backtesting framework with configuration management, parallel execution, and result analysis capabilities. - Developed an incremental indicators framework to support real-time data processing with constant memory usage. - Enhanced documentation to provide clear usage examples and architecture overview, ensuring maintainability and ease of understanding for future development. - Ensured compatibility with existing strategies and maintained a focus on performance and scalability throughout the implementation.
207 lines
7.6 KiB
Python
207 lines
7.6 KiB
Python
"""
|
|
Backtester Configuration
|
|
|
|
This module provides configuration classes and utilities for backtesting
|
|
incremental trading strategies.
|
|
"""
|
|
|
|
import os
|
|
import pandas as pd
|
|
from dataclasses import dataclass
|
|
from typing import Optional, Dict, Any, List
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class BacktestConfig:
|
|
"""
|
|
Configuration for backtesting runs.
|
|
|
|
This class encapsulates all configuration parameters needed for running
|
|
backtests, including data settings, trading parameters, and performance options.
|
|
|
|
Attributes:
|
|
data_file: Path to the data file (relative to data directory)
|
|
start_date: Start date for backtesting (YYYY-MM-DD format)
|
|
end_date: End date for backtesting (YYYY-MM-DD format)
|
|
initial_usd: Initial USD balance for trading
|
|
timeframe: Data timeframe (e.g., "1min", "5min", "15min")
|
|
stop_loss_pct: Default stop loss percentage (0.0 to disable)
|
|
take_profit_pct: Default take profit percentage (0.0 to disable)
|
|
max_workers: Maximum number of worker processes for parallel execution
|
|
chunk_size: Chunk size for data processing
|
|
data_dir: Directory containing data files
|
|
results_dir: Directory for saving results
|
|
|
|
Example:
|
|
config = BacktestConfig(
|
|
data_file="btc_1min_2023.csv",
|
|
start_date="2023-01-01",
|
|
end_date="2023-12-31",
|
|
initial_usd=10000,
|
|
stop_loss_pct=0.02
|
|
)
|
|
"""
|
|
data_file: str
|
|
start_date: str
|
|
end_date: str
|
|
initial_usd: float = 10000
|
|
timeframe: str = "1min"
|
|
|
|
# Risk management parameters
|
|
stop_loss_pct: float = 0.0
|
|
take_profit_pct: float = 0.0
|
|
|
|
# Performance settings
|
|
max_workers: Optional[int] = None
|
|
chunk_size: int = 1000
|
|
|
|
# Directory settings
|
|
data_dir: str = "data"
|
|
results_dir: str = "results"
|
|
|
|
def __post_init__(self):
|
|
"""Validate configuration after initialization."""
|
|
self._validate_config()
|
|
self._ensure_directories()
|
|
|
|
def _validate_config(self):
|
|
"""Validate configuration parameters."""
|
|
# Validate dates
|
|
try:
|
|
start_dt = pd.to_datetime(self.start_date)
|
|
end_dt = pd.to_datetime(self.end_date)
|
|
if start_dt >= end_dt:
|
|
raise ValueError("start_date must be before end_date")
|
|
except Exception as e:
|
|
raise ValueError(f"Invalid date format: {e}")
|
|
|
|
# Validate financial parameters
|
|
if self.initial_usd <= 0:
|
|
raise ValueError("initial_usd must be positive")
|
|
|
|
if not (0 <= self.stop_loss_pct <= 1):
|
|
raise ValueError("stop_loss_pct must be between 0 and 1")
|
|
|
|
if not (0 <= self.take_profit_pct <= 1):
|
|
raise ValueError("take_profit_pct must be between 0 and 1")
|
|
|
|
# Validate performance parameters
|
|
if self.max_workers is not None and self.max_workers <= 0:
|
|
raise ValueError("max_workers must be positive")
|
|
|
|
if self.chunk_size <= 0:
|
|
raise ValueError("chunk_size must be positive")
|
|
|
|
def _ensure_directories(self):
|
|
"""Ensure required directories exist."""
|
|
os.makedirs(self.data_dir, exist_ok=True)
|
|
os.makedirs(self.results_dir, exist_ok=True)
|
|
|
|
def get_data_path(self) -> str:
|
|
"""Get full path to data file."""
|
|
return os.path.join(self.data_dir, self.data_file)
|
|
|
|
def get_results_path(self, filename: str) -> str:
|
|
"""Get full path for results file."""
|
|
return os.path.join(self.results_dir, filename)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert configuration to dictionary."""
|
|
return {
|
|
"data_file": self.data_file,
|
|
"start_date": self.start_date,
|
|
"end_date": self.end_date,
|
|
"initial_usd": self.initial_usd,
|
|
"timeframe": self.timeframe,
|
|
"stop_loss_pct": self.stop_loss_pct,
|
|
"take_profit_pct": self.take_profit_pct,
|
|
"max_workers": self.max_workers,
|
|
"chunk_size": self.chunk_size,
|
|
"data_dir": self.data_dir,
|
|
"results_dir": self.results_dir
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, config_dict: Dict[str, Any]) -> 'BacktestConfig':
|
|
"""Create configuration from dictionary."""
|
|
return cls(**config_dict)
|
|
|
|
def copy(self, **kwargs) -> 'BacktestConfig':
|
|
"""Create a copy of the configuration with optional parameter overrides."""
|
|
config_dict = self.to_dict()
|
|
config_dict.update(kwargs)
|
|
return self.from_dict(config_dict)
|
|
|
|
def __repr__(self) -> str:
|
|
"""String representation of the configuration."""
|
|
return (f"BacktestConfig(data_file={self.data_file}, "
|
|
f"date_range={self.start_date} to {self.end_date}, "
|
|
f"initial_usd=${self.initial_usd})")
|
|
|
|
|
|
class OptimizationConfig:
|
|
"""
|
|
Configuration for parameter optimization runs.
|
|
|
|
This class provides additional configuration options specifically for
|
|
parameter optimization and grid search operations.
|
|
"""
|
|
|
|
def __init__(self,
|
|
base_config: BacktestConfig,
|
|
strategy_param_grid: Dict[str, List],
|
|
trader_param_grid: Optional[Dict[str, List]] = None,
|
|
max_workers: Optional[int] = None,
|
|
save_individual_results: bool = True,
|
|
save_detailed_logs: bool = False):
|
|
"""
|
|
Initialize optimization configuration.
|
|
|
|
Args:
|
|
base_config: Base backtesting configuration
|
|
strategy_param_grid: Grid of strategy parameters to test
|
|
trader_param_grid: Grid of trader parameters to test
|
|
max_workers: Maximum number of worker processes
|
|
save_individual_results: Whether to save individual strategy results
|
|
save_detailed_logs: Whether to save detailed action logs
|
|
"""
|
|
self.base_config = base_config
|
|
self.strategy_param_grid = strategy_param_grid
|
|
self.trader_param_grid = trader_param_grid or {}
|
|
self.max_workers = max_workers
|
|
self.save_individual_results = save_individual_results
|
|
self.save_detailed_logs = save_detailed_logs
|
|
|
|
def get_total_combinations(self) -> int:
|
|
"""Calculate total number of parameter combinations."""
|
|
from itertools import product
|
|
|
|
# Calculate strategy combinations
|
|
strategy_values = list(self.strategy_param_grid.values())
|
|
strategy_combinations = len(list(product(*strategy_values))) if strategy_values else 1
|
|
|
|
# Calculate trader combinations
|
|
trader_values = list(self.trader_param_grid.values())
|
|
trader_combinations = len(list(product(*trader_values))) if trader_values else 1
|
|
|
|
return strategy_combinations * trader_combinations
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert optimization configuration to dictionary."""
|
|
return {
|
|
"base_config": self.base_config.to_dict(),
|
|
"strategy_param_grid": self.strategy_param_grid,
|
|
"trader_param_grid": self.trader_param_grid,
|
|
"max_workers": self.max_workers,
|
|
"save_individual_results": self.save_individual_results,
|
|
"save_detailed_logs": self.save_detailed_logs,
|
|
"total_combinations": self.get_total_combinations()
|
|
}
|
|
|
|
def __repr__(self) -> str:
|
|
"""String representation of the optimization configuration."""
|
|
return (f"OptimizationConfig(combinations={self.get_total_combinations()}, "
|
|
f"max_workers={self.max_workers})") |