cleanup new COB ladder
This commit is contained in:
@ -405,24 +405,21 @@ class DashboardComponentManager:
|
||||
], className="text-center")
|
||||
|
||||
def _create_cob_ladder_panel(self, bids, asks, mid_price, symbol=""):
|
||||
"""Creates the right panel with the compact COB ladder."""
|
||||
"""Creates Bookmap-style COB display with horizontal bars extending from center price."""
|
||||
# Use symbol-specific bucket sizes: ETH = $1, BTC = $10
|
||||
bucket_size = 1.0 if "ETH" in symbol else 10.0
|
||||
num_levels = 5
|
||||
num_levels = 20 # Show 20 levels each side
|
||||
|
||||
def aggregate_buckets(orders):
|
||||
buckets = {}
|
||||
for order in orders:
|
||||
# Handle both dictionary format and ConsolidatedOrderBookLevel objects
|
||||
if hasattr(order, 'price'):
|
||||
# ConsolidatedOrderBookLevel object
|
||||
price = order.price
|
||||
size = order.total_size
|
||||
volume_usd = order.total_volume_usd
|
||||
else:
|
||||
# Dictionary format (legacy)
|
||||
price = order.get('price', 0)
|
||||
# Handle both old format (size) and new format (total_size)
|
||||
size = order.get('total_size', order.get('size', 0))
|
||||
volume_usd = order.get('total_volume_usd', size * price)
|
||||
|
||||
@ -437,68 +434,133 @@ class DashboardComponentManager:
|
||||
bid_buckets = aggregate_buckets(bids)
|
||||
ask_buckets = aggregate_buckets(asks)
|
||||
|
||||
# Calculate max volume for scaling
|
||||
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
|
||||
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)]
|
||||
|
||||
def create_ladder_row(price, bucket_data, max_vol, row_type):
|
||||
usd_volume = bucket_data.get('usd_volume', 0)
|
||||
crypto_volume = bucket_data.get('crypto_volume', 0)
|
||||
def create_bookmap_row(price, bid_data, ask_data, max_vol):
|
||||
"""Create a Bookmap-style row with horizontal bars extending from center"""
|
||||
bid_volume = bid_data.get('usd_volume', 0)
|
||||
ask_volume = ask_data.get('usd_volume', 0)
|
||||
|
||||
progress = (usd_volume / max_vol) * 100 if max_vol > 0 else 0
|
||||
color = "danger" if row_type == 'ask' else "success"
|
||||
text_color = "text-danger" if row_type == 'ask' else "text-success"
|
||||
# Calculate bar widths (0-100%)
|
||||
bid_width = (bid_volume / max_vol) * 100 if max_vol > 0 else 0
|
||||
ask_width = (ask_volume / max_vol) * 100 if max_vol > 0 else 0
|
||||
|
||||
# Format USD volume (no $ symbol)
|
||||
if usd_volume > 1e6:
|
||||
usd_str = f"{usd_volume/1e6:.1f}M"
|
||||
elif usd_volume > 1e3:
|
||||
usd_str = f"{usd_volume/1e3:.0f}K"
|
||||
else:
|
||||
usd_str = f"{usd_volume:,.0f}"
|
||||
# Format volumes
|
||||
def format_volume(vol):
|
||||
if vol > 1e6:
|
||||
return f"{vol/1e6:.1f}M"
|
||||
elif vol > 1e3:
|
||||
return f"{vol/1e3:.0f}K"
|
||||
elif vol > 0:
|
||||
return f"{vol:,.0f}"
|
||||
return ""
|
||||
|
||||
# Format crypto volume (no unit symbol)
|
||||
if crypto_volume > 1000:
|
||||
crypto_str = f"{crypto_volume/1000:.1f}K"
|
||||
elif crypto_volume > 1:
|
||||
crypto_str = f"{crypto_volume:.1f}"
|
||||
else:
|
||||
crypto_str = f"{crypto_volume:.3f}"
|
||||
bid_vol_str = format_volume(bid_volume)
|
||||
ask_vol_str = format_volume(ask_volume)
|
||||
|
||||
return html.Div([
|
||||
# Price level row
|
||||
html.Div([
|
||||
# Bid side (left) - green bar extending right
|
||||
html.Div([
|
||||
html.Div(
|
||||
bid_vol_str,
|
||||
className="text-end text-success small fw-bold px-1",
|
||||
style={
|
||||
"background": "rgba(40, 167, 69, 0.8)" if bid_volume > 0 else "transparent",
|
||||
"width": f"{bid_width}%",
|
||||
"minHeight": "18px",
|
||||
"display": "flex",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "flex-end",
|
||||
"marginLeft": "auto"
|
||||
}
|
||||
)
|
||||
], style={"width": "40%", "display": "flex", "justifyContent": "flex-end"}),
|
||||
|
||||
# Price in center
|
||||
html.Div(
|
||||
f"{price:,.0f}",
|
||||
className="text-center small fw-bold text-light px-2",
|
||||
style={
|
||||
"width": "20%",
|
||||
"minHeight": "18px",
|
||||
"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)"
|
||||
}
|
||||
),
|
||||
|
||||
# Ask side (right) - red bar extending left
|
||||
html.Div([
|
||||
html.Div(
|
||||
ask_vol_str,
|
||||
className="text-start text-danger small fw-bold px-1",
|
||||
style={
|
||||
"background": "rgba(220, 53, 69, 0.8)" if ask_volume > 0 else "transparent",
|
||||
"width": f"{ask_width}%",
|
||||
"minHeight": "18px",
|
||||
"display": "flex",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "flex-start"
|
||||
}
|
||||
)
|
||||
], style={"width": "40%", "display": "flex", "justifyContent": "flex-start"})
|
||||
|
||||
], style={
|
||||
"display": "flex",
|
||||
"alignItems": "center",
|
||||
"marginBottom": "1px",
|
||||
"background": "rgba(33, 37, 41, 0.9)",
|
||||
"border": "1px solid rgba(255,255,255,0.1)"
|
||||
})
|
||||
])
|
||||
|
||||
return html.Tr([
|
||||
html.Td(f"${price:,.0f}", className=f"{text_color} price-level small"),
|
||||
html.Td(
|
||||
dbc.Progress(value=progress, color=color, className="vh-25 compact-progress"),
|
||||
className="progress-cell p-0"
|
||||
),
|
||||
html.Td(usd_str, className="volume-level text-end fw-bold small p-0 pe-1"),
|
||||
html.Td(crypto_str, className="volume-level text-start small text-muted p-0 ps-1")
|
||||
], className="compact-ladder-row p-0")
|
||||
# Create all price levels
|
||||
all_levels = sorted(set(ask_levels + bid_levels + [center_bucket]), reverse=True)
|
||||
|
||||
rows = []
|
||||
for price in all_levels:
|
||||
bid_data = bid_buckets.get(price, {'usd_volume': 0})
|
||||
ask_data = ask_buckets.get(price, {'usd_volume': 0})
|
||||
|
||||
# Only show rows with some volume or near mid price
|
||||
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))
|
||||
|
||||
def get_bucket_data(buckets, price):
|
||||
return buckets.get(price, {'usd_volume': 0, 'crypto_volume': 0})
|
||||
# Add header
|
||||
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%"})
|
||||
], style={
|
||||
"display": "flex",
|
||||
"marginBottom": "5px",
|
||||
"padding": "5px",
|
||||
"background": "rgba(52, 58, 64, 0.9)",
|
||||
"border": "1px solid rgba(255,255,255,0.2)"
|
||||
})
|
||||
|
||||
ask_rows = [create_ladder_row(p, get_bucket_data(ask_buckets, p), max_volume, 'ask') for p in sorted(ask_levels, reverse=True)]
|
||||
bid_rows = [create_ladder_row(p, get_bucket_data(bid_buckets, p), max_volume, 'bid') for p in sorted(bid_levels, reverse=True)]
|
||||
|
||||
mid_row = html.Tr([
|
||||
html.Td(f"${mid_price:,.0f}", colSpan=4, className="text-center fw-bold small mid-price-row p-0")
|
||||
])
|
||||
|
||||
ladder_table = html.Table([
|
||||
html.Thead(html.Tr([
|
||||
html.Th("Price", className="small p-0"),
|
||||
html.Th("Volume", className="small p-0"),
|
||||
html.Th("USD", className="small text-end p-0 pe-1"),
|
||||
html.Th("Crypto", className="small text-start p-0 ps-1")
|
||||
])),
|
||||
html.Tbody(ask_rows + [mid_row] + bid_rows)
|
||||
], className="table table-sm table-borderless cob-ladder-table-compact m-0 p-0") # Compact classes
|
||||
|
||||
return ladder_table
|
||||
return html.Div([
|
||||
header,
|
||||
html.Div(rows, style={
|
||||
"maxHeight": "400px",
|
||||
"overflowY": "auto",
|
||||
"background": "rgba(33, 37, 41, 0.95)",
|
||||
"border": "1px solid rgba(255,255,255,0.2)",
|
||||
"borderRadius": "4px"
|
||||
})
|
||||
], style={"fontFamily": "monospace"})
|
||||
|
||||
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"""
|
||||
|
Reference in New Issue
Block a user