Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
429 views
in Technique[技术] by (71.8m points)

python - How to add Arrow annotations with an offset to a bokeh plot with a datetime x-axis

I want to draw an arrow or dots when 2 ma cross each other like there will up arrow when short ma cross above long ma etc. but I don't know how to plot when it is datetime. I try to use this code and it just give me errors.

#plot short ma and long ma
p.line(df['Date'], df['short_ma'], color='red')
p.line(df['Date'], df['long_ma'], color='black')

p.add_layout(Arrow(end=VeeHead(size=35), line_color="red",x_start=df['Date'], y_start=df['crossabove']+5, x_end=df['Date'], y_end=df['Date']))
#the crossabove + 5 so the arrow draw above where the cross occur 

I post an image for the result i was expect the result i expect

code to plot candlestick chart and add arrow when 2 ema cross

import pandas as pd
import numpy as np
import timeit
import talib as tb
import datetime
import random
from bokeh.models import Arrow, NormalHead, OpenHead, VeeHead
from bokeh.plotting import figure, output_file, show

df =  pd.read_csv("D:/testdata/msft.csv") #open csv
df['short_ema'] = tb.EMA(df['Close'], 100) # short ema
df['long_ema'] = tb.EMA(df['Close'], 200)  #long ema
df = df.round(2)    #round to 2
df['Date']=pd.to_datetime(df['Date'])
#print(df.dtypes)
#chart figures
p = figure(plot_width=1400, plot_height=860,
           x_axis_type='datetime',)

#candle
inc = df.Close > df.Open
dec = df.Open > df.Close
w = 12*60*60*1000 # half day in ms
p.segment(df['Date'], df['High'], df.Date, df.Low, color="black")
p.vbar(df['Date'][inc], w, df.Open[inc], df.Close[inc], fill_color="#D5E1DD", line_color="black")
p.vbar(df['Date'][dec], w, df.Open[dec], df.Close[dec], fill_color="#F2583E", line_color="black")

#ma lines
p.line(df['Date'], df['short_ema'], color='red')
p.line(df['Date'], df['long_ema'], color='black')
                                     
#df.to_csv("D:/testdata/msft result.csv")

#loop for cross add arrow
match = df[((df.short_ema.shift(1) > df.long_ema.shift(1)) & (df.short_ema.shift(2)< df.long_ema.shift(2)))]

for x_, (y_, _) in match[['Date', 'long_ema']].iterrows():
    print(x_,y_)
    p.add_layout(Arrow(end=VeeHead(line_color="blue", line_width=4, fill_color='blue'),
                       line_color='blue', line_width=4,
                       x_start=df['Date'], y_start= y_ + 3,
                       x_end=df['Date'], y_end=y_ + 1))

show(p)

enter image description here

question from:https://stackoverflow.com/questions/65931163/how-to-add-arrow-annotations-with-an-offset-to-a-bokeh-plot-with-a-datetime-x-ax

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
  • For an Arrow, x_start and x_end must be a datetime format, not a string or a dataframe.
    • x_start=pd.to_datetime('2010-10-09')
    • The coordinates for the arrow may not be passed as a dataframe, they must be passed as individual values, which is done in a loop below.
      • x_ is the date from the datetime index.
      • y_ is the y intersection point, to which an offset (e.g. +5) may be added
  • This example was used, and arrows were added to it
  • See Labels for text annotations
import pandas as pd
from bokeh.models import Arrow, NormalHead, OpenHead, VeeHead, Label
from bokeh.plotting import figure, show
from bokeh.sampledata.glucose import data
from bokeh.io import output_notebook, curdoc  # output_file
output_notebook()

# for a file, uncomment the next line and output_file in the imports
# output_file("box_annotation.html", title="box_annotation.py example")

TOOLS = "pan,wheel_zoom,box_zoom,reset,save"

#reduce data size
data = data.loc['2010-10-06':'2010-10-13'].copy()

# test line to show where glucose and line cross each other
data['line'] = 170

# determine where the lines cross
match = data[data.glucose == data.line]

p = figure(x_axis_type="datetime", tools=TOOLS)

p.line(data.index.to_series(), data['glucose'], line_color="gray", line_width=1, legend_label="glucose")
p.line(data.index.to_series(), data['line'], line_color="purple", line_width=1, legend_label="line")

# add arrows to all spots where the lines are equal
for x_, (y_, _) in match[['glucose', 'line']].iterrows():
    
    p.add_layout(Arrow(end=VeeHead(line_color="blue", line_width=4, fill_color='blue'),
                       line_color='blue', line_width=4,
                       x_start=x_, y_start= y_ + 130,
                       x_end=x_, y_end=y_ + 5))

p.title.text = "Glucose Range"
p.xgrid[0].grid_line_color=None
p.ygrid[0].grid_line_alpha=0.5
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'

show(p)

enter image description here

Update

  • In the following section:
    • x_start=df['Date'] & x_end=df['Date'] are used instead of x_, which should be a single date value, not a Series of dates.
    • The for-loop selects the incorrect values to be x_ and y_. In my original match, the dates are in the index, but your match has dates in a column.
match = df[((df.short_ema.shift(1) > df.long_ema.shift(1)) & (df.short_ema.shift(2)< df.long_ema.shift(2)))]

for x_, (y_, _) in match[['Date', 'long_ema']].iterrows():
    print(x_,y_)
    p.add_layout(Arrow(end=VeeHead(line_color="blue", line_width=4, fill_color='blue'),
                       line_color='blue', line_width=4,
                       x_start=df['Date'], y_start= y_ + 3,
                       x_end=df['Date'], y_end=y_ + 1))

Corrected Code

for _, (x_, y_) in match[['Date', 'long_ema']].iterrows():
    print(x_,y_)
    p.add_layout(Arrow(end=VeeHead(line_color="blue", line_width=4, fill_color='blue'),
                       line_color='blue', line_width=4,
                       x_start=x_, y_start= y_ + 3,
                       x_end=x_, y_end=y_ + 1))

show(p)

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...