Candlestick chart is an important for stock technical analysis,
and Bokeh is the best Python tool I’ve found so far to chart. Boken has a candlestick
chart example http://bokeh.pydata.org/en/latest/docs/gallery/candlestick.html,
there are some problems with this example though:
- When there are gaps between dates (e.g. on weekends, most stock exchanges to not work), there will be gaps on the neighbouring candlesticks.
- (This is a minor issue) The up and bottom tails should be of the same colour as the candle body, instead, they are of black.
For the first problem, google shows there are people who are
looking at the same issue and without answers. I decided not to tackle the
problem head on(because I am pragmatic), but work around it like the
following (the head-on tackle would be some kind of DateGapTickFormatter):
- Do not use date as the axis, rather use a sequence.
- Use tooltip when hover over a candlestick, the tooltip will show the detailed info of the candlestick.
Here is the relevant code:
#df contains stock
information
#append a sequence to df
seqs=np.arange(df.shape[0])
df["seq"]=pd.Series(seqs)
df["date"] = pd.to_datetime(df["date"])
df['date']=df['date'].apply(lambda x: x.strftime('%m/%d'))
df['changepercent']=df['changepercent'].apply(lambda x: str(x)+"%")
df['mid']=df.apply(lambda x:(x['open']+x['close'])/2,axis=1)
df['height']=df.apply(lambda x:abs(x['close']-x['open'] if x['close']!=x['open'] else 0.001),axis=1)
inc = df.close > df.open
dec = df.open > df.close
w=0.3
#append a sequence to df
seqs=np.arange(df.shape[0])
df["seq"]=pd.Series(seqs)
df["date"] = pd.to_datetime(df["date"])
df['date']=df['date'].apply(lambda x: x.strftime('%m/%d'))
df['changepercent']=df['changepercent'].apply(lambda x: str(x)+"%")
df['mid']=df.apply(lambda x:(x['open']+x['close'])/2,axis=1)
df['height']=df.apply(lambda x:abs(x['close']-x['open'] if x['close']!=x['open'] else 0.001),axis=1)
inc = df.close > df.open
dec = df.open > df.close
w=0.3
#use ColumnDataSource
to pass in data for tooltips
sourceInc=ColumnDataSource(ColumnDataSource.from_df(df.loc[inc]))
sourceDec=ColumnDataSource(ColumnDataSource.from_df(df.loc[dec]))
#the values for the tooltip come from ColumnDataSource
hover = HoverTool(
tooltips=[
("date", "@date"),
("open", "@open"),
("close", "@close"),
("percent", "@changepercent"),
]
)
TOOLS = [CrosshairTool(), hover]
p = figure(plot_width=700, plot_height=400, tools=TOOLS,title = df["code"][0]+" "+df["name"][0])
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha=0.3
sourceInc=ColumnDataSource(ColumnDataSource.from_df(df.loc[inc]))
sourceDec=ColumnDataSource(ColumnDataSource.from_df(df.loc[dec]))
#the values for the tooltip come from ColumnDataSource
hover = HoverTool(
tooltips=[
("date", "@date"),
("open", "@open"),
("close", "@close"),
("percent", "@changepercent"),
]
)
TOOLS = [CrosshairTool(), hover]
p = figure(plot_width=700, plot_height=400, tools=TOOLS,title = df["code"][0]+" "+df["name"][0])
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha=0.3
#this is the up tail
p.segment(df.seq[inc], df.high[inc], df.seq[inc], df.low[inc], color="red")
p.segment(df.seq[inc], df.high[inc], df.seq[inc], df.low[inc], color="red")
#this is the bottom
tail
p.segment(df.seq[dec], df.high[dec], df.seq[dec], df.low[dec], color="green")
#this is the candle body for the red dates
p.segment(df.seq[dec], df.high[dec], df.seq[dec], df.low[dec], color="green")
#this is the candle body for the red dates
p.rect(x='seq', y='mid', width=w, height='height', fill_color="red", line_color="red", source=sourceInc)
#this is the candle
body for the green dates
p.rect(x='seq', y='mid', width=w, height='height', fill_color="green", line_color="green", source=sourceDec)
show(p)
p.rect(x='seq', y='mid', width=w, height='height', fill_color="green", line_color="green", source=sourceDec)
show(p)
Notice how annoying the gaps are!