How can I merge the two functions given below to achieve something like the histogram example. Any button or drop down would do fine.
If you run the function, you get a nice Candlesticks chart with the functionality of removing non trading day gaps.
def plot_candlesticks(df, names = ('DATE','OPEN','CLOSE','LOW','HIGH'), mv:list = [200], slider:bool = False, fig_size:bool = (1400,700), plot:bool = True):
'''
Plot a candlestick on a given dataframe
args:
df: DataFrame
names: Tuple of column names showing ('DATE','OPEN','CLOSE','LOW','HIGH')
mv: Moving Averages
slider: Whether to have below zoom slider or not
fig_size: Size of Figure as (Width, Height)
plotting: Whether to plot the figure or just return the figure for firther modifications
'''
freq = 5 # 5 min candle
candle_text = f"{str(freq)} Min"
stocks = df.copy()
stocks.sort_index(ascending=False, inplace = True) # Without reverse, recent rolling mean will be either NaN or equal to the exact value
Date, Open, Close, Low, High = names
mv = [] if not mv else mv # just in case you don't want to have any moving averages
colors = sample(['black','magenta','teal','brown','violet'],len(mv))
# To remove, non-trading days, grab first and last observations from df.date and make a continuous date range from that
start = stocks['DATE'].iloc[0] - timedelta(days=1)
end = stocks['DATE'].iloc[-1] + timedelta(days=1)
dt_all = pd.date_range(start=start,end=end, freq = f'{str(freq)}min')
# check which dates from your source that also accur in the continuous date range
dt_obs = [d.strftime("%Y-%m-%d %H:%M:%S") for d in stocks['DATE']]
# isolate missing timestamps
dt_breaks = [d for d in dt_all.strftime("%Y-%m-%d %H:%M:%S").tolist() if not d in dt_obs]
rangebreaks=[dict(dvalue = freq*60*1000, values=dt_breaks)]
range_selector = dict(buttons = list([dict(step = 'all', label = 'All')]))
candle = go.Figure(data = [go.Candlestick(opacity = 0.9, x = stocks[Date], name = 'X',
open = stocks[Open], high = stocks[High], low = stocks[Low], close = stocks[Close]),])
for i in range(len(mv)):
stocks[f'{str(mv[i])}-SMA'] = stocks[Close].rolling(mv[i], min_periods = 1).mean()
candle.add_trace(go.Scatter(name=f'{str(mv[i])} MA',x=stocks[Date], y=stocks[f'{str(mv[i])}-SMA'],
line=dict(color=colors[i], width=1.7)))
candle.update_xaxes(title_text = 'Date', rangeslider_visible = slider, rangeselector = range_selector, rangebreaks=rangebreaks)
candle.update_layout(autosize = False, width = fig_size[0], height = fig_size[1],
title = {'text': f"{stocks['SYMBOL'][0]} : {str(candle_text)} Candles",'y':0.97,'x':0.5,
'xanchor': 'center','yanchor': 'top'},
margin=dict(l=30,r=30,b=30,t=30,pad=2),
paper_bgcolor="lightsteelblue")
candle.update_yaxes(title_text = 'Price in Rupees', tickprefix = u"\u20B9" ) # Rupee symbol
if plot:
candle.show()
return candle
and running the below code resamples your data.
def resample_data(self,to:str = '15min', names:tuple = ('OPEN','CLOSE','LOW','HIGH','DATE')):
'''
Resample the data from 5 Minutes to 15 or 75 Minutes
args:
data: Dataframe of Daily data
to: One of [15M, 75M]
'''
Open, Close, Low, High, Date = names
data = data.resample(to,on=Date).agg({Open:'first', High:'max', Low: 'min', Close:'last'})
return data.sort_index(ascending = False).reset_index()
Is there a functionality when I click 15M / 75M button in my chart, it shows me exactly the same data but resampled? Just like there is functionality in online trading softwares.
no sample data so I have used https://plotly.com/python/candlestick-charts/ sample
at core use https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html and change trace contents with resampled data
plus using https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html for events from widgets
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import ipywidgets as widgets
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv",
parse_dates=["Date"],
)
fig = go.FigureWidget(
data=[
go.Candlestick(
x=df["Date"],
open=df["AAPL.Open"],
high=df["AAPL.High"],
low=df["AAPL.Low"],
close=df["AAPL.Close"],
)
]
).update_layout(margin={"t": 30, "b": 0, "l": 0, "r": 0})
out = widgets.Output(layout={"border": "1px solid black"})
out.append_stdout("Output appended with append_stdout\n")
reset = widgets.Button(description="Reset")
slider = widgets.IntSlider(
value=1,
min=1,
max=10,
step=1,
description='Days:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='d'
)
#out.capture()
def on_slider_change(v):
print(f"slider: {v['new']}")
dfr = df.resample(f"{v['new']}B", on="Date").mean().reset_index()
t = fig.data[0]
t.update(
x=dfr["Date"],
open=dfr["AAPL.Open"],
high=dfr["AAPL.High"],
low=dfr["AAPL.Low"],
close=dfr["AAPL.Close"],
)
#out.capture()
def on_reset_clicked(b):
print("reset")
t = fig.data[0]
t.update(
x=df["Date"],
open=df["AAPL.Open"],
high=df["AAPL.High"],
low=df["AAPL.Low"],
close=df["AAPL.Close"],
)
out.clear_output()
reset.on_click(on_reset_clicked)
slider.observe(on_slider_change, names='value')
widgets.VBox([widgets.HBox([reset, slider]), widgets.VBox([fig, out])])
I am trying to update Bokeh DataTable by selecting a plot.Not sure why it's not getting updated?In the code below,source_devon is ColumnDataSource for the plot- I wanted Datatable to get updated when we select a particular portion.Currently DataTable is created(initialization),but it doesn't changes on selection
from bokeh.io import curdoc
from bokeh.layouts import layout,row
from bokeh.models import CDSView,HoverTool,GroupFilter,ColumnDataSource,Button,Select,TextInput,Slider,DataTable,TableColumn,DateFormatter,LinearAxis,Range1d,CustomJS,Rect
from bokeh.plotting import figure,output_file,show
from datetime import datetime, timedelta
from bokeh.client import push_session
import pandas as pd
import numpy as np
TOOLS='pan,wheel_zoom,box_zoom,reset,tap,save,lasso_select,xbox_select'
# Select widget
ccy_options = ['AUDUSD', 'USDJPY']
menu = Select(options=['AUDUSD','USDJPY'], value='AUDUSD')
slider = Slider(start=-1000, end=10000, step=1000, value=-1000, title='Volume Cutoff')
# Function to get Order/Trade/Price Datasets
def get_combined_dataset(src,name):
df = src[(src.CCYPAIR == name)].copy()
return ColumnDataSource(data=df)
# Function to Make Plots
def make_plot(source_order):
x = 'DATE'
y = 'PRICE'
y1 = 'Volume'
size = 10
alpha = 0.5
hover = HoverTool(
tooltips = [
('OrderId', '#ORDER_ID_108'),
('Volume', '#Volume'),
('Price', '#PRICE')
]
)
view1 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='ORDER'),GroupFilter(column_name='SIDE',group='B')])
view2 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='ORDER'),GroupFilter(column_name='SIDE',group='S')])
view3 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='TRADE'),GroupFilter(column_name='SIDE',group='B')])
view4 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='TRADE'),GroupFilter(column_name='SIDE',group='S')])
view5 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='DevonTrade')])
view6 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='Prices')])
plot2 = figure(plot_width=1000, plot_height=300, tools=[hover, TOOLS],
title='Agg. Position Snapshot for Devon',
x_axis_label='Date', y_axis_label='Price',x_axis_type="datetime")
plot = figure(plot_width=1000, plot_height=300,tools=TOOLS,x_axis_type="datetime",title='Order/Execution Snapshot with Price Levels')
plot.circle(x=x,y=y,source=source_order,view=view1,alpha=0.6,color='blue')
plot.circle(x=x,y=y,source=source_order,view=view2,alpha=0.6,color='red')
plot.triangle(x=x,y=y,source=source_order,view=view3,alpha=0.6,color='blue')
plot.triangle(x=x,y=y,source=source_order,view=view4,alpha=0.6,color='red')
plot.line(x=x,y=y,source=source_order,view=view6,color='green')
plot2.line(x=x,y=y1,source=source_order,view=view5,color='blue')
plot.legend.location = 'top_left'
return plot,plot2
def make_table(source):
columns = [
TableColumn(field='DATE', title="DATE", formatter=DateFormatter()),
TableColumn(field='CCYPAIR', title="CCYPAIR"),
TableColumn(field='SIDE', title="SIDE"),
TableColumn(field='PRICE', title="PRICE"),
TableColumn(field='TYPE', title="TYPE"),
TableColumn(field='Volume', title="Volume"),
TableColumn(field='ORDER_ID_108', title="ORDERID"),
]
data_table = DataTable(source=source, columns=columns, width=1000, height=200)
return data_table
def update_plot(attrname, old, new):
newccy = menu.value
newvalue = slider.value
src_data_table = get_combined_dataset(Combined,newccy)
DisplayData.data.update(src_data_table.data)
def update_plot(attrname, old, new):
newccy = menu.value
newvalue = slider.value
src_data_table = get_combined_dataset(Combined,newccy)
DisplayData.data.update(src_data_table.data)
def selection_change(attrname, old, new):
data = get_all_dataset(Combined,menu.value)
selected = DisplayData.selected['1d']['indices']
if selected:
data = data.iloc[selected, :]
update_datatable(data)
def update_datatable(data):
src_data_table = get_combined_dataset(data,menu.value)
s2.data.update(src_data_table.data)
# Input Files
date_today = datetime.now()
days = pd.date_range(date_today, date_today + timedelta(5), freq='D')
Combined1 = {'DATE': days,
'CCYPAIR': ['USDJPY', 'USDJPY', 'USDJPY','USDJPY', 'USDJPY', 'USDJPY'],
'SIDE' : ['B', 'B', 'B','B', 'B', 'B'],
'PRICE': [100.00, 200.00, 300.00,100.00, 200.00, 300.00],
'TYPE' : ['ORDER', 'ORDER', 'ORDER','DevonTrade', 'DevonTrade', 'DevonTrade'],
'Volume': [100, 200, 300, 100, 200, 300],
'ORDER_ID_108': [111,222,333,111,222,333]
}
Combined = pd.DataFrame(Combined1)
DisplayData = get_combined_dataset(Combined,menu.value)
plot,plot2 = make_plot(DisplayData)
menu.on_change('value', update_plot)
plot.x_range = plot2.x_range
s2 = ColumnDataSource(data=dict(DATE=[],CCYPAIR=[],SIDE=[],PRICE=[],TYPE=[],Volume=[],ORDER_ID_108=[]))
columns = [
TableColumn(field='DATE', title="DATE", formatter=DateFormatter()),
TableColumn(field='CCYPAIR', title="CCYPAIR"),
TableColumn(field='SIDE', title="SIDE"),
TableColumn(field='PRICE', title="PRICE"),
TableColumn(field='TYPE', title="TYPE"),
TableColumn(field='Volume', title="Volume"),
TableColumn(field='ORDER_ID_108', title="ORDER_ID_108")
]
data_table = DataTable(source=s2,columns=columns,width=1000, height=200)
layout = layout([menu],
[plot],
[plot2],
[data_table])
curdoc().add_root(layout)
DisplayData.on_change('selected', selection_change)
Your main issue is that the source you are updating does not have the same fields as the source containing the original data. In your callback you are trying to access d2['DATES'] and d2['PRICES'], however you defined the fields as 'x' and 'y'. See below code which has corrected this. I also defined the plotting ranges of the second plot so the data will appear.
from bokeh.io import curdoc
from bokeh.layouts import layout,row
from bokeh.models import HoverTool,ColumnDataSource,Button,Select,TextInput,Slider,DataTable,TableColumn,DateFormatter,LinearAxis,Range1d,CustomJS,Rect
from bokeh.plotting import figure,output_file,show
from datetime import datetime, timedelta
from bokeh.client import push_session
import pandas as pd
import numpy as np
TOOLS='pan,wheel_zoom,box_zoom,reset,tap,save,lasso_select,xbox_select'
# Select widget
menu = Select(options=['AUDUSD','USDJPY'], value='USDJPY')
# Function to get Order/Trade/Price Datasets
def get_order_dataset(src,name):
df = src[(src.CCYPAIR == name) & (src.TYPE == 'ORDER') & (src.SIDE == 'B')].copy()
return ColumnDataSource(data=df)
# Function to Make Plots
def make_plot(source_order):
x = 'DATE'
y = 'PRICE'
size = 10
alpha = 0.5
hover = HoverTool(
tooltips = [
('OrderId', '#ORDER_ID_108'),
('Volume', '#Volume'),
('Price', '#PRICE')
]
)
plot = figure(plot_width=1000, plot_height=300, tools=[hover, TOOLS],
title='Order/Execution Snapshot with Price Levels',
x_axis_label='Date', y_axis_label='Price',x_axis_type="datetime",active_drag="xbox_select")
plot.circle(x=x, y=y, size=size, alpha=alpha, color='blue',
legend='Orders', source=source_order,selection_color="orange")
plot.legend.location = 'top_left'
return plot
def update_plot(attrname, old, new):
newccy = menu.value
src_order = get_order_dataset(Combined,newccy)
source_order.data.update(src_order.data)
date_today = datetime.now()
days = pd.date_range(date_today, date_today + timedelta(2), freq='D')
Combined1 = {'DATE': days,
'CCYPAIR': ['USDJPY', 'USDJPY', 'USDJPY'],
'SIDE' : ['B', 'B', 'B'],
'PRICE': [100.00, 200.00, 300.00],
'TYPE' : ['ORDER', 'ORDER', 'ORDER'],
'Volume': [100, 200, 300],
'ORDER_ID_108': [111,222,333]
}
Combined = pd.DataFrame(Combined1)
source_order = get_order_dataset(Combined,menu.value)
plot = make_plot(source_order)
menu.on_change('value', update_plot)
s2 = ColumnDataSource(data=dict(DATE=[], PRICE=[]))
p2 = figure(plot_width=1000, plot_height=400,
tools="", title="Watch Here",x_axis_type="datetime", y_range=(90,310),x_range=(days[0],days[-1]))
p2.circle('DATE', 'PRICE', source=s2, alpha=0.6, size=10)
source_order.callback = CustomJS(args=dict(s2=s2), code="""
var inds = cb_obj.selected['1d'].indices;
console.log(inds)
var d1 = cb_obj.data;
var d2 = s2.data;
d2['DATE'] = []
d2['PRICE'] = []
for (i = 0; i < inds.length; i++) {
d2['DATE'].push(d1['DATE'][inds[i]])
d2['PRICE'].push(d1['PRICE'][inds[i]])
}
s2.change.emit();""")
layout = layout([menu],
[plot],
[p2])
curdoc().add_root(layout)
from bokeh.io import curdoc
from bokeh.layouts import layout,row
from bokeh.models import CDSView,HoverTool,GroupFilter,ColumnDataSource,Button,Select,TextInput,Slider,DataTable,TableColumn,DateFormatter,LinearAxis,Range1d,CustomJS,Rect
from bokeh.plotting import figure,output_file,show
from datetime import datetime, timedelta
from bokeh.client import push_session
import pandas as pd
import numpy as np
TOOLS='pan,wheel_zoom,box_zoom,reset,tap,save,lasso_select,xbox_select'
# Select widget
ccy_options = ['AUDUSD', 'USDJPY']
menu = Select(options=['AUDUSD','USDJPY'], value='AUDUSD')
slider = Slider(start=-1000, end=10000, step=1000, value=-1000, title='Volume Cutoff')
# Function to get Order/Trade/Price Datasets
def get_combined_dataset(src,name):
df = src[(src.CCYPAIR == name)].copy()
return ColumnDataSource(data=df)
# Function to Make Plots
def make_plot(source_order):
x = 'DATE'
y = 'PRICE'
y1 = 'Volume'
size = 10
alpha = 0.5
hover = HoverTool(
tooltips = [
('OrderId', '#ORDER_ID_108'),
('Volume', '#Volume'),
('Price', '#PRICE')
]
)
view1 = CDSView(source=source_order, filters= [GroupFilter(column_name='TYPE',group='ORDER'),GroupFilter(column_name='SIDE',group='B')])
view2 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='ORDER'),GroupFilter(column_name='SIDE',group='S')])
view3 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='TRADE'),GroupFilter(column_name='SIDE',group='B')])
view4 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='TRADE'),GroupFilter(column_name='SIDE',group='S')])
view5 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='DevonTrade')])
view6 = CDSView(source=source_order, filters=[GroupFilter(column_name='TYPE',group='Prices')])
plot2 = figure(plot_width=1000, plot_height=300, tools=[hover, TOOLS],
title='Agg. Position Snapshot for Devon',
x_axis_label='Date', y_axis_label='Price',x_axis_type="datetime")
plot = figure(plot_width=1000, plot_height=300,tools=TOOLS,x_axis_type="datetime",title='Order/Execution Snapshot with Price Levels')
plot.circle(x=x,y=y,source=source_order,view=view1,alpha=0.6,color='blue')
plot.circle(x=x,y=y,source=source_order,view=view2,alpha=0.6,color='red')
plot.triangle(x=x,y=y,source=source_order,view=view3,alpha=0.6,color='blue')
plot.triangle(x=x,y=y,source=source_order,view=view4,alpha=0.6,color='red')
plot.line(x=x,y=y,source=source_order,view=view6,color='green')
plot2.line(x=x,y=y1,source=source_order,view=view5,color='blue')
plot.legend.location = 'top_left'
return plot,plot2
def make_table(source):
columns = [
TableColumn(field='DATE', title="DATE", formatter=DateFormatter()),
TableColumn(field='CCYPAIR', title="CCYPAIR"),
TableColumn(field='SIDE', title="SIDE"),
TableColumn(field='PRICE', title="PRICE"),
TableColumn(field='TYPE', title="TYPE"),
TableColumn(field='Volume', title="Volume"),
TableColumn(field='ORDER_ID_108', title="ORDERID"),
]
data_table = DataTable(source=source, columns=columns, width=1000, height=200)
return data_table
def update_plot(attrname, old, new):
newccy = menu.value
newvalue = slider.value
src_data_table = get_combined_dataset(Combined,newccy)
DisplayData.data.update(src_data_table.data)
def update_plot(attrname, old, new):
newccy = menu.value
newvalue = slider.value
src_data_table = get_combined_dataset(Combined,newccy)
DisplayData.data.update(src_data_table.data)
def selection_change(attrname, old, new):
data = get_all_dataset(Combined,menu.value)
selected = DisplayData.selected['1d']['indices']
if selected:
data = data.iloc[selected, :]
update_datatable(data)
def update_datatable(data):
src_data_table = get_combined_dataset(data,menu.value)
s2.data.update(src_data_table.data)
# Input Files
date_today = datetime.now()
days = pd.date_range(date_today, date_today + timedelta(5), freq='D')
Combined1 = {'DATE': days,
'CCYPAIR': ['USDJPY', 'USDJPY', 'USDJPY','USDJPY', 'USDJPY', 'USDJPY'],
'SIDE' : ['B', 'B', 'B','B', 'B', 'B'],
'PRICE': [100.00, 200.00, 300.00,100.00, 200.00, 300.00],
'TYPE' : ['ORDER', 'ORDER', 'ORDER','DevonTrade', 'DevonTrade', 'DevonTrade'],
'Volume': [100, 200, 300, 100, 200, 300],
'ORDER_ID_108': [111,222,333,111,222,333]
}
Combined = pd.DataFrame(Combined1)
DisplayData = get_combined_dataset(Combined,menu.value)
plot,plot2 = make_plot(DisplayData)
menu.on_change('value', update_plot)
plot.x_range = plot2.x_range
s2 = ColumnDataSource(data=dict(DATE=[],CCYPAIR=[],SIDE=[],PRICE=[],TYPE= [],Volume=[],ORDER_ID_108=[]))
columns = [
TableColumn(field='DATE', title="DATE", formatter=DateFormatter()),
TableColumn(field='CCYPAIR', title="CCYPAIR"),
TableColumn(field='SIDE', title="SIDE"),
TableColumn(field='PRICE', title="PRICE"),
TableColumn(field='TYPE', title="TYPE"),
TableColumn(field='Volume', title="Volume"),
TableColumn(field='ORDER_ID_108', title="ORDER_ID_108")
]
data_table = DataTable(source=s2,columns=columns,width=1000, height=200)
layout = layout([menu],
[plot],
[plot2],
[data_table])
curdoc().add_root(layout)
DisplayData.on_change('selected', selection_change)