How to add lines with annotations to candlestick charts when some values are missing?

Viewed 1013

I'm trying to use Plotly to overlay a marker/line chart on top of my OHLC candle chart.

Code

import plotly.graph_objects as go
import pandas as pd
from datetime import datetime

    df = pd.DataFrame(
{'index': {0: 0,
  1: 1,
  2: 2,
  3: 3,
  4: 4,
  5: 5,
  6: 6,
  7: 7,
  8: 8,
  9: 9,
  10: 10,
  11: 11,
  12: 12,
  13: 13,
  14: 14,
  15: 15,
  16: 16,
  17: 17,
  18: 18,
  19: 19,
  20: 20,
  21: 21,
  22: 22,
  23: 23,
  24: 24},
 'Date': {0: '2018-09-03',
  1: '2018-09-04',
  2: '2018-09-05',
  3: '2018-09-06',
  4: '2018-09-07',
  5: '2018-09-10',
  6: '2018-09-11',
  7: '2018-09-12',
  8: '2018-09-13',
  9: '2018-09-14',
  10: '2018-09-17',
  11: '2018-09-18',
  12: '2018-09-19',
  13: '2018-09-20',
  14: '2018-09-21',
  15: '2018-09-24',
  16: '2018-09-25',
  17: '2018-09-26',
  18: '2018-09-27',
  19: '2018-09-28',
  20: '2018-10-01',
  21: '2018-10-02',
  22: '2018-10-03',
  23: '2018-10-04',
  24: '2018-10-05'},
 'Open': {0: 1.2922067642211914,
  1: 1.2867859601974487,
  2: 1.2859420776367188,
  3: 1.2914056777954102,
  4: 1.2928247451782229,
  5: 1.292808175086975,
  6: 1.3027958869934082,
  7: 1.3017443418502808,
  8: 1.30451238155365,
  9: 1.3110626935958862,
  10: 1.3071041107177734,
  11: 1.3146650791168213,
  12: 1.3166556358337402,
  13: 1.3140604496002195,
  14: 1.3271400928497314,
  15: 1.3080958127975464,
  16: 1.3117163181304932,
  17: 1.3180439472198486,
  18: 1.3169677257537842,
  19: 1.3077707290649414,
  20: 1.3039510250091553,
  21: 1.3043931722640991,
  22: 1.2979763746261597,
  23: 1.2941633462905884,
  24: 1.3022021055221558},
 'High': {0: 1.2934937477111816,
  1: 1.2870012521743774,
  2: 1.2979259490966797,
  3: 1.2959914207458496,
  4: 1.3024225234985352,
  5: 1.3052103519439695,
  6: 1.30804443359375,
  7: 1.3044441938400269,
  8: 1.3120088577270508,
  9: 1.3143367767333984,
  10: 1.3156682252883911,
  11: 1.3171066045761108,
  12: 1.3211784362792969,
  13: 1.3296104669570925,
  14: 1.3278449773788452,
  15: 1.3166556358337402,
  16: 1.3175750970840454,
  17: 1.3196094036102295,
  18: 1.3180439472198486,
  19: 1.3090718984603882,
  20: 1.3097577095031738,
  21: 1.3049719333648682,
  22: 1.3020155429840088,
  23: 1.3036959171295166,
  24: 1.310753345489502},
 'Low': {0: 1.2856279611587524,
  1: 1.2813942432403564,
  2: 1.2793285846710205,
  3: 1.289723515510559,
  4: 1.2918561697006226,
  5: 1.289823293685913,
  6: 1.2976733446121216,
  7: 1.298414707183838,
  8: 1.3027619123458862,
  9: 1.3073604106903076,
  10: 1.3070186376571655,
  11: 1.3120776414871216,
  12: 1.3120431900024414,
  13: 1.3140085935592651,
  14: 1.305841088294983,
  15: 1.3064552545547483,
  16: 1.3097233772277832,
  17: 1.3141123056411743,
  18: 1.309706211090088,
  19: 1.3002548217773438,
  20: 1.3014055490493774,
  21: 1.2944146394729614,
  22: 1.2964619398117063,
  23: 1.2924572229385376,
  24: 1.3005592823028564},
 'Close': {0: 1.292306900024414,
  1: 1.2869019508361816,
  2: 1.2858428955078125,
  3: 1.2914891242980957,
  4: 1.2925406694412231,
  5: 1.2930254936218262,
  6: 1.302643060684204,
  7: 1.3015578985214231,
  8: 1.304546356201172,
  9: 1.311131477355957,
  10: 1.307326316833496,
  11: 1.3146305084228516,
  12: 1.3168463706970217,
  13: 1.3141123056411743,
  14: 1.327087163925171,
  15: 1.30804443359375,
  16: 1.3117333650588991,
  17: 1.3179919719696045,
  18: 1.3172800540924072,
  19: 1.3078734874725342,
  20: 1.3039000034332275,
  21: 1.3043591976165771,
  22: 1.2981956005096436,
  23: 1.294062852859497,
  24: 1.3024225234985352},
 'Pivot Price': {0: 1.2934937477111816,
  1: np.nan,
  2: 1.2793285846710205,
  3: np.nan,
  4: np.nan,
  5: np.nan,
  6: np.nan,
  7: np.nan,
  8: np.nan,
  9: np.nan,
  10: np.nan,
  11: np.nan,
  12: np.nan,
  13: 1.3296104669570925,
  14: np.nan,
  15: np.nan,
  16: np.nan,
  17: np.nan,
  18: np.nan,
  19: np.nan,
  20: np.nan,
  21: np.nan,
  22: np.nan,
  23: 1.2924572229385376,
  24: np.nan}})

fig = go.Figure(data=[go.Candlestick(x=df['Date'],
                open=df['Open'],
                high=df['High'],
                low=df['Low'],
                close=df['Close'])])

fig.add_trace(
    go.Scatter(mode = "lines+markers",
        x=df['Date'],
        y=df["Pivot Price"]
    ))

fig.update_layout(
    autosize=False,
    width=1000,
    height=800,)

fig.show()

This is the current image

current image

This is the desired output/image

I want black line between the markers (pivots). I would also ideally like a value next to each line showing the distance between each pivot but Im not sure how to do this.

For example the distance between the first two pivots round(abs(1.293494 - 1.279329),3) returns 0.014 so I would ideally like this next to the line.

The second is round(abs(1.279329 - 1.329610),3) so the value would be 0.05. I have hand edited the image and added the lines for the first two values to give a visual representation of what Im trying to achieve.

enter image description here

2 Answers

Problem seems the missing values, plotly has difficulty with. With this trick you can only plot the point;

has_value = ~df["Pivot Price"].isna()

import plotly.graph_objects as go
import pandas as pd
from datetime import datetime

df=pd.read_csv("notebooks/for_so.csv")
fig = go.Figure(data=[go.Candlestick(x=df['Date'],
                open=df['Open'],
                high=df['High'],
                low=df['Low'],
                close=df['Close'])])

fig.add_trace(
    go.Scatter(mode = 'lines',
               x=df[has_value]['Date'],
        y=df[has_value]["Pivot Price"], line={'color':'black', 'width':1}
    ))

fig.add_trace(
    go.Scatter(mode = "markers",
        x=df['Date'],
        y=df["Pivot Price"]
    ))

fig.update_layout(
    autosize=False,
    width=1000,
    height=800,)

fig.show()

This did it for me.

The problem seems to be the missing values. So just use pandas.Series.interpolate in combination with fig.add_annotation to get:

enter image description here

I've included annotations for differences as well. There are surely more elegant ways to do it than with for loops, but it does the job. Let me know if anything is unclear!

import pandas as pd
import numpy as np
import plotly.graph_objects as go

df = pd.DataFrame(
{'index': {0: 0,
  1: 1,
  2: 2,
  3: 3,
  4: 4,
  5: 5,
  6: 6,
  7: 7,
  8: 8,
  9: 9,
  10: 10,
  11: 11,
  12: 12,
  13: 13,
  14: 14,
  15: 15,
  16: 16,
  17: 17,
  18: 18,
  19: 19,
  20: 20,
  21: 21,
  22: 22,
  23: 23,
  24: 24},
 'Date': {0: '2018-09-03',
  1: '2018-09-04',
  2: '2018-09-05',
  3: '2018-09-06',
  4: '2018-09-07',
  5: '2018-09-10',
  6: '2018-09-11',
  7: '2018-09-12',
  8: '2018-09-13',
  9: '2018-09-14',
  10: '2018-09-17',
  11: '2018-09-18',
  12: '2018-09-19',
  13: '2018-09-20',
  14: '2018-09-21',
  15: '2018-09-24',
  16: '2018-09-25',
  17: '2018-09-26',
  18: '2018-09-27',
  19: '2018-09-28',
  20: '2018-10-01',
  21: '2018-10-02',
  22: '2018-10-03',
  23: '2018-10-04',
  24: '2018-10-05'},
 'Open': {0: 1.2922067642211914,
  1: 1.2867859601974487,
  2: 1.2859420776367188,
  3: 1.2914056777954102,
  4: 1.2928247451782229,
  5: 1.292808175086975,
  6: 1.3027958869934082,
  7: 1.3017443418502808,
  8: 1.30451238155365,
  9: 1.3110626935958862,
  10: 1.3071041107177734,
  11: 1.3146650791168213,
  12: 1.3166556358337402,
  13: 1.3140604496002195,
  14: 1.3271400928497314,
  15: 1.3080958127975464,
  16: 1.3117163181304932,
  17: 1.3180439472198486,
  18: 1.3169677257537842,
  19: 1.3077707290649414,
  20: 1.3039510250091553,
  21: 1.3043931722640991,
  22: 1.2979763746261597,
  23: 1.2941633462905884,
  24: 1.3022021055221558},
 'High': {0: 1.2934937477111816,
  1: 1.2870012521743774,
  2: 1.2979259490966797,
  3: 1.2959914207458496,
  4: 1.3024225234985352,
  5: 1.3052103519439695,
  6: 1.30804443359375,
  7: 1.3044441938400269,
  8: 1.3120088577270508,
  9: 1.3143367767333984,
  10: 1.3156682252883911,
  11: 1.3171066045761108,
  12: 1.3211784362792969,
  13: 1.3296104669570925,
  14: 1.3278449773788452,
  15: 1.3166556358337402,
  16: 1.3175750970840454,
  17: 1.3196094036102295,
  18: 1.3180439472198486,
  19: 1.3090718984603882,
  20: 1.3097577095031738,
  21: 1.3049719333648682,
  22: 1.3020155429840088,
  23: 1.3036959171295166,
  24: 1.310753345489502},
 'Low': {0: 1.2856279611587524,
  1: 1.2813942432403564,
  2: 1.2793285846710205,
  3: 1.289723515510559,
  4: 1.2918561697006226,
  5: 1.289823293685913,
  6: 1.2976733446121216,
  7: 1.298414707183838,
  8: 1.3027619123458862,
  9: 1.3073604106903076,
  10: 1.3070186376571655,
  11: 1.3120776414871216,
  12: 1.3120431900024414,
  13: 1.3140085935592651,
  14: 1.305841088294983,
  15: 1.3064552545547483,
  16: 1.3097233772277832,
  17: 1.3141123056411743,
  18: 1.309706211090088,
  19: 1.3002548217773438,
  20: 1.3014055490493774,
  21: 1.2944146394729614,
  22: 1.2964619398117063,
  23: 1.2924572229385376,
  24: 1.3005592823028564},
 'Close': {0: 1.292306900024414,
  1: 1.2869019508361816,
  2: 1.2858428955078125,
  3: 1.2914891242980957,
  4: 1.2925406694412231,
  5: 1.2930254936218262,
  6: 1.302643060684204,
  7: 1.3015578985214231,
  8: 1.304546356201172,
  9: 1.311131477355957,
  10: 1.307326316833496,
  11: 1.3146305084228516,
  12: 1.3168463706970217,
  13: 1.3141123056411743,
  14: 1.327087163925171,
  15: 1.30804443359375,
  16: 1.3117333650588991,
  17: 1.3179919719696045,
  18: 1.3172800540924072,
  19: 1.3078734874725342,
  20: 1.3039000034332275,
  21: 1.3043591976165771,
  22: 1.2981956005096436,
  23: 1.294062852859497,
  24: 1.3024225234985352},
 'Pivot Price': {0: 1.2934937477111816,
  1: np.nan,
  2: 1.2793285846710205,
  3: np.nan,
  4: np.nan,
  5: np.nan,
  6: np.nan,
  7: np.nan,
  8: np.nan,
  9: np.nan,
  10: np.nan,
  11: np.nan,
  12: np.nan,
  13: 1.3296104669570925,
  14: np.nan,
  15: np.nan,
  16: np.nan,
  17: np.nan,
  18: np.nan,
  19: np.nan,
  20: np.nan,
  21: np.nan,
  22: np.nan,
  23: 1.2924572229385376,
  24: np.nan}})

import plotly.graph_objects as go
import pandas as pd
from datetime import datetime

# df=pd.read_csv("for_so.csv")
fig = go.Figure(data=[go.Candlestick(x=df['Date'],
# fig = go.Figure(data=[go.Candlestick(x=df.index,
                open=df['Open'],
                high=df['High'],
                low=df['Low'],
                close=df['Close'])])

# some calculations
df_diff = df['Pivot Price'].dropna().diff().copy()
df2 = df[df.index.isin(df_diff.index)].copy()
df2['Price Diff'] = df['Pivot Price'].dropna().values

fig.add_trace(
    go.Scatter(mode = "lines+markers",
        x=df['Date'],
        y=df["Pivot Price"]
    ))

fig.update_layout(
    autosize=False,
    width=1000,
    height=800,)

fig.add_trace(go.Scatter(x=df['Date'], y=df['Pivot Price'].interpolate(),
# fig.add_trace(go.Scatter(x=df.index, y=df['Pivot Price'].interpolate(),
                         mode = 'lines',
                         line = dict(color='black')))


def annot(value):
#     print(type(value))
    if np.isnan(value):
        return ''
    else:
        return value
    

j = 0
for i, p in enumerate(df['Pivot Price']):
#      print(p)
#     if not np.isnan(p) and not np.isnan(df_diff.iloc[j]):
    if not np.isnan(p):
#         print(not np.isnan(df_diff.iloc[j]))
        
        fig.add_annotation(dict(font=dict(color='rgba(0,0,200,0.8)',size=12),
                                        x=df['Date'].iloc[i],
#                                          x=df.index[i],
#                                         x = xStart
                                        y=p,
                                        showarrow=False,
                                        text=annot(round(abs(df_diff.iloc[j]),3)),
                                        textangle=0,
                                        xanchor='right',
                                        xref="x",
                                        yref="y"))
        j = j + 1
fig.update_xaxes(type='category')
fig.show()
Related