MarketDataCollector/visualizer.py
2025-05-30 12:40:49 +08:00

109 lines
4.2 KiB
Python

import dash
from dash import dcc, html
from dash.dependencies import Output, Input
import plotly.graph_objs as go
import time
from collections import defaultdict
def run_dash(book_history, trade_history, BOOK_HISTORY_SECONDS=5, TRADE_HISTORY_SECONDS=60):
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1("Order Book Depth Chart", style={"textAlign": "center", "color": "#222"}),
dcc.Graph(id='order-book-graph', style={"height": "90vh", "width": "100vw"}),
dcc.Interval(id='interval-component', interval=2*1000, n_intervals=0)
], style={"height": "100vh", "width": "100vw", "margin": 0, "padding": 0, "overflow": "hidden", "backgroundColor": "#f7f7f7"})
@app.callback(
[Output('order-book-graph', 'figure')],
[Input('interval-component', 'n_intervals')]
)
def update_graphs(n):
now = time.time() * 1000 # current time in ms
# Prune book_history to only keep last BOOK_HISTORY_SECONDS
while book_history and now - book_history[0]['timestamp'] > BOOK_HISTORY_SECONDS * 1000:
book_history.popleft()
# Prune trade_history to only keep last TRADE_HISTORY_SECONDS
while trade_history and now - float(trade_history[0]['timestamp']) > TRADE_HISTORY_SECONDS * 1000:
trade_history.popleft()
# Aggregate bids/asks from book_history
bids_dict = {}
asks_dict = {}
for book in book_history:
for price, size, *_ in book['bids']:
price = float(price)
size = float(size)
bids_dict[price] = bids_dict.get(price, 0) + size
for price, size, *_ in book['asks']:
price = float(price)
size = float(size)
asks_dict[price] = asks_dict.get(price, 0) + size
try:
# Prepare and sort bids/asks
bids = sorted([[p, s] for p, s in bids_dict.items()], reverse=True)
asks = sorted([[p, s] for p, s in asks_dict.items()])
# Cumulative sum
bid_prices = [b[0] for b in bids]
bid_sizes = [b[1] for b in bids]
ask_prices = [a[0] for a in asks]
ask_sizes = [a[1] for a in asks]
bid_cumsum = [sum(bid_sizes[:i+1]) for i in range(len(bid_sizes))]
ask_cumsum = [sum(ask_sizes[:i+1]) for i in range(len(ask_sizes))]
except Exception as e:
bid_prices, bid_cumsum, ask_prices, ask_cumsum = [], [], [], []
fig = go.Figure()
# Add order book lines (primary y-axis)
fig.add_trace(go.Scatter(
x=bid_prices, y=bid_cumsum, mode='lines', name='Bids',
line=dict(color='green'), fill='tozeroy', yaxis='y1'
))
fig.add_trace(go.Scatter(
x=ask_prices, y=ask_cumsum, mode='lines', name='Asks',
line=dict(color='red'), fill='tozeroy', yaxis='y1'
))
trade_volume_by_price = defaultdict(float)
for trade in trade_history:
price_bin = round(float(trade['price']), 2)
trade_volume_by_price[price_bin] += float(trade['size'])
prices = list(trade_volume_by_price.keys())
volumes = list(trade_volume_by_price.values())
# Sort by price for display
sorted_pairs = sorted(zip(prices, volumes))
prices = [p for p, v in sorted_pairs]
volumes = [v for p, v in sorted_pairs]
# Add trade volume bars (secondary y-axis)
fig.add_trace(go.Bar(
x=prices, y=volumes, marker_color='#7ec8e3', name='Trade Volume',
opacity=0.7, yaxis='y2'
))
# Update layout for dual y-axes
fig.update_layout(
title='Order Book Depth & Realized Trade Volume by Price',
xaxis=dict(title='Price'),
yaxis=dict(title='Cumulative Size', side='left'),
yaxis2=dict(
title='Traded Volume',
overlaying='y',
side='right',
showgrid=False
),
template='plotly_dark'
)
return [fig]
app.run(debug=True, use_reloader=False)