diff --git a/core/multi_exchange_cob_provider.py b/core/multi_exchange_cob_provider.py
index 93064b5..d77af8b 100644
--- a/core/multi_exchange_cob_provider.py
+++ b/core/multi_exchange_cob_provider.py
@@ -46,12 +46,17 @@ import aiohttp.resolver
logger = logging.getLogger(__name__)
+# goal: use top 10 exchanges
+# https://www.coingecko.com/en/exchanges
+
class ExchangeType(Enum):
BINANCE = "binance"
COINBASE = "coinbase"
KRAKEN = "kraken"
HUOBI = "huobi"
BITFINEX = "bitfinex"
+ BYBIT = "bybit"
+ BITGET = "bitget"
@dataclass
class ExchangeOrderBookLevel:
@@ -288,6 +293,24 @@ class MultiExchangeCOBProvider:
rate_limits={'requests_per_minute': 1000}
)
+ # Bybit configuration
+ configs[ExchangeType.BYBIT.value] = ExchangeConfig(
+ exchange_type=ExchangeType.BYBIT,
+ weight=0.18,
+ websocket_url="wss://stream.bybit.com/v5/public/spot",
+ rest_api_url="https://api.bybit.com",
+ symbols_mapping={'BTC/USDT': 'BTCUSDT', 'ETH/USDT': 'ETHUSDT'},
+ rate_limits={'requests_per_minute': 1200}
+ )
+ # Bitget configuration
+ configs[ExchangeType.BITGET.value] = ExchangeConfig(
+ exchange_type=ExchangeType.BITGET,
+ weight=0.12,
+ websocket_url="wss://ws.bitget.com/spot/v1/stream",
+ rest_api_url="https://api.bitget.com",
+ symbols_mapping={'BTC/USDT': 'BTCUSDT_SPBL', 'ETH/USDT': 'ETHUSDT_SPBL'},
+ rate_limits={'requests_per_minute': 1200}
+ )
return configs
async def start_streaming(self):
@@ -459,6 +482,10 @@ class MultiExchangeCOBProvider:
await self._stream_huobi_orderbook(symbol, config)
elif exchange_name == ExchangeType.BITFINEX.value:
await self._stream_bitfinex_orderbook(symbol, config)
+ elif exchange_name == ExchangeType.BYBIT.value:
+ await self._stream_bybit_orderbook(symbol, config)
+ elif exchange_name == ExchangeType.BITGET.value:
+ await self._stream_bitget_orderbook(symbol, config)
except Exception as e:
logger.error(f"Error streaming {exchange_name} for {symbol}: {e}")
@@ -467,7 +494,9 @@ class MultiExchangeCOBProvider:
async def _stream_binance_orderbook(self, symbol: str, config: ExchangeConfig):
"""Stream order book data from Binance"""
try:
- ws_url = f"{config.websocket_url}{config.symbols_mapping[symbol].lower()}@depth@1000ms"
+ # Use partial book depth stream with maximum levels - Binance format
+ # @depth20@100ms gives us 20 levels at 100ms, but we also have REST API for full depth
+ ws_url = f"{config.websocket_url}{config.symbols_mapping[symbol].lower()}@depth20@100ms"
logger.info(f"Connecting to Binance WebSocket: {ws_url}")
if websockets is None or websockets_connect is None:
diff --git a/web/clean_dashboard.py b/web/clean_dashboard.py
index cbc64f5..83e0f72 100644
--- a/web/clean_dashboard.py
+++ b/web/clean_dashboard.py
@@ -174,12 +174,64 @@ class CleanTradingDashboard:
timezone_name = self.config.get('system', {}).get('timezone', 'Europe/Sofia')
self.timezone = pytz.timezone(timezone_name)
- # Create Dash app
+ # Create Dash app with dark theme
self.app = Dash(__name__, external_stylesheets=[
'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css',
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'
])
+ # Add custom dark theme CSS
+ self.app.index_string = '''
+
+
+
+ {%metas%}
+ {%title%}
+ {%favicon%}
+ {%css%}
+
+
+
+ {%app_entry%}
+
+
+
+ '''
+
# Suppress Dash development mode logging
self.app.enable_dev_tools(debug=False, dev_tools_silence_routes_logging=True)
diff --git a/web/component_manager.py b/web/component_manager.py
index a86e75c..a9009c0 100644
--- a/web/component_manager.py
+++ b/web/component_manager.py
@@ -438,10 +438,19 @@ class DashboardComponentManager:
all_usd_volumes = [b['usd_volume'] for b in bid_buckets.values()] + [a['usd_volume'] for a in ask_buckets.values()]
max_volume = max(all_usd_volumes) if all_usd_volumes else 1
- # Create price levels around mid price
+ # Create price levels around mid price - expanded range for more bars
center_bucket = round(mid_price / bucket_size) * bucket_size
ask_levels = [center_bucket + i * bucket_size for i in range(1, num_levels + 1)]
bid_levels = [center_bucket - i * bucket_size for i in range(num_levels)]
+
+ # Debug: Log how many orders we have to work with
+ print(f"DEBUG COB: {symbol} - Processing {len(bids)} bids, {len(asks)} asks")
+ print(f"DEBUG COB: Mid price: ${mid_price:.2f}, Bucket size: ${bucket_size}")
+ print(f"DEBUG COB: Bid buckets: {len(bid_buckets)}, Ask buckets: {len(ask_buckets)}")
+ if bid_buckets:
+ print(f"DEBUG COB: Bid price range: ${min(bid_buckets.keys()):.2f} - ${max(bid_buckets.keys()):.2f}")
+ if ask_buckets:
+ print(f"DEBUG COB: Ask price range: ${min(ask_buckets.keys()):.2f} - ${max(ask_buckets.keys()):.2f}")
def create_bookmap_row(price, bid_data, ask_data, max_vol):
"""Create a Bookmap-style row with horizontal bars extending from center"""
@@ -472,32 +481,40 @@ class DashboardComponentManager:
html.Div([
html.Div(
bid_vol_str,
- className="text-end text-success small fw-bold px-1",
+ className="text-end small fw-bold px-2",
style={
- "background": "rgba(40, 167, 69, 0.8)" if bid_volume > 0 else "transparent",
+ "background": "linear-gradient(90deg, rgba(34, 197, 94, 0.3), rgba(34, 197, 94, 0.9))" if bid_volume > 0 else "transparent",
+ "color": "#ffffff" if bid_volume > 0 else "transparent",
"width": f"{bid_width}%",
- "minHeight": "18px",
+ "minHeight": "22px",
"display": "flex",
"alignItems": "center",
"justifyContent": "flex-end",
- "marginLeft": "auto"
+ "marginLeft": "auto",
+ "border": "1px solid rgba(34, 197, 94, 0.5)" if bid_volume > 0 else "none",
+ "borderRadius": "2px",
+ "textShadow": "1px 1px 2px rgba(0,0,0,0.8)",
+ "fontWeight": "600"
}
)
- ], style={"width": "40%", "display": "flex", "justifyContent": "flex-end"}),
+ ], style={"width": "40%", "display": "flex", "justifyContent": "flex-end", "padding": "1px"}),
# Price in center
html.Div(
f"{price:,.0f}",
- className="text-center small fw-bold text-light px-2",
+ className="text-center small fw-bold px-2",
style={
"width": "20%",
- "minHeight": "18px",
+ "minHeight": "22px",
"display": "flex",
"alignItems": "center",
"justifyContent": "center",
- "background": "rgba(108, 117, 125, 0.8)",
- "borderLeft": "1px solid rgba(255,255,255,0.2)",
- "borderRight": "1px solid rgba(255,255,255,0.2)"
+ "background": "linear-gradient(180deg, rgba(75, 85, 99, 0.9), rgba(55, 65, 81, 0.9))",
+ "color": "#f8f9fa",
+ "borderLeft": "1px solid rgba(156, 163, 175, 0.3)",
+ "borderRight": "1px solid rgba(156, 163, 175, 0.3)",
+ "textShadow": "1px 1px 2px rgba(0,0,0,0.8)",
+ "fontWeight": "600"
}
),
@@ -505,24 +522,30 @@ class DashboardComponentManager:
html.Div([
html.Div(
ask_vol_str,
- className="text-start text-danger small fw-bold px-1",
+ className="text-start small fw-bold px-2",
style={
- "background": "rgba(220, 53, 69, 0.8)" if ask_volume > 0 else "transparent",
+ "background": "linear-gradient(270deg, rgba(239, 68, 68, 0.3), rgba(239, 68, 68, 0.9))" if ask_volume > 0 else "transparent",
+ "color": "#ffffff" if ask_volume > 0 else "transparent",
"width": f"{ask_width}%",
- "minHeight": "18px",
+ "minHeight": "22px",
"display": "flex",
"alignItems": "center",
- "justifyContent": "flex-start"
+ "justifyContent": "flex-start",
+ "border": "1px solid rgba(239, 68, 68, 0.5)" if ask_volume > 0 else "none",
+ "borderRadius": "2px",
+ "textShadow": "1px 1px 2px rgba(0,0,0,0.8)",
+ "fontWeight": "600"
}
)
- ], style={"width": "40%", "display": "flex", "justifyContent": "flex-start"})
+ ], style={"width": "40%", "display": "flex", "justifyContent": "flex-start", "padding": "1px"})
], style={
"display": "flex",
"alignItems": "center",
- "marginBottom": "1px",
- "background": "rgba(33, 37, 41, 0.9)",
- "border": "1px solid rgba(255,255,255,0.1)"
+ "marginBottom": "2px",
+ "background": "rgba(17, 24, 39, 0.95)",
+ "border": "1px solid rgba(75, 85, 99, 0.3)",
+ "borderRadius": "3px"
})
])
@@ -538,29 +561,41 @@ class DashboardComponentManager:
if bid_data['usd_volume'] > 0 or ask_data['usd_volume'] > 0 or abs(price - mid_price) <= bucket_size * 5:
rows.append(create_bookmap_row(price, bid_data, ask_data, max_volume))
- # Add header
+ # Add header with improved dark theme styling
header = html.Div([
- html.Div("BIDS", className="text-success text-center fw-bold small", style={"width": "40%"}),
- html.Div("PRICE", className="text-light text-center fw-bold small", style={"width": "20%"}),
- html.Div("ASKS", className="text-danger text-center fw-bold small", style={"width": "40%"})
+ html.Div("BIDS", className="text-center fw-bold small",
+ style={"width": "40%", "color": "#10b981", "textShadow": "1px 1px 2px rgba(0,0,0,0.8)"}),
+ html.Div("PRICE", className="text-center fw-bold small",
+ style={"width": "20%", "color": "#f8f9fa", "textShadow": "1px 1px 2px rgba(0,0,0,0.8)"}),
+ html.Div("ASKS", className="text-center fw-bold small",
+ style={"width": "40%", "color": "#ef4444", "textShadow": "1px 1px 2px rgba(0,0,0,0.8)"})
], style={
"display": "flex",
- "marginBottom": "5px",
- "padding": "5px",
- "background": "rgba(52, 58, 64, 0.9)",
- "border": "1px solid rgba(255,255,255,0.2)"
+ "marginBottom": "8px",
+ "padding": "8px",
+ "background": "linear-gradient(180deg, rgba(31, 41, 55, 0.95), rgba(17, 24, 39, 0.95))",
+ "border": "1px solid rgba(75, 85, 99, 0.4)",
+ "borderRadius": "6px",
+ "boxShadow": "0 2px 4px rgba(0,0,0,0.3)"
})
return html.Div([
header,
html.Div(rows, style={
- "maxHeight": "400px",
+ "maxHeight": "500px",
"overflowY": "auto",
- "background": "rgba(33, 37, 41, 0.95)",
- "border": "1px solid rgba(255,255,255,0.2)",
- "borderRadius": "4px"
+ "background": "linear-gradient(180deg, rgba(17, 24, 39, 0.98), rgba(31, 41, 55, 0.98))",
+ "border": "2px solid rgba(75, 85, 99, 0.4)",
+ "borderRadius": "8px",
+ "boxShadow": "inset 0 2px 4px rgba(0,0,0,0.3)"
})
- ], style={"fontFamily": "monospace"})
+ ], style={
+ "fontFamily": "monospace",
+ "background": "rgba(17, 24, 39, 0.9)",
+ "padding": "8px",
+ "borderRadius": "8px",
+ "border": "1px solid rgba(75, 85, 99, 0.3)"
+ })
def format_cob_data_with_buckets(self, cob_snapshot, symbol, price_buckets, memory_stats, bucket_size=1.0):
"""Format COB data with price buckets for high-frequency display"""
diff --git a/web/layout_manager.py b/web/layout_manager.py
index dcd0bf0..8cc293e 100644
--- a/web/layout_manager.py
+++ b/web/layout_manager.py
@@ -15,12 +15,16 @@ class DashboardLayoutManager:
self.trading_executor = trading_executor
def create_main_layout(self):
- """Create the main dashboard layout"""
+ """Create the main dashboard layout with dark theme"""
return html.Div([
self._create_header(),
self._create_interval_component(),
self._create_main_content()
- ], className="container-fluid")
+ ], className="container-fluid", style={
+ "backgroundColor": "#111827",
+ "minHeight": "100vh",
+ "color": "#f8f9fa"
+ })
def _create_header(self):
"""Create the dashboard header"""
@@ -84,7 +88,12 @@ class DashboardLayoutManager:
html.H5(id=card_id, className=f"{text_class} mb-0 small"),
html.P(label, className="text-muted mb-0 tiny")
], className="card-body text-center p-2")
- ], className="card bg-light", style={"height": "60px"})
+ ], className="card", style={
+ "height": "60px",
+ "backgroundColor": "#1f2937",
+ "border": "1px solid #374151",
+ "color": "#f8f9fa"
+ })
cards.append(card)
return html.Div(