dash working
This commit is contained in:
@ -2311,49 +2311,56 @@ class CleanTradingDashboard:
|
||||
cob_data = self.data_provider.get_latest_cob_data(symbol)
|
||||
logger.debug(f"COB data type for {symbol}: {type(cob_data)}, data: {cob_data}")
|
||||
|
||||
if cob_data and isinstance(cob_data, dict) and 'stats' in cob_data:
|
||||
logger.debug(f"COB snapshot available for {symbol} from centralized data provider")
|
||||
|
||||
# Create a snapshot object from the data provider's data
|
||||
class COBSnapshot:
|
||||
def __init__(self, data):
|
||||
# Convert list format [[price, qty], ...] to dictionary format
|
||||
raw_bids = data.get('bids', [])
|
||||
raw_asks = data.get('asks', [])
|
||||
|
||||
# Convert to dictionary format expected by component manager
|
||||
self.consolidated_bids = []
|
||||
for bid in raw_bids:
|
||||
if isinstance(bid, list) and len(bid) >= 2:
|
||||
self.consolidated_bids.append({
|
||||
'price': bid[0],
|
||||
'size': bid[1],
|
||||
'total_size': bid[1],
|
||||
'total_volume_usd': bid[0] * bid[1]
|
||||
})
|
||||
|
||||
self.consolidated_asks = []
|
||||
for ask in raw_asks:
|
||||
if isinstance(ask, list) and len(ask) >= 2:
|
||||
self.consolidated_asks.append({
|
||||
'price': ask[0],
|
||||
'size': ask[1],
|
||||
'total_size': ask[1],
|
||||
'total_volume_usd': ask[0] * ask[1]
|
||||
})
|
||||
|
||||
self.stats = data.get('stats', {})
|
||||
# Add direct attributes for new format compatibility
|
||||
self.volume_weighted_mid = self.stats.get('mid_price', 0)
|
||||
self.spread_bps = self.stats.get('spread_bps', 0)
|
||||
self.liquidity_imbalance = self.stats.get('imbalance', 0)
|
||||
self.total_bid_liquidity = self.stats.get('bid_liquidity', 0)
|
||||
self.total_ask_liquidity = self.stats.get('ask_liquidity', 0)
|
||||
self.exchanges_active = ['Binance'] # Default for now
|
||||
|
||||
return COBSnapshot(cob_data)
|
||||
if cob_data and isinstance(cob_data, dict):
|
||||
# Validate COB data structure
|
||||
if 'stats' in cob_data and cob_data['stats']:
|
||||
logger.debug(f"COB snapshot available for {symbol} from centralized data provider")
|
||||
|
||||
# Create a snapshot object from the data provider's data
|
||||
class COBSnapshot:
|
||||
def __init__(self, data):
|
||||
# Convert list format [[price, qty], ...] to dictionary format
|
||||
raw_bids = data.get('bids', [])
|
||||
raw_asks = data.get('asks', [])
|
||||
|
||||
# Convert to dictionary format expected by component manager
|
||||
self.consolidated_bids = []
|
||||
for bid in raw_bids:
|
||||
if isinstance(bid, list) and len(bid) >= 2:
|
||||
self.consolidated_bids.append({
|
||||
'price': bid[0],
|
||||
'size': bid[1],
|
||||
'total_size': bid[1],
|
||||
'total_volume_usd': bid[0] * bid[1]
|
||||
})
|
||||
|
||||
self.consolidated_asks = []
|
||||
for ask in raw_asks:
|
||||
if isinstance(ask, list) and len(ask) >= 2:
|
||||
self.consolidated_asks.append({
|
||||
'price': ask[0],
|
||||
'size': ask[1],
|
||||
'total_size': ask[1],
|
||||
'total_volume_usd': ask[0] * ask[1]
|
||||
})
|
||||
|
||||
self.stats = data.get('stats', {})
|
||||
# Add direct attributes for new format compatibility
|
||||
self.volume_weighted_mid = self.stats.get('mid_price', 0)
|
||||
self.spread_bps = self.stats.get('spread_bps', 0)
|
||||
self.liquidity_imbalance = self.stats.get('imbalance', 0)
|
||||
self.total_bid_liquidity = self.stats.get('bid_liquidity', 0)
|
||||
self.total_ask_liquidity = self.stats.get('ask_liquidity', 0)
|
||||
self.exchanges_active = ['Binance'] # Default for now
|
||||
|
||||
return COBSnapshot(cob_data)
|
||||
else:
|
||||
# Data exists but no stats - this is the "Invalid COB data" case
|
||||
logger.debug(f"COB data for {symbol} missing stats structure: {type(cob_data)}, keys: {list(cob_data.keys()) if isinstance(cob_data, dict) else 'not dict'}")
|
||||
return None
|
||||
else:
|
||||
logger.warning(f"Invalid COB data for {symbol}: type={type(cob_data)}, has_stats={'stats' in cob_data if isinstance(cob_data, dict) else False}")
|
||||
logger.debug(f"No COB data available for {symbol} from data provider")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting COB data from data provider: {e}")
|
||||
|
||||
@ -5358,6 +5365,18 @@ class CleanTradingDashboard:
|
||||
|
||||
self.latest_cob_data[symbol] = cob_snapshot
|
||||
|
||||
# Store in history for moving average calculations
|
||||
if not hasattr(self, 'cob_data_history'):
|
||||
self.cob_data_history = {'ETH/USDT': deque(maxlen=61), 'BTC/USDT': deque(maxlen=61)}
|
||||
|
||||
if symbol in self.cob_data_history:
|
||||
self.cob_data_history[symbol].append(cob_snapshot)
|
||||
|
||||
# Update last update timestamp
|
||||
if not hasattr(self, 'cob_last_update'):
|
||||
self.cob_last_update = {}
|
||||
self.cob_last_update[symbol] = time.time()
|
||||
|
||||
# Update current price from COB data
|
||||
if 'stats' in cob_snapshot and 'mid_price' in cob_snapshot['stats']:
|
||||
self.current_prices[symbol] = cob_snapshot['stats']['mid_price']
|
||||
@ -6021,33 +6040,71 @@ class CleanTradingDashboard:
|
||||
raise
|
||||
|
||||
def _calculate_cumulative_imbalance(self, symbol: str) -> Dict[str, float]:
|
||||
"""Calculate average imbalance over multiple time windows."""
|
||||
"""Calculate Moving Averages (MA) of imbalance over different periods."""
|
||||
stats = {}
|
||||
now = time.time()
|
||||
history = self.cob_data_history.get(symbol)
|
||||
|
||||
if not history:
|
||||
return {'1s': 0.0, '5s': 0.0, '15s': 0.0, '60s': 0.0}
|
||||
|
||||
periods = {'1s': 1, '5s': 5, '15s': 15, '60s': 60}
|
||||
|
||||
for name, duration in periods.items():
|
||||
recent_imbalances = []
|
||||
for snap in history:
|
||||
# Check if snap is a valid dict with timestamp and stats
|
||||
if isinstance(snap, dict) and 'timestamp' in snap and (now - snap['timestamp'] <= duration) and 'stats' in snap and snap['stats']:
|
||||
imbalance = snap['stats'].get('imbalance')
|
||||
if imbalance is not None:
|
||||
recent_imbalances.append(imbalance)
|
||||
# Convert history to list and get recent snapshots
|
||||
history_list = list(history)
|
||||
if not history_list:
|
||||
return {'1s': 0.0, '5s': 0.0, '15s': 0.0, '60s': 0.0}
|
||||
|
||||
if recent_imbalances:
|
||||
stats[name] = sum(recent_imbalances) / len(recent_imbalances)
|
||||
else:
|
||||
stats[name] = 0.0
|
||||
# Extract imbalance values from recent snapshots
|
||||
imbalances = []
|
||||
for snap in history_list:
|
||||
if isinstance(snap, dict) and 'stats' in snap and snap['stats']:
|
||||
imbalance = snap['stats'].get('imbalance')
|
||||
if imbalance is not None:
|
||||
imbalances.append(imbalance)
|
||||
|
||||
if not imbalances:
|
||||
return {'1s': 0.0, '5s': 0.0, '15s': 0.0, '60s': 0.0}
|
||||
|
||||
# Calculate Moving Averages over different periods
|
||||
# MA periods: 1s=1 period, 5s=5 periods, 15s=15 periods, 60s=60 periods
|
||||
ma_periods = {'1s': 1, '5s': 5, '15s': 15, '60s': 60}
|
||||
|
||||
# Debug logging to verify cumulative imbalance calculation
|
||||
for name, period in ma_periods.items():
|
||||
if len(imbalances) >= period:
|
||||
# Calculate SMA over the last 'period' values
|
||||
recent_imbalances = imbalances[-period:]
|
||||
sma_value = sum(recent_imbalances) / len(recent_imbalances)
|
||||
|
||||
# Also calculate EMA for better responsiveness
|
||||
if period > 1:
|
||||
# EMA calculation with alpha = 2/(period+1)
|
||||
alpha = 2.0 / (period + 1)
|
||||
ema_value = recent_imbalances[0] # Start with first value
|
||||
for value in recent_imbalances[1:]:
|
||||
ema_value = alpha * value + (1 - alpha) * ema_value
|
||||
# Use EMA for better responsiveness
|
||||
stats[name] = ema_value
|
||||
else:
|
||||
# For 1s, use SMA (no EMA needed)
|
||||
stats[name] = sma_value
|
||||
else:
|
||||
# If not enough data, use available data
|
||||
available_imbalances = imbalances[-min(period, len(imbalances)):]
|
||||
if available_imbalances:
|
||||
if len(available_imbalances) > 1:
|
||||
# Calculate EMA for available data
|
||||
alpha = 2.0 / (len(available_imbalances) + 1)
|
||||
ema_value = available_imbalances[0]
|
||||
for value in available_imbalances[1:]:
|
||||
ema_value = alpha * value + (1 - alpha) * ema_value
|
||||
stats[name] = ema_value
|
||||
else:
|
||||
# Single value, use as is
|
||||
stats[name] = available_imbalances[0]
|
||||
else:
|
||||
stats[name] = 0.0
|
||||
|
||||
# Debug logging to verify MA calculation
|
||||
if any(value != 0.0 for value in stats.values()):
|
||||
logger.debug(f"[CUMULATIVE-IMBALANCE] {symbol}: {stats}")
|
||||
logger.debug(f"[MOVING-AVERAGE-IMBALANCE] {symbol}: {stats} (from {len(imbalances)} snapshots)")
|
||||
|
||||
return stats
|
||||
|
||||
|
@ -412,10 +412,10 @@ class DashboardComponentManager:
|
||||
]),
|
||||
|
||||
html.Div([
|
||||
self._create_timeframe_imbalance("1s", stats.get('imbalance_1s', imbalance)),
|
||||
self._create_timeframe_imbalance("5s", stats.get('imbalance_5s', imbalance)),
|
||||
self._create_timeframe_imbalance("15s", stats.get('imbalance_15s', imbalance)),
|
||||
self._create_timeframe_imbalance("60s", stats.get('imbalance_60s', imbalance)),
|
||||
self._create_timeframe_imbalance("1s", cumulative_imbalance_stats.get('1s', imbalance)),
|
||||
self._create_timeframe_imbalance("5s", cumulative_imbalance_stats.get('5s', imbalance)),
|
||||
self._create_timeframe_imbalance("15s", cumulative_imbalance_stats.get('15s', imbalance)),
|
||||
self._create_timeframe_imbalance("60s", cumulative_imbalance_stats.get('60s', imbalance)),
|
||||
], className="d-flex justify-content-between mb-2"),
|
||||
|
||||
html.Div(imbalance_stats_display),
|
||||
|
@ -986,33 +986,71 @@ class TemplatedTradingDashboard:
|
||||
logger.debug(f"TEMPLATED DASHBOARD: Error generating bucketed COB data: {e}")
|
||||
|
||||
def _calculate_cumulative_imbalance(self, symbol: str) -> Dict[str, float]:
|
||||
"""Calculate average imbalance over multiple time windows."""
|
||||
"""Calculate Moving Averages (MA) of imbalance over different periods."""
|
||||
stats = {}
|
||||
now = time.time()
|
||||
history = self.cob_data_history.get(symbol)
|
||||
|
||||
if not history:
|
||||
return {'1s': 0.0, '5s': 0.0, '15s': 0.0, '60s': 0.0}
|
||||
|
||||
periods = {'1s': 1, '5s': 5, '15s': 15, '60s': 60}
|
||||
|
||||
for name, duration in periods.items():
|
||||
recent_imbalances = []
|
||||
for snap in history:
|
||||
# Check if snap is a valid dict with timestamp and stats
|
||||
if isinstance(snap, dict) and 'timestamp' in snap and (now - snap['timestamp'] <= duration) and 'stats' in snap and snap['stats']:
|
||||
imbalance = snap['stats'].get('imbalance')
|
||||
if imbalance is not None:
|
||||
recent_imbalances.append(imbalance)
|
||||
# Convert history to list and get recent snapshots
|
||||
history_list = list(history)
|
||||
if not history_list:
|
||||
return {'1s': 0.0, '5s': 0.0, '15s': 0.0, '60s': 0.0}
|
||||
|
||||
if recent_imbalances:
|
||||
stats[name] = sum(recent_imbalances) / len(recent_imbalances)
|
||||
else:
|
||||
stats[name] = 0.0
|
||||
# Extract imbalance values from recent snapshots
|
||||
imbalances = []
|
||||
for snap in history_list:
|
||||
if isinstance(snap, dict) and 'stats' in snap and snap['stats']:
|
||||
imbalance = snap['stats'].get('imbalance')
|
||||
if imbalance is not None:
|
||||
imbalances.append(imbalance)
|
||||
|
||||
if not imbalances:
|
||||
return {'1s': 0.0, '5s': 0.0, '15s': 0.0, '60s': 0.0}
|
||||
|
||||
# Calculate Moving Averages over different periods
|
||||
# MA periods: 1s=1 period, 5s=5 periods, 15s=15 periods, 60s=60 periods
|
||||
ma_periods = {'1s': 1, '5s': 5, '15s': 15, '60s': 60}
|
||||
|
||||
# Debug logging to verify cumulative imbalance calculation
|
||||
for name, period in ma_periods.items():
|
||||
if len(imbalances) >= period:
|
||||
# Calculate SMA over the last 'period' values
|
||||
recent_imbalances = imbalances[-period:]
|
||||
sma_value = sum(recent_imbalances) / len(recent_imbalances)
|
||||
|
||||
# Also calculate EMA for better responsiveness
|
||||
if period > 1:
|
||||
# EMA calculation with alpha = 2/(period+1)
|
||||
alpha = 2.0 / (period + 1)
|
||||
ema_value = recent_imbalances[0] # Start with first value
|
||||
for value in recent_imbalances[1:]:
|
||||
ema_value = alpha * value + (1 - alpha) * ema_value
|
||||
# Use EMA for better responsiveness
|
||||
stats[name] = ema_value
|
||||
else:
|
||||
# For 1s, use SMA (no EMA needed)
|
||||
stats[name] = sma_value
|
||||
else:
|
||||
# If not enough data, use available data
|
||||
available_imbalances = imbalances[-min(period, len(imbalances)):]
|
||||
if available_imbalances:
|
||||
if len(available_imbalances) > 1:
|
||||
# Calculate EMA for available data
|
||||
alpha = 2.0 / (len(available_imbalances) + 1)
|
||||
ema_value = available_imbalances[0]
|
||||
for value in available_imbalances[1:]:
|
||||
ema_value = alpha * value + (1 - alpha) * ema_value
|
||||
stats[name] = ema_value
|
||||
else:
|
||||
# Single value, use as is
|
||||
stats[name] = available_imbalances[0]
|
||||
else:
|
||||
stats[name] = 0.0
|
||||
|
||||
# Debug logging to verify MA calculation
|
||||
if any(value != 0.0 for value in stats.values()):
|
||||
logger.debug(f"TEMPLATED DASHBOARD: [CUMULATIVE-IMBALANCE] {symbol}: {stats}")
|
||||
logger.debug(f"TEMPLATED DASHBOARD: [MOVING-AVERAGE-IMBALANCE] {symbol}: {stats} (from {len(imbalances)} snapshots)")
|
||||
|
||||
return stats
|
||||
|
||||
|
Reference in New Issue
Block a user