- 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.
197 lines
6.8 KiB
Python
197 lines
6.8 KiB
Python
"""
|
|
Base Indicator State Class
|
|
|
|
This module contains the abstract base class for all incremental indicator states.
|
|
All indicator implementations must inherit from IndicatorState and implement
|
|
the required methods for incremental calculation.
|
|
"""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import Any, Dict, Optional, Union
|
|
import numpy as np
|
|
|
|
|
|
class IndicatorState(ABC):
|
|
"""
|
|
Abstract base class for maintaining indicator calculation state.
|
|
|
|
This class defines the interface that all incremental indicators must implement.
|
|
Indicators maintain their internal state and can be updated incrementally with
|
|
new data points, providing constant memory usage and high performance.
|
|
|
|
Attributes:
|
|
period (int): The period/window size for the indicator
|
|
values_received (int): Number of values processed so far
|
|
is_initialized (bool): Whether the indicator has been initialized
|
|
|
|
Example:
|
|
class MyIndicator(IndicatorState):
|
|
def __init__(self, period: int):
|
|
super().__init__(period)
|
|
self._sum = 0.0
|
|
|
|
def update(self, new_value: float) -> float:
|
|
self._sum += new_value
|
|
self.values_received += 1
|
|
return self._sum / min(self.values_received, self.period)
|
|
"""
|
|
|
|
def __init__(self, period: int):
|
|
"""
|
|
Initialize the indicator state.
|
|
|
|
Args:
|
|
period: The period/window size for the indicator calculation
|
|
|
|
Raises:
|
|
ValueError: If period is not a positive integer
|
|
"""
|
|
if not isinstance(period, int) or period <= 0:
|
|
raise ValueError(f"Period must be a positive integer, got {period}")
|
|
|
|
self.period = period
|
|
self.values_received = 0
|
|
self.is_initialized = False
|
|
|
|
@abstractmethod
|
|
def update(self, new_value: Union[float, Dict[str, float]]) -> Union[float, Dict[str, float]]:
|
|
"""
|
|
Update indicator with new value and return current indicator value.
|
|
|
|
This method processes a new data point and updates the internal state
|
|
of the indicator. It returns the current indicator value after the update.
|
|
|
|
Args:
|
|
new_value: New data point (can be single value or OHLCV dict)
|
|
|
|
Returns:
|
|
Current indicator value after update (single value or dict)
|
|
|
|
Raises:
|
|
ValueError: If new_value is invalid or incompatible
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def is_warmed_up(self) -> bool:
|
|
"""
|
|
Check whether indicator has enough data for reliable values.
|
|
|
|
Returns:
|
|
True if indicator has received enough data points for reliable calculation
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def reset(self) -> None:
|
|
"""
|
|
Reset indicator state to initial conditions.
|
|
|
|
This method clears all internal state and resets the indicator
|
|
as if it was just initialized.
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_current_value(self) -> Union[float, Dict[str, float], None]:
|
|
"""
|
|
Get the current indicator value without updating.
|
|
|
|
Returns:
|
|
Current indicator value, or None if not warmed up
|
|
"""
|
|
pass
|
|
|
|
def get_state_summary(self) -> Dict[str, Any]:
|
|
"""
|
|
Get summary of current indicator state for debugging.
|
|
|
|
Returns:
|
|
Dictionary containing indicator state information
|
|
"""
|
|
return {
|
|
'indicator_type': self.__class__.__name__,
|
|
'period': self.period,
|
|
'values_received': self.values_received,
|
|
'is_warmed_up': self.is_warmed_up(),
|
|
'is_initialized': self.is_initialized,
|
|
'current_value': self.get_current_value()
|
|
}
|
|
|
|
def validate_input(self, value: Union[float, Dict[str, float]]) -> None:
|
|
"""
|
|
Validate input value for the indicator.
|
|
|
|
Args:
|
|
value: Input value to validate
|
|
|
|
Raises:
|
|
ValueError: If value is invalid
|
|
TypeError: If value type is incorrect
|
|
"""
|
|
if isinstance(value, (int, float)):
|
|
if not np.isfinite(value):
|
|
raise ValueError(f"Input value must be finite, got {value}")
|
|
elif isinstance(value, dict):
|
|
required_keys = ['open', 'high', 'low', 'close']
|
|
for key in required_keys:
|
|
if key not in value:
|
|
raise ValueError(f"OHLCV dict missing required key: {key}")
|
|
if not np.isfinite(value[key]):
|
|
raise ValueError(f"OHLCV value for {key} must be finite, got {value[key]}")
|
|
# Validate OHLC relationships
|
|
if not (value['low'] <= value['open'] <= value['high'] and
|
|
value['low'] <= value['close'] <= value['high']):
|
|
raise ValueError(f"Invalid OHLC relationships: {value}")
|
|
else:
|
|
raise TypeError(f"Input value must be float or OHLCV dict, got {type(value)}")
|
|
|
|
def __repr__(self) -> str:
|
|
"""String representation of the indicator state."""
|
|
return (f"{self.__class__.__name__}(period={self.period}, "
|
|
f"values_received={self.values_received}, "
|
|
f"warmed_up={self.is_warmed_up()})")
|
|
|
|
|
|
class SimpleIndicatorState(IndicatorState):
|
|
"""
|
|
Base class for simple single-value indicators.
|
|
|
|
This class provides common functionality for indicators that work with
|
|
single float values and maintain a simple rolling calculation.
|
|
"""
|
|
|
|
def __init__(self, period: int):
|
|
"""Initialize simple indicator state."""
|
|
super().__init__(period)
|
|
self._current_value = None
|
|
|
|
def get_current_value(self) -> Optional[float]:
|
|
"""Get current indicator value."""
|
|
return self._current_value if self.is_warmed_up() else None
|
|
|
|
def is_warmed_up(self) -> bool:
|
|
"""Check if indicator is warmed up."""
|
|
return self.values_received >= self.period
|
|
|
|
|
|
class OHLCIndicatorState(IndicatorState):
|
|
"""
|
|
Base class for OHLC-based indicators.
|
|
|
|
This class provides common functionality for indicators that work with
|
|
OHLC data (Open, High, Low, Close) and may return multiple values.
|
|
"""
|
|
|
|
def __init__(self, period: int):
|
|
"""Initialize OHLC indicator state."""
|
|
super().__init__(period)
|
|
self._current_values = {}
|
|
|
|
def get_current_value(self) -> Optional[Dict[str, float]]:
|
|
"""Get current indicator values."""
|
|
return self._current_values.copy() if self.is_warmed_up() else None
|
|
|
|
def is_warmed_up(self) -> bool:
|
|
"""Check if indicator is warmed up."""
|
|
return self.values_received >= self.period |