1
votes

I am trying to find the max and min value for each category within source = columndatasource where my stock data is organized into columns by (Open, High, Low, Close, AdjClose, Volume, etc....)

I tried using,

 max(source.data['Close'])
 min(source.data['Close']) 

however, the problem with max(source.data['Open'] is that the values do not update when I update my data when using the slider and select widgets.

Is there a way in which that I can find the min and max of each column that will update each time when I update my data ?

from math import pi
import pandas as pd
import numpy as np
import datetime
import time
from datetime import date
from bokeh.layouts import row, widgetbox, column
from bokeh.models import DataRange1d, LinearAxis, Range1d, ColumnDataSource, PrintfTickFormatter, CDSView, BooleanFilter, NumeralTickFormatter
from bokeh.models.widgets import PreText, Select, DateRangeSlider, Button, DataTable, TableColumn, NumberFormatter
from bokeh.io import curdoc, show, reset_output
from bokeh.plotting import figure, output_file

DEFAULT_TICKERS = ['AAPL','GOOG','NFLX', 'TSLA']
ticker1 = Select(value='AAPL', options = DEFAULT_TICKERS)
range_slider1 = DateRangeSlider(start=date(2014,1,1) , end=date(2017,1,1), value=(date(2014,2,1),date(2016,3,1)), step=1)


def load_ticker(ticker):
    fname = ( '%s.csv' % ticker.lower())
    data = pd.read_csv( fname, header = None, parse_dates = ['Date'],
                  names =['Date','Open','High','Low','Close','AdjClose','Volume'])
    return data

def get_data(t1):
    data = load_ticker(t1)
    return data

def ticker1_change(attrname, old, new):
    update()

def range_slider_change(attrname, old, new):
    update()

def update(selected=None):
    t1 = ticker1.value

    if isinstance(range_slider1.value[0], (int, float)):
        # pandas expects nanoseconds since epoch
        start_date = pd.Timestamp(float(range_slider1.value[0])*1e6)
        end_date = pd.Timestamp(float(range_slider1.value[1])*1e6)
    else:
        start_date = pd.Timestamp(range_slider1.value[0])
        end_date = pd.Timestamp(range_slider1.value[1])

    datarange = get_data(t1)
    datarange['Date'] = pd.to_datetime(datarange['Date'])
    mask = (datarange['Date'] > start_date) & (datarange['Date'] <= end_date)
    data = datarange.loc[mask]
    source.data = source.from_df(data)
    p.title.text = t1

data = get_data(ticker1.value)
source = ColumnDataSource(data)

p = figure(plot_width=900, plot_height=400, x_axis_type='datetime', y_range = Range1d(min(source.data['Close']), max(source.data['Close'])))
p.grid.grid_line_alpha = 0.3
p.line('Date', 'Close', source=source)

ticker1.on_change('value', ticker1_change)
range_slider1.on_change('value', range_slider_change)
update()

layout = column(ticker1,range_slider1, p)                                                              
curdoc().add_root(layout)
curdoc().title = "Stock"
1
It's not clear what you are asking for. If the data changes, the max and min potentially change, and will always have to be recomputed. There's nothing in Bokeh (or Python at all for that matter) to do that automatically, if that is what you are asking for. - bigreddot
I just updated my code. What I am trying to do is have the min and max values update my y_range. However, its not doing that when I make a different selection within my "Select" Widget. - Brandon N

1 Answers

0
votes
  • Yes. Your question is a little convoluted

Short answer: You need to create another "source" that contains the max and min values.

Long answer: Your code is not running properly. I copied/pasted your code ^^ and ran it on a local bokeh server. No output i.e. you need to fix your code first.

But, let's say that your code was running. The only way as of now to auto update a max or min each time you change your bokeh slider or other widget value is to create another source, let's say source2.

source = ColumnDataSource(data_max_min)

Then, match the keys to the same value. In your example^^, it would most likely be date in the dictionary (data_max_min).

E.g.

pd = read_csv('.../AAPL.csv', header=0, index=None)
aapl_close = pd.DataFrame(aapl_df['close'])
aapl_close.index = aapl_df.date
aapl_close

    close
date    
2018/11/23  172.29
2018/11/26  174.62
2018/11/27  174.24

I'm assuming that you want to get a max and min value for each time range that you want to analyze on a rolling basis (or something like that). My code will just get the max for each close (*it will be the same value) just as an example. If you don't understand this, I would recommend reading some of the documentation again.

aapl_max_df = pd.DataFrame()
aapl_max_df['max'] = [max(prices) for prices in aapl_close['close']]
aapl_max_df.index = aapl_close.index

aapl_max_min = {}
dates = aapl_max_min.index
for i in range(aapl_max_min.shape[0]):
    aapl_max_min[aapl_max_min.index.values[i]] = aapl_max_min['max'].values[i]

source2 = ColumnDataSource(data=aapl_max_min[dates[0]])

Then when you update the slider, you will need to update the "date" for for both sources. This is something not yet in your code. There are several examples online on how to do this (https://github.com/bokeh/bokeh/tree/master/examples/app/gapminder).

like so-->

def slider_update(attrname, old, new):
    year = slider.value
    label.text = str(year)
    source.data = data[year]
    source2.data = data[year]