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)