Update OKX configuration and aggregation logic for enhanced multi-timeframe support
- Increased health check interval from 30s to 120s in `okx_config.json`. - Added support for additional timeframes (1s, 5s, 10s, 15s, 30s) in the aggregation logic across multiple components. - Updated `CandleProcessingConfig` and `RealTimeCandleProcessor` to handle new timeframes. - Enhanced validation and parsing functions to include new second-based timeframes. - Updated database schema to support new timeframes in `schema_clean.sql`. - Improved documentation to reflect changes in multi-timeframe aggregation capabilities.
This commit is contained in:
@@ -133,7 +133,17 @@ class TimeframeBucket:
|
||||
|
||||
def _calculate_end_time(self, start_time: datetime, timeframe: str) -> datetime:
|
||||
"""Calculate end time for this timeframe (right-aligned timestamp)."""
|
||||
if timeframe == '1m':
|
||||
if timeframe == '1s':
|
||||
return start_time + timedelta(seconds=1)
|
||||
elif timeframe == '5s':
|
||||
return start_time + timedelta(seconds=5)
|
||||
elif timeframe == '10s':
|
||||
return start_time + timedelta(seconds=10)
|
||||
elif timeframe == '15s':
|
||||
return start_time + timedelta(seconds=15)
|
||||
elif timeframe == '30s':
|
||||
return start_time + timedelta(seconds=30)
|
||||
elif timeframe == '1m':
|
||||
return start_time + timedelta(minutes=1)
|
||||
elif timeframe == '5m':
|
||||
return start_time + timedelta(minutes=5)
|
||||
@@ -314,6 +324,10 @@ class RealTimeCandleProcessor:
|
||||
The start time is the LEFT boundary of the interval.
|
||||
|
||||
EXAMPLES:
|
||||
- Trade at 09:03:45.123 for 1s timeframe -> bucket start = 09:03:45.000
|
||||
- Trade at 09:03:47.456 for 5s timeframe -> bucket start = 09:03:45.000 (45-50s bucket)
|
||||
- Trade at 09:03:52.789 for 10s timeframe -> bucket start = 09:03:50.000 (50-60s bucket)
|
||||
- Trade at 09:03:23.456 for 15s timeframe -> bucket start = 09:03:15.000 (15-30s bucket)
|
||||
- Trade at 09:03:45 for 5m timeframe -> bucket start = 09:00:00
|
||||
- Trade at 09:07:23 for 5m timeframe -> bucket start = 09:05:00
|
||||
- Trade at 14:00:00 for 1h timeframe -> bucket start = 14:00:00
|
||||
@@ -325,6 +339,26 @@ class RealTimeCandleProcessor:
|
||||
Returns:
|
||||
Bucket start time (left boundary)
|
||||
"""
|
||||
if timeframe == '1s':
|
||||
# 1-second buckets align to second boundaries (remove microseconds)
|
||||
return timestamp.replace(microsecond=0)
|
||||
elif timeframe == '5s':
|
||||
# 5-second buckets: 00:00, 00:05, 00:10, 00:15, etc.
|
||||
dt = timestamp.replace(microsecond=0)
|
||||
return dt.replace(second=(dt.second // 5) * 5)
|
||||
elif timeframe == '10s':
|
||||
# 10-second buckets: 00:00, 00:10, 00:20, 00:30, 00:40, 00:50
|
||||
dt = timestamp.replace(microsecond=0)
|
||||
return dt.replace(second=(dt.second // 10) * 10)
|
||||
elif timeframe == '15s':
|
||||
# 15-second buckets: 00:00, 00:15, 00:30, 00:45
|
||||
dt = timestamp.replace(microsecond=0)
|
||||
return dt.replace(second=(dt.second // 15) * 15)
|
||||
elif timeframe == '30s':
|
||||
# 30-second buckets: 00:00, 00:30
|
||||
dt = timestamp.replace(microsecond=0)
|
||||
return dt.replace(second=(dt.second // 30) * 30)
|
||||
|
||||
# Normalize to UTC and remove microseconds for clean boundaries
|
||||
dt = timestamp.replace(second=0, microsecond=0)
|
||||
|
||||
@@ -519,12 +553,12 @@ def validate_timeframe(timeframe: str) -> bool:
|
||||
Validate if timeframe is supported.
|
||||
|
||||
Args:
|
||||
timeframe: Timeframe string (e.g., '1m', '5m', '1h')
|
||||
timeframe: Timeframe string (e.g., '1s', '5s', '10s', '1m', '5m', '1h')
|
||||
|
||||
Returns:
|
||||
True if supported, False otherwise
|
||||
"""
|
||||
supported = ['1m', '5m', '15m', '30m', '1h', '4h', '1d']
|
||||
supported = ['1s', '5s', '10s', '15s', '30s', '1m', '5m', '15m', '30m', '1h', '4h', '1d']
|
||||
return timeframe in supported
|
||||
|
||||
|
||||
@@ -533,18 +567,19 @@ def parse_timeframe(timeframe: str) -> tuple[int, str]:
|
||||
Parse timeframe string into number and unit.
|
||||
|
||||
Args:
|
||||
timeframe: Timeframe string (e.g., '5m', '1h')
|
||||
timeframe: Timeframe string (e.g., '1s', '5m', '1h')
|
||||
|
||||
Returns:
|
||||
Tuple of (number, unit)
|
||||
|
||||
Examples:
|
||||
'1s' -> (1, 's')
|
||||
'5m' -> (5, 'm')
|
||||
'1h' -> (1, 'h')
|
||||
'1d' -> (1, 'd')
|
||||
"""
|
||||
import re
|
||||
match = re.match(r'^(\d+)([mhd])$', timeframe.lower())
|
||||
match = re.match(r'^(\d+)([smhd])$', timeframe.lower())
|
||||
if not match:
|
||||
raise ValueError(f"Invalid timeframe format: {timeframe}")
|
||||
|
||||
|
||||
@@ -118,14 +118,14 @@ class OHLCVCandle:
|
||||
@dataclass
|
||||
class CandleProcessingConfig:
|
||||
"""Configuration for candle processing - shared across exchanges."""
|
||||
timeframes: List[str] = field(default_factory=lambda: ['1m', '5m', '15m', '1h'])
|
||||
timeframes: List[str] = field(default_factory=lambda: ['1s', '5s', '1m', '5m', '15m', '1h'])
|
||||
auto_save_candles: bool = True
|
||||
emit_incomplete_candles: bool = False
|
||||
max_trades_per_candle: int = 100000 # Safety limit
|
||||
|
||||
def __post_init__(self):
|
||||
"""Validate configuration after initialization."""
|
||||
supported_timeframes = ['1m', '5m', '15m', '30m', '1h', '4h', '1d']
|
||||
supported_timeframes = ['1s', '5s', '10s', '15s', '30s', '1m', '5m', '15m', '30m', '1h', '4h', '1d']
|
||||
for tf in self.timeframes:
|
||||
if tf not in supported_timeframes:
|
||||
raise ValueError(f"Unsupported timeframe: {tf}")
|
||||
@@ -139,6 +139,7 @@ class TradeSide(Enum):
|
||||
|
||||
class TimeframeUnit(Enum):
|
||||
"""Time units for candle timeframes."""
|
||||
SECOND = "s"
|
||||
MINUTE = "m"
|
||||
HOUR = "h"
|
||||
DAY = "d"
|
||||
|
||||
Reference in New Issue
Block a user