mplfinance icon indicating copy to clipboard operation
mplfinance copied to clipboard

Event Handling with mplfinance

Open croganm opened this issue 4 years ago • 12 comments

Hey Daniel, love what you're doing with this package, it's absolutely fantastic.

I'm a pretty novice coder so excuse any code that might be unpreferred but I recently had an idea for a "point and click" technical indicator and wanted to try it out. The only issue is that I have absolutely no idea how to use event handling with the new mplfinance package.

I made a makeshift version using the old mpl_finance package. Attached is it (I'm using Alphavantage for intraday data, not sure I want to post my apikey here so I'll post everything but that):

import requests_html
import yahoo_fin.stock_info as yf
import pandas as pd
import numpy as np
import mpl_finance as mpf
import json
import requests
import datetime
import matplotlib as mpl
from mplfinance.original_flavor import candlestick_ohlc


ticker='SPY'
interval='60min'
apikey=''

r = requests.get("https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol={}&interval={}&outputsize=full&apikey={}".format(ticker,interval, apikey))
df = pd.DataFrame.from_dict(r.json()['Time Series ({})'.format(interval)]).T
df.columns=['Open','High','Low','Close','Volume']
df.index = pd.to_datetime(df.index)
df.sort_index(ascending=True, inplace=True)
df = df.apply(pd.to_numeric)

inital_data = df
inital_data.columns = map(str.title, inital_data.columns)


fig, ax = plt.subplots(figsize=(15,15))


candlestick_ohlc(ax, zip(mpl.dates.date2num(inital_data.index), inital_data['Open'], inital_data['High'], inital_data['Low'], inital_data['Close']))  


def onclick(event):
    date = mpl.dates.num2date(event.xdata)
    # print(datetime.datetime.strftime(date,"%Y-%m-%d %H:%M"))
    data = inital_data.loc[date:,:]
    vwap = []


    for x in range(len(data['Close'])):
        vwap.append(np.sum((data['Close'][0:(x+1)]*data['Volume'][0:(x+1)]))/np.sum(data['Volume'][0:(x+1)]))

    data[date] = vwap
    inital_data[date]=data[date]

    ax.plot(inital_data.index,inital_data[date])

    inital_data.drop(date, axis=1, inplace=True)
    event.canvas.draw()
    print(data.index[0])

cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

The point and click functionality works incredible, exactly what I wanted, but it is so ugly. I made a non-point and click version using the new mplfinance and it looks beautiful but I want the point and click functionality for that.

Here's the ugly looking one: https://imgur.com/a/VPa0SlH

Here's the beautiful looking one: https://imgur.com/a/6s5gQSu

Any help in getting the event handling for the new package would be much appreciated.

croganm avatar Apr 08 '20 04:04 croganm

For some reason, I updated Anaconda and the script above stopped working. I updated the script to work again, feel free to use whichever one works on your end:

import requests_html
import yahoo_fin.stock_info as yf
import pandas as pd
import numpy as np
import json
import requests
import datetime
import matplotlib as mpl
import matplotlib.pyplot as plt
from mplfinance.original_flavor import candlestick_ohlc


ticker='SPY'
interval='60min'
apikey=''

r = requests.get("https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol={}&interval={}&outputsize=full&apikey={}".format(ticker,interval, apikey))
df = pd.DataFrame.from_dict(r.json()['Time Series ({})'.format(interval)]).T
df.columns=['Open','High','Low','Close','Volume']
df.index = pd.to_datetime(df.index)
df.sort_index(ascending=True, inplace=True)
df = df.apply(pd.to_numeric)

df.columns = map(str.title, df.columns)



fig, ax = plt.subplots(figsize=(15,15))


candlestick_ohlc(ax, zip(mpl.dates.date2num(df.index), df['Open'], df['High'], df['Low'], df['Close']))  

def onclick(event):
    if event.dblclick:
        date = mpl.dates.num2date(event.xdata)
        date = datetime.datetime.strftime(date,"%Y-%m-%d %H:%M")
        data = df.loc[date:,:]
        vwap = []

        
        for x in range(len(data['Close'])):
            vwap.append(np.sum((data['Close'][0:(x+1)]*data['Volume'][0:(x+1)]))/np.sum(data['Volume'][0:(x+1)]))

        data[date] = vwap
        new_df = pd.concat([df, data[date]], axis=1, sort=False)

        ax.plot(new_df[date])
        
        event.canvas.draw()
        print(data.index[0])

cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

croganm avatar Apr 08 '20 21:04 croganm

Thanks. It will probably be a week or two before i have time to look into this, but i am definitely interested. Thank you for getting involved.

DanielGoldfarb avatar Apr 08 '20 21:04 DanielGoldfarb

Thank you, Daniel!

I figure some sort of reload function or write function could handle this, I'm just not there skill-wise yet.

croganm avatar Apr 09 '20 16:04 croganm

Hello

i'm also struggling to use this package with event handling which figure can I use to do these types of command : fig.canvas.mpl_connect("scroll_event", zoom_fun) ? Is there any workaround to adapt previous work from more general matplotlib event handling ? OP, as i've understand, you had to use the previous API to do it right ?

I would like to add this scroll event handler to have a Tradingview feeling of the charts :

base_scale=1.5
def zoom_fun(event):
    """ ref : https://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel"""
    cur_xlim = ax.get_xlim()
    cur_ylim = ax.get_ylim()
    xdata = event.xdata
    ydata = event.ydata 
    if event.button == 'down':
        scale_factor = 1 / base_scale
    elif event.button == 'up':
        scale_factor = base_scale
    else:
        scale_factor = 1
        print(event.button)
    ax.set_xlim([xdata - (xdata - cur_xlim[0]) / scale_factor, xdata + (cur_xlim[1] - xdata) / scale_factor])
    ax.set_ylim([ydata - (ydata - cur_ylim[0]) / scale_factor, ydata + (cur_ylim[1] - ydata) / scale_factor])
    plt.draw()  

as well as other handlers thank you for your help:)

brbatv avatar Apr 28 '20 18:04 brbatv

Hello

i'm also struggling to use this package with event handling which figure can I use to do these types of command : fig.canvas.mpl_connect("scroll_event", my_scroll_function) ? Is there any workaround to adapt previous work from more general matplotlib event handling ? OP, as i've understand, you had to use the previous API to do it right ? thank you :)

That is correct, the old matplotlib finance API worked well for this. I went into the new mplfinance.plotting file and it was easy enough to set up event handling, but I am having a difficult time setting up canvas redrawing

croganm avatar Apr 28 '20 18:04 croganm

I expect next week to begin work on the enhancement to allow you to pass in your own Figure and Axes, which should make implementation of event handling much easier. I estimate 3 to 6 weeks to complete that enhancement (depending what other distractions or issues come up in the meantime).

DanielGoldfarb avatar Apr 28 '20 18:04 DanielGoldfarb

Please ping me if you need any help with the event system.

tacaswell avatar Apr 28 '20 20:04 tacaswell

any news ? thanks

brbatv avatar Jun 18 '20 09:06 brbatv

Regarding direct support for event handling through mplfinance, I do not expect to code for this anytime in the next several months; it's hard to say if and when it will become a priority for me given a number of other projects on my plate at this time.

That said, the enhancement mentioned here to allow a caller to pass in their own Figure and Axes, while delayed, I do expect to do, and I am hoping to get it done approximately near the end of July (as noted here) .

Please correct me if I'm wrong but, based on my limited understanding, the ability to pass in your own Figure and Axes (and have mplfinance draw on them) will at least give those people who are familiar with matplotlib and its event handling the ability to do event hanlding with mplfinance plots.

DanielGoldfarb avatar Jun 18 '20 11:06 DanielGoldfarb

Hi, @croganm : p1

I also want to use the mouse event of mplfinance. I wrote an example to get fig and convert it into Matplotlib object. After setting the mouse event, I failed.

How did you solve your problem?

Please give me some guidance!

Thank you very much.

lyl1836 avatar Mar 20 '21 00:03 lyl1836

@lyl1836 mpf.plot() will not return Figure and Axes list unless you set kwarg returnfig=True:

fig, axlist = mpf.plot(df,returnfig=True)

See also https://github.com/matplotlib/mplfinance/wiki/Acessing-mplfinance-Figure-and-Axes-objects

DanielGoldfarb avatar Mar 21 '21 00:03 DanielGoldfarb

I want to use pick_event, but plot doesn't support picker. Is there a way?

lyl1836 avatar Mar 21 '21 01:03 lyl1836