109 lines
4.2 KiB
Python
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)
|