wip training
This commit is contained in:
@ -550,72 +550,318 @@ class DataProvider:
|
||||
logger.error(f"Error aggregating COB 1s for {symbol}: {e}")
|
||||
|
||||
def _add_multi_timeframe_imbalances(self, symbol: str, aggregated_data: Dict, current_second: int) -> Dict:
|
||||
"""Add 1s, 5s, 15s, and 60s imbalance indicators to the aggregated data"""
|
||||
"""Add COB-based order book imbalances with configurable price ranges"""
|
||||
try:
|
||||
# Get historical aggregated data for calculations
|
||||
historical_data = list(self.cob_1s_aggregated[symbol])
|
||||
# Get price range based on symbol
|
||||
price_range = self._get_price_range_for_symbol(symbol)
|
||||
|
||||
# Calculate imbalances for different timeframes
|
||||
# Get latest COB data for current imbalance calculation
|
||||
latest_cob = self.get_latest_cob_data(symbol)
|
||||
current_imbalance = 0.0
|
||||
|
||||
if latest_cob:
|
||||
current_imbalance = self._calculate_cob_imbalance(latest_cob, price_range)
|
||||
|
||||
# Get historical COB data for timeframe calculations
|
||||
historical_cob_data = list(self.cob_raw_ticks[symbol]) if symbol in self.cob_raw_ticks else []
|
||||
|
||||
# Calculate imbalances for different timeframes using COB data
|
||||
imbalances = {
|
||||
'imbalance_1s': aggregated_data.get('imbalance', 0.0), # Current 1s imbalance
|
||||
'imbalance_5s': self._calculate_timeframe_imbalance(historical_data, 5),
|
||||
'imbalance_15s': self._calculate_timeframe_imbalance(historical_data, 15),
|
||||
'imbalance_60s': self._calculate_timeframe_imbalance(historical_data, 60)
|
||||
'imbalance_1s': current_imbalance, # Current COB imbalance
|
||||
'imbalance_5s': self._calculate_timeframe_cob_imbalance(historical_cob_data, 5, price_range),
|
||||
'imbalance_15s': self._calculate_timeframe_cob_imbalance(historical_cob_data, 15, price_range),
|
||||
'imbalance_60s': self._calculate_timeframe_cob_imbalance(historical_cob_data, 60, price_range)
|
||||
}
|
||||
|
||||
# Add imbalances to aggregated data
|
||||
aggregated_data.update(imbalances)
|
||||
# Add volume-weighted imbalances within price range
|
||||
volume_imbalances = {
|
||||
'volume_imbalance_1s': current_imbalance,
|
||||
'volume_imbalance_5s': self._calculate_volume_weighted_imbalance(historical_cob_data, 5, price_range),
|
||||
'volume_imbalance_15s': self._calculate_volume_weighted_imbalance(historical_cob_data, 15, price_range),
|
||||
'volume_imbalance_60s': self._calculate_volume_weighted_imbalance(historical_cob_data, 60, price_range)
|
||||
}
|
||||
|
||||
# Combine all imbalance metrics
|
||||
all_imbalances = {**imbalances, **volume_imbalances}
|
||||
|
||||
# Add to aggregated data
|
||||
aggregated_data.update(all_imbalances)
|
||||
|
||||
# Also add to stats section for compatibility
|
||||
if 'stats' not in aggregated_data:
|
||||
aggregated_data['stats'] = {}
|
||||
aggregated_data['stats'].update(imbalances)
|
||||
aggregated_data['stats'].update(all_imbalances)
|
||||
|
||||
# Add price range information for debugging
|
||||
aggregated_data['stats']['price_range_used'] = price_range
|
||||
|
||||
logger.debug(f"COB imbalances for {symbol} (±${price_range}): {current_imbalance:.4f}")
|
||||
|
||||
return aggregated_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating multi-timeframe imbalances for {symbol}: {e}")
|
||||
logger.error(f"Error calculating COB-based imbalances for {symbol}: {e}")
|
||||
# Return original data with default imbalances
|
||||
default_imbalances = {
|
||||
'imbalance_1s': 0.0,
|
||||
'imbalance_5s': 0.0,
|
||||
'imbalance_15s': 0.0,
|
||||
'imbalance_60s': 0.0
|
||||
'imbalance_1s': 0.0, 'imbalance_5s': 0.0, 'imbalance_15s': 0.0, 'imbalance_60s': 0.0,
|
||||
'volume_imbalance_1s': 0.0, 'volume_imbalance_5s': 0.0, 'volume_imbalance_15s': 0.0, 'volume_imbalance_60s': 0.0
|
||||
}
|
||||
aggregated_data.update(default_imbalances)
|
||||
return aggregated_data
|
||||
|
||||
def _calculate_timeframe_imbalance(self, historical_data: List[Dict], seconds: int) -> float:
|
||||
"""Calculate average imbalance over the specified number of seconds"""
|
||||
def _get_price_range_for_symbol(self, symbol: str) -> float:
|
||||
"""Get configurable price range for order book imbalance calculation"""
|
||||
# Configurable price ranges per symbol
|
||||
price_ranges = {
|
||||
'ETH/USDT': 5.0, # $5 range for ETH
|
||||
'BTC/USDT': 50.0, # $50 range for BTC
|
||||
}
|
||||
|
||||
return price_ranges.get(symbol, 10.0) # Default $10 range for other symbols
|
||||
|
||||
def get_current_cob_imbalance(self, symbol: str) -> Dict[str, float]:
|
||||
"""Get current COB imbalance metrics for a symbol"""
|
||||
try:
|
||||
if not historical_data or len(historical_data) < seconds:
|
||||
price_range = self._get_price_range_for_symbol(symbol)
|
||||
latest_cob = self.get_latest_cob_data(symbol)
|
||||
|
||||
if not latest_cob:
|
||||
return {
|
||||
'imbalance': 0.0,
|
||||
'price_range': price_range,
|
||||
'mid_price': 0.0,
|
||||
'bid_volume_in_range': 0.0,
|
||||
'ask_volume_in_range': 0.0
|
||||
}
|
||||
|
||||
# Calculate detailed imbalance info
|
||||
bids = latest_cob.get('bids', [])
|
||||
asks = latest_cob.get('asks', [])
|
||||
|
||||
if not bids or not asks:
|
||||
return {'imbalance': 0.0, 'price_range': price_range, 'mid_price': 0.0}
|
||||
|
||||
# Calculate mid price with proper safety checks
|
||||
try:
|
||||
if not bids or not asks or len(bids) == 0 or len(asks) == 0:
|
||||
return {'imbalance': 0.0, 'price_range': price_range, 'mid_price': 0.0}
|
||||
|
||||
best_bid = float(bids[0][0])
|
||||
best_ask = float(asks[0][0])
|
||||
mid_price = (best_bid + best_ask) / 2.0
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error calculating mid price for {symbol}: {e}")
|
||||
return {'imbalance': 0.0, 'price_range': price_range, 'mid_price': 0.0, 'error': str(e)}
|
||||
|
||||
# Calculate volumes in range with safety checks
|
||||
price_min = mid_price - price_range
|
||||
price_max = mid_price + price_range
|
||||
|
||||
bid_volume_in_range = 0.0
|
||||
ask_volume_in_range = 0.0
|
||||
|
||||
try:
|
||||
for price, vol in bids:
|
||||
price = float(price)
|
||||
vol = float(vol)
|
||||
if price_min <= price <= mid_price:
|
||||
bid_volume_in_range += vol
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error processing bid volumes for {symbol}: {e}")
|
||||
|
||||
try:
|
||||
for price, vol in asks:
|
||||
price = float(price)
|
||||
vol = float(vol)
|
||||
if mid_price <= price <= price_max:
|
||||
ask_volume_in_range += vol
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error processing ask volumes for {symbol}: {e}")
|
||||
|
||||
total_volume = bid_volume_in_range + ask_volume_in_range
|
||||
imbalance = (bid_volume_in_range - ask_volume_in_range) / total_volume if total_volume > 0 else 0.0
|
||||
|
||||
return {
|
||||
'imbalance': imbalance,
|
||||
'price_range': price_range,
|
||||
'mid_price': mid_price,
|
||||
'bid_volume_in_range': bid_volume_in_range,
|
||||
'ask_volume_in_range': ask_volume_in_range,
|
||||
'total_volume_in_range': total_volume,
|
||||
'best_bid': best_bid,
|
||||
'best_ask': best_ask
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting current COB imbalance for {symbol}: {e}")
|
||||
return {'imbalance': 0.0, 'price_range': price_range, 'error': str(e)}
|
||||
|
||||
def _calculate_cob_imbalance(self, cob_data: Dict, price_range: float) -> float:
|
||||
"""Calculate order book imbalance within specified price range around mid price"""
|
||||
try:
|
||||
bids = cob_data.get('bids', [])
|
||||
asks = cob_data.get('asks', [])
|
||||
|
||||
if not bids or not asks:
|
||||
return 0.0
|
||||
|
||||
# Get the last N seconds of data
|
||||
recent_data = historical_data[-seconds:]
|
||||
|
||||
# Calculate weighted average imbalance
|
||||
total_volume = 0
|
||||
weighted_imbalance = 0
|
||||
|
||||
for data in recent_data:
|
||||
imbalance = data.get('imbalance', 0.0)
|
||||
volume = data.get('total_volume', 1.0) # Use 1.0 as default to avoid division by zero
|
||||
# Calculate mid price with proper safety checks
|
||||
try:
|
||||
if not bids or not asks or len(bids) == 0 or len(asks) == 0:
|
||||
return 0.0
|
||||
|
||||
weighted_imbalance += imbalance * volume
|
||||
total_volume += volume
|
||||
best_bid = float(bids[0][0])
|
||||
best_ask = float(asks[0][0])
|
||||
|
||||
if best_bid <= 0 or best_ask <= 0:
|
||||
return 0.0
|
||||
|
||||
mid_price = (best_bid + best_ask) / 2.0
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error calculating mid price: {e}")
|
||||
return 0.0
|
||||
|
||||
# Define price range around mid price
|
||||
price_min = mid_price - price_range
|
||||
price_max = mid_price + price_range
|
||||
|
||||
# Sum volumes within price range
|
||||
bid_volume_in_range = 0.0
|
||||
ask_volume_in_range = 0.0
|
||||
|
||||
# Sum bid volumes within range with safety checks
|
||||
try:
|
||||
for bid_price, bid_volume in bids:
|
||||
bid_price = float(bid_price)
|
||||
bid_volume = float(bid_volume)
|
||||
if price_min <= bid_price <= mid_price:
|
||||
bid_volume_in_range += bid_volume
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error processing bid volumes: {e}")
|
||||
|
||||
# Sum ask volumes within range with safety checks
|
||||
try:
|
||||
for ask_price, ask_volume in asks:
|
||||
ask_price = float(ask_price)
|
||||
ask_volume = float(ask_volume)
|
||||
if mid_price <= ask_price <= price_max:
|
||||
ask_volume_in_range += ask_volume
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error processing ask volumes: {e}")
|
||||
|
||||
# Calculate imbalance: (bid_volume - ask_volume) / (bid_volume + ask_volume)
|
||||
total_volume = bid_volume_in_range + ask_volume_in_range
|
||||
|
||||
if total_volume > 0:
|
||||
return weighted_imbalance / total_volume
|
||||
imbalance = (bid_volume_in_range - ask_volume_in_range) / total_volume
|
||||
return imbalance
|
||||
else:
|
||||
# Fallback to simple average
|
||||
imbalances = [data.get('imbalance', 0.0) for data in recent_data]
|
||||
return sum(imbalances) / len(imbalances) if imbalances else 0.0
|
||||
return 0.0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating {seconds}s imbalance: {e}")
|
||||
logger.error(f"Error calculating COB imbalance: {e}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_timeframe_cob_imbalance(self, historical_cob_data: List[Dict], seconds: int, price_range: float) -> float:
|
||||
"""Calculate average COB imbalance over specified timeframe"""
|
||||
try:
|
||||
if not historical_cob_data or len(historical_cob_data) == 0:
|
||||
return 0.0
|
||||
|
||||
# Get recent data within timeframe (approximate by using last N ticks)
|
||||
# Assuming ~100 ticks per second, so N = seconds * 100
|
||||
max_ticks = seconds * 100
|
||||
recent_ticks = historical_cob_data[-max_ticks:] if len(historical_cob_data) > max_ticks else historical_cob_data
|
||||
|
||||
if not recent_ticks:
|
||||
return 0.0
|
||||
|
||||
# Calculate imbalance for each tick and average
|
||||
imbalances = []
|
||||
for tick in recent_ticks:
|
||||
imbalance = self._calculate_cob_imbalance(tick, price_range)
|
||||
imbalances.append(imbalance)
|
||||
|
||||
if imbalances:
|
||||
return sum(imbalances) / len(imbalances)
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating {seconds}s COB imbalance: {e}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_volume_weighted_imbalance(self, historical_cob_data: List[Dict], seconds: int, price_range: float) -> float:
|
||||
"""Calculate volume-weighted average imbalance over timeframe"""
|
||||
try:
|
||||
if not historical_cob_data:
|
||||
return 0.0
|
||||
|
||||
# Get recent data within timeframe
|
||||
max_ticks = seconds * 100 # Approximate ticks per second
|
||||
recent_ticks = historical_cob_data[-max_ticks:] if len(historical_cob_data) > max_ticks else historical_cob_data
|
||||
|
||||
if not recent_ticks:
|
||||
return 0.0
|
||||
|
||||
total_weighted_imbalance = 0.0
|
||||
total_volume = 0.0
|
||||
|
||||
for tick in recent_ticks:
|
||||
imbalance = self._calculate_cob_imbalance(tick, price_range)
|
||||
|
||||
# Calculate total volume in range for weighting
|
||||
bids = tick.get('bids', [])
|
||||
asks = tick.get('asks', [])
|
||||
|
||||
if bids and asks and len(bids) > 0 and len(asks) > 0:
|
||||
# Get mid price for this tick with proper safety checks
|
||||
try:
|
||||
best_bid = float(bids[0][0])
|
||||
best_ask = float(asks[0][0])
|
||||
mid_price = (best_bid + best_ask) / 2.0
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Skipping tick due to data format issue: {e}")
|
||||
continue
|
||||
|
||||
# Calculate volume in range
|
||||
price_min = mid_price - price_range
|
||||
price_max = mid_price + price_range
|
||||
|
||||
tick_volume = 0.0
|
||||
try:
|
||||
for bid_price, bid_volume in bids:
|
||||
bid_price = float(bid_price)
|
||||
bid_volume = float(bid_volume)
|
||||
if price_min <= bid_price <= mid_price:
|
||||
tick_volume += bid_volume
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error processing bid volumes in weighted calculation: {e}")
|
||||
|
||||
try:
|
||||
for ask_price, ask_volume in asks:
|
||||
ask_price = float(ask_price)
|
||||
ask_volume = float(ask_volume)
|
||||
if mid_price <= ask_price <= price_max:
|
||||
tick_volume += ask_volume
|
||||
except (IndexError, KeyError, ValueError) as e:
|
||||
logger.debug(f"Error processing ask volumes in weighted calculation: {e}")
|
||||
|
||||
if tick_volume > 0:
|
||||
total_weighted_imbalance += imbalance * tick_volume
|
||||
total_volume += tick_volume
|
||||
|
||||
if total_volume > 0:
|
||||
return total_weighted_imbalance / total_volume
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating volume-weighted {seconds}s imbalance: {e}")
|
||||
return 0.0
|
||||
|
||||
|
||||
|
||||
def _create_1s_cob_aggregation(self, symbol: str, ticks: List[Dict], timestamp: int) -> Dict:
|
||||
"""Create 1s aggregation with $1 price buckets"""
|
||||
try:
|
||||
|
Reference in New Issue
Block a user