""" This file implements a bokeh applet for returns distribution. It requires a bokeh-server. See the README.md file in this directory for instructions on running. """ import logging logging.basicConfig(level=logging.DEBUG) import numpy as np from bokeh.plotting import figure from bokeh.models import Plot, ColumnDataSource, DataRange1d, TapTool, Circle from bokeh.properties import Instance from bokeh.server.app import bokeh_app from bokeh.server.utils.plugins import object_page from bokeh.models.widgets import HBox, VBox, Slider, TextInput, VBoxForm class StockApp(VBox): """ Applet for exploring the returns of a stock """ extra_generated_classes = [['StockApp', 'StockApp', 'VBox']] stock_plot = Instance(Plot) source = Instance(ColumnDataSource) @classmethod def create_stock(cls, source): # xdr1 = DataRange1d(sources=[source.columns("x")]) # ydr1 = DataRange1d(sources=[source.columns("y")]) # plot1 = figure(title="Outliers", x_range=xdr1, y_range=ydr1, plot_width=650, plot_height=400) stock_plot = figure(title="", plot_width=650, plot_height=400) # stock_plot.tools.append(TapTool(plot=stock_plot)) # stock_plot.line(x="x", y="values", size=12, color="blue", line_dash=[2, 4], source=source) return stock_plot # plot1.scatter(x="x", y="y", size="size", fill_color="red", source=source) @classmethod def create(clz): """One-time creation of app's objects. This function is called once, and is responsible for creating all objects (plots, datasources, etc) """ self = clz() n_vals = 1000 self.source = ColumnDataSource( data=dict( top=[], bottom=0, left=[], right=[], x= np.arange(n_vals), values= np.random.randn(n_vals) )) # Generate a figure container self.stock_plot = clz.create_stock(self.source) self.update_data() self.children.append(self.stock_plot) def setup_events(self): """Attaches the on_change event to the value property of the widget. The callback is set to the input_change method of this app. """ super(StockApp, self).setup_events() logging.debug("%s" % str(self.source)) # Slider event registration # self.source.on_change('selected', self, 'on_selection_change') print("+++++++++++++++++++++++++++++++++") print(self) self.stock_plot.on_change('value', self, 'input_change') # self.outliers_source.on_change('selected', self, 'on_selection_change') # for w in ["bins"]: # getattr(self, w).on_change('value', self, 'input_change') def input_change(self, obj, attrname, old, new): """Executes whenever the input form changes. It is responsible for updating the plot, or anything else you want. Args: obj : the object that changed attrname : the attr that changed old : old value of attr new : new value of attr """ self.update_data() def update_data(self): """Called each time that any watched property changes. This updates the sin wave data with the most recent values of the sliders. This is stored as two numpy arrays in a dict into the app's data histogram_source property. """ logging.debug("update_data") n_vals = 1000 self.source.data = dict(top=hist, bottom=0, left=0, right = 0, x=np.arange(n_vals), values=np.random.randn(n_vals)) def on_selection_change(self, attr, _, inds, x): pass # The following code adds a "/bokeh/sliders/" url to the bokeh-server. This # URL will render this sine wave sliders app. If you don't want to serve this # applet from a Bokeh server (for instance if you are embedding in a separate # Flask application), then just remove this block of code. @bokeh_app.route("/bokeh/stock/") @object_page("StockApp") def make_stock(): logging.debug('creating') app = StockApp.create() logging.debug('created') return app