2
votes

I am trying to make a OHLC graph plotted with matplotlib interactive upon the user clicking on a valid point. The data is stored as a pandas dataframe of the form

index       PX_BID  PX_ASK  PX_LAST  PX_OPEN  PX_HIGH  PX_LOW
2016-07-01  1.1136  1.1137   1.1136   1.1106   1.1169  1.1072
2016-07-04  1.1154  1.1155   1.1154   1.1143   1.1160  1.1098
2016-07-05  1.1076  1.1077   1.1076   1.1154   1.1186  1.1062
2016-07-06  1.1100  1.1101   1.1100   1.1076   1.1112  1.1029
2016-07-07  1.1062  1.1063   1.1063   1.1100   1.1107  1.1053

I am plotting it with matplotlib's candlestick function:

candlestick2_ohlc(ax1, df['PX_OPEN'],df['PX_HIGH'],df['PX_LOW'],df['PX_LAST'],width=1)

When plotted it looks somthing like this:

https://pythonprogramming.net/static/images/matplotlib/candlestick-ohlc-graphs-matplotlib-tutorial.png

I want the console to print out the value of the point clicked, the date and whether it is an open, high low or close. So far I have something like:

fig, ax1 = plt.subplots()

ax1.set_title('click on points', picker=True)
ax1.set_ylabel('ylabel', picker=True, bbox=dict(facecolor='red'))
line = candlestick2_ohlc(ax1, df['PX_OPEN'],df['PX_HIGH'],df['PX_LOW'],df['PX_LAST'],width=0.4)

def onpick1(event):
    if isinstance(event.artist, (lineCollection, barCollection)):
        thisline = event.artist
        xdata = thisline.get_xdata()
        ydata = thisline.get_ydata()
        ind = event.ind
        #points = tuple(zip(xdata[ind], ydata[ind]))
        #print('onpick points:', points)
        print( 'X='+str(np.take(xdata, ind)[0]) ) # Print X point
        print( 'Y='+str(np.take(ydata, ind)[0]) ) # Print Y point

fig.canvas.mpl_connect('pick_event', onpick1)
plt.show()

This code however does not print anything when ran and points are clicked. When I look at examples of interactive matplotlib graphs, they tend to have an argument in the plot function such as:

line, = ax.plot(rand(100), 'o', picker=5)

However, candlestick2_ohlc does not take a 'picker' arg. Any tips on how I can get around this?

Thanks

1

1 Answers

1
votes

You need to set set_picker(True) to enable a pick event or give a tolerance in points as a float (see http://matplotlib.org/api/artist_api.html#matplotlib.artist.Artist.set_picker).

So in your case ax1.set_picker(True) if you want pick event to be fired whenever if mouseevent is over ax1.

You can enable pick events on the elements of the candlestick chart. I read the documentation and candlestick2_ohlc returns a tuple of two objects: a LineCollection and a PolyCollection. So you can name these objects and set the picker to true on them

(lines,polys) = candlestick2_ohlc(ax1, ...)
lines.set_picker(True) # collection of lines in the candlestick chart
polys.set_picker(True) # collection of polygons in the candlestick chart

The index of the event ind = event.ind[0] will tell you which element in the collection contained the mouse event (event.ind returns a list of indices since a mouse event might pertain more than one item).

Once you trigger a pick event on a candlestick you can print the data from the original dataframe.

Here's some working code

import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection, PolyCollection
from matplotlib.text import Text
from matplotlib.finance import candlestick2_ohlc
import numpy as np
import pandas as pd

np.random.seed(0)
dates = pd.date_range('20160101',periods=7)
df = pd.DataFrame(np.reshape(1+np.random.random_sample(42)*0.1,(7,6)),index=dates,columns=["PX_BID","PX_ASK","PX_LAST","PX_OPEN","PX_HIGH","PX_LOW"])
df['PX_HIGH']+=.1
df['PX_LOW']-=.1

fig, ax1 = plt.subplots()

ax1.set_title('click on points', picker=20)
ax1.set_ylabel('ylabel', picker=20, bbox=dict(facecolor='red'))

(lines,polys) = candlestick2_ohlc(ax1, df['PX_OPEN'],df['PX_HIGH'],df['PX_LOW'],df['PX_LAST'],width=0.4)
lines.set_picker(True)
polys.set_picker(True)

def onpick1(event):
    if isinstance(event.artist, (Text)):
        text = event.artist
        print 'You clicked on the title ("%s")' % text.get_text()
    elif isinstance(event.artist, (LineCollection, PolyCollection)):   
        thisline = event.artist
        mouseevent = event.mouseevent
        ind = event.ind[0]
        print 'You clicked on item %d' % ind
        print 'Day: ' + df.index[ind].normalize().to_datetime().strftime('%Y-%m-%d')
        for p in ['PX_OPEN','PX_OPEN','PX_HIGH','PX_LOW']:
            print p + ':' + str(df[p][ind])    
        print('x=%d, y=%d, xdata=%f, ydata=%f' %
          ( mouseevent.x, mouseevent.y, mouseevent.xdata, mouseevent.ydata))



fig.canvas.mpl_connect('pick_event', onpick1)
plt.show()