如何将带有偏移量的箭头注释添加到带有日期时间 x 轴的散景图

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

我想在 2 ma 相互交叉时画一个箭头或点,就像当 short ma 交叉在 long ma 上方时会有向上箭头等。但我不知道在日期时间时如何绘制。我尝试使用此代码,但它只会给我错误。

#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 

我 post 我期望的结果的图像 the result i expect

绘制烛台图表并在 2 均线交叉时添加箭头的代码

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)

  • 对于 Arrowx_startx_end 必须是 datetime 格式,而不是 stringdataframe
    • x_start=pd.to_datetime('2010-10-09')
    • 箭头的坐标不能作为数据框传递,它们必须作为单独的值传递,这是在下面的循环中完成的。
      • x_ 是日期时间索引中的日期。
      • y_为y轴交点,可加偏移量(如+5
  • 这个 example was used, and arrows 已添加到其中
  • 有关文本注释,请参阅 Labels
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)

更新

  • 在以下部分中:
      使用
    • x_start=df['Date']x_end=df['Date'] 而不是 x_,它应该是单个日期值,而不是 Series 个日期。
    • for-loop 选择了不正确的值 x_y_。在我原来的 match 中,日期在索引中,但是你的 match 中的日期在一列中。
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))

更正代码

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)