import os import ccxt import time import datetime import dash from dash.dependencies import Input, Output, Event import dash_core_components as dcc import dash_html_components as html import plotly.graph_objs as go import threading import dataset from collections import deque def ensure_dir(directory): """""" if not os.path.exists(directory): os.makedirs(directory) class CryptoDataGrabber(object): def __init__(self): """""" self.exchange = ccxt.poloniex() self.exchange.load_markets() self.delay_seconds = self.exchange.rateLimit / 1000 self.symbols = self.exchange.markets self.timeframe = '1d' self.db_url = 'sqlite:///databases/market_prices.db' self.deques = dict() self.ohlcv = dict() self.database = dataset.connect(self.db_url) ensure_dir('databases') for symbol in self.symbols: if self.exchange.has['fetchOHLCV']: print('Obtaining OHLCV data') data = self.exchange.fetch_ohlcv(symbol, self.timeframe) data = list(zip(*data)) data[0] = [datetime.datetime.fromtimestamp(ms / 1000) for ms in data[0]] self.ohlcv[symbol] = data time.sleep(self.delay_seconds) else: print('No OHLCV data available') self.deques[symbol] = deque() if len(self.database[symbol]): for e in self.database[symbol]: entry = (e['bid'], e['ask'], e['spread'], e['time']) self.deques[symbol].append(entry) del self.database self.thread = threading.Thread(target=self.__update) self.thread.daemon = True self.thread.start() def get_symbols(self): """""" return self.deques.keys() def get_prices(self, symbol): """""" return self.deques[symbol] def get_ohlcv(self, symbol): """""" data = self.ohlcv[symbol] return data[0], data[1], data[2], data[3], data[4], data[5] def __update(self): """ https://github.com/ccxt-dev/ccxt/wiki/Manual#market-price """ self.database = dataset.connect(self.db_url) while True: for symbol in self.symbols: start_time = time.clock() orders = self.exchange.fetch_order_book(symbol) bid = orders['bids'][0][0] if len(orders['bids']) > 0 else None ask = orders['asks'][0][0] if len(orders['asks']) > 0 else None spread = (ask - bid) if (bid and ask) else None dtime = datetime.datetime.now() self.deques[symbol].append((bid, ask, spread, dtime)) self.database.begin() try: self.database[symbol].insert({ 'ask': ask, 'bid': bid, 'spread': spread, 'time': dtime }) self.database.commit() except: self.database.rollback() time.sleep(self.delay_seconds - (time.clock() - start_time)) data = CryptoDataGrabber() selected_dropdown_value = 'ETH/BTC' app = dash.Dash() app.layout = html.Div([ html.Div([ html.H1('Poloniex Nerd', id='h1_title'), dcc.Dropdown( id='symbol-dropdown', options=[{'label': key, 'value': key} for key in data.get_symbols()], value=selected_dropdown_value ), html.Div([ dcc.Graph( id='ohlc', config={ 'displayModeBar': False } ), ], className="row"), html.Div([ dcc.Graph( id='v', config={ 'displayModeBar': False } ), ], className="row"), html.Div([ html.Div([ dcc.Graph( id='market-prices-graph', config={ 'displayModeBar': False } ), dcc.Graph( id='spread-graph', config={ 'displayModeBar': False } ), ], className="eight columns"), html.Div([ dcc.Graph( id='market-prices-hist', config={ 'displayModeBar': False } ), dcc.Graph( id='spread-hist', config={ 'displayModeBar': False } ), ], className="four columns") ], className="row"), dcc.Interval(id='graph-speed-update', interval=2000), ], className="row") ]) app.css.append_css({ 'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css' }) @app.callback(Output('h1_title', 'children'), [Input('symbol-dropdown', 'value')]) def change_plot(value): global selected_dropdown_value selected_dropdown_value = value return 'Market Prices ' + str(value) @app.callback(Output('ohlc', 'figure'), [Input('symbol-dropdown', 'value')]) def plot_olhc(value): global data dates, open_data, high_data, low_data, close_data, _ \ = data.get_ohlcv(value) return { 'data': [go.Ohlc(x=dates, open=open_data, high=high_data, low=low_data, close=close_data)], 'layout': dict(title="OHLC") } @app.callback(Output('v', 'figure'), [Input('symbol-dropdown', 'value')]) def plot_v(value): global data dates, _, _, _, _, volume = data.get_ohlcv(value) return { 'data': [go.Bar(x=dates, y=volume)], 'layout': dict(title="Volume") } @app.callback(Output('market-prices-graph', 'figure'), events=[Event('graph-speed-update', 'interval')]) def update_market_prices(): global selected_dropdown_value global data prices = data.get_prices(selected_dropdown_value) prices = [list(p) for p in zip(*prices)] if len(prices) > 0: traces = [] x = list(prices[3]) for i, key in enumerate(['bid', 'ask']): trace = go.Scatter(x=x, y=prices[i], name=key, opacity=0.8) traces.append(trace) return { 'data': traces, 'layout': dict(title="Market Prices") } @app.callback(Output('market-prices-hist', 'figure'), events=[Event('graph-speed-update', 'interval')]) def update_market_prices_hist(): global selected_dropdown_value global data prices = data.get_prices(selected_dropdown_value) prices = [list(p) for p in zip(*prices)] if len(prices) > 0: traces = [] for i, key in enumerate(['bid', 'ask']): trace = go.Histogram(x=prices[i][-200:], name=key, opacity=0.8) traces.append(trace) return { 'data': traces, 'layout': dict(title="Market Prices Histogram (200 Most Recent)") } @app.callback(Output('spread-graph', 'figure'), events=[Event('graph-speed-update', 'interval')]) def update_spread(): global selected_dropdown_value global data prices = data.get_prices(selected_dropdown_value) prices = [list(p) for p in zip(*prices)] if len(prices) > 0: traces = [] trace = go.Scatter(x=list(prices[3]), y=list(prices[2]), name='spread', line=dict(color='rgb(114, 186, 59)'), fill='tozeroy', fillcolor='rgba(114, 186, 59, 0.5)', mode='none') traces.append(trace) return { 'data': traces, 'layout': dict(title="Spread") } @app.callback(Output('spread-hist', 'figure'), events=[Event('graph-speed-update', 'interval')]) def update_spread_hist(): global selected_dropdown_value global data prices = data.get_prices(selected_dropdown_value) prices = [list(p) for p in zip(*prices)] if len(prices) > 0: traces = [] trace = go.Histogram(x=list(prices[2][-200:]), name='spread', marker=dict(color='rgba(114, 186, 59, 0.5)')) traces.append(trace) return { 'data': traces, 'layout': dict(title="Spread Histogram (200 Most Recent)") } if __name__ == '__main__': app.run_server() print(self.server)