matplotlib / mplfinance

Financial Markets Data Visualization using Matplotlib
https://pypi.org/project/mplfinance/
Other
3.57k stars 618 forks source link

compatibility with mpld3 #132

Open esabunor opened 4 years ago

esabunor commented 4 years ago

hi thank you for this awesome library. I've been trying to display a candlestick chart, which works well on ipython notebook, but with mpld3, all that displays is this: Screen Shot 2020-05-12 at 6 55 15 pm

instead of this: Screen Shot 2020-05-12 at 6 54 48 pm

`df = retrieve_data() candles = pd.DataFrame( {'Date': pd.to_datetime(df['time']), 'Open': df.Open, 'High': df.High, 'Low': df.Low, 'Close': df.Close, 'Volume': df.Volume}) candles = candles[['Date', 'Open', 'High', 'Low', 'Close', 'Volume']] candles = candles.set_index(pd.DatetimeIndex(candles["Date"]))

fig, ax = mpf.plot(candles, type='candle', returnfig=True, style='nightclouds') mpld3.show()`

DanielGoldfarb commented 4 years ago

Tega, I am not familiar with mpld3 and would need some time to look into it (which presently I don't have).

Just took a quick glance though, and can make an uneducated guess: maybe you need to first call mpld3.fig_to_html(fig) and/or maybe just pass the figure in: mpld3.show(fig=fig) ??

Also, as a workaround you may use mplfinance's ability to save the figure to a png or jpg file, and then perhaps mpld3 has a method to display an image file??

Sorry I cannot be more helpful at this time. If you find the solution, please post it here. Otherwise I will probably circle back and take a closer look at this in a couple of weeks.

balajiperumaal commented 3 years ago

Having the same issue.

image

Any update on this?

DanielGoldfarb commented 3 years ago

Have any of you actually tried any of the suggestions that I made here?

So for example, maybe something like

fig, ax = mpf.plot(candles, type='candle', returnfig=True, style='nightclouds')
mpld3.fig_to_html(fig)

or similarly, maybe mpld3.save_html(fig) will work, or as suggested above mpld3.show(fig=fig)

Please list here all of things that you have tried.

balajiperumaal commented 3 years ago
def index(request):
    df = pd.read_json(os.getcwd()+'/screener/misc/curl.json')

    df.columns = ["Date", "Open", "High", "Low", "Close", "Volume"]
    df.index = pd.to_datetime(df["Date"])
    data = pd.DataFrame(df[["Open", "High", "Low", "Close"]])
    fig, ax = mpf.plot(data, type="candle", style="nightclouds", returnfig=True)
    res = mpld3.fig_to_html(fig)
    return HttpResponse(res)

image

df.columns = ["Date", "Open", "High", "Low", "Close", "Volume", "Oi"]
    df.index = pd.to_datetime(df["Date"])
    data = pd.DataFrame(df[["Open", "High", "Low", "Close"]])
    fig = mpf.figure()
    fig, ax = mpf.plot(data, type="candle", style="nightclouds", returnfig=True)
    mpld3.save_html(fig, "test.html")

Same image as above while opening test.html

DanielGoldfarb commented 3 years ago

I spent some time playing with mpld3 and mplfinance and matplotlib, and I have discovered that the problem is with mpld3, in that it does not handle matplotlib's twinx() and twiny() functionality (and mplfinance uses twinx()).

It appears that mpld3 has classified this issue as an enhancement and has been aware of the issue since 2014.

If I have some time, I will look into alternatives to mplfinance using twinx(). In the meantime, mplfinance external axes mode should work, as long as you avoid calling twinx() or twiny(). (When in external axes mode, mplfinance does not call twinx())

I have tested mpld3 with mplfinance in external axes mode and it worked, however it appears that mpld3 also does a lot of its own formatting of labels, and ticks, etc. This is true even when using plain matplotlib (not mplfinance). This means that whatever mplfinance style you may choose, there is always a possibility that mpld3 will modify that style in whatever ways it chooses.

Keep in mind also that, when in external axes mode, some mplfinance functionality becomes unavailable, and, if desired, must be implemented externally by the caller. One such example is the mplfinance's savefig kwarg. This is not available when in external axes mode, however the caller can call matplotlib's Figure.savefig() function directly on the Figure object.

balajiperumaal commented 3 years ago

Hi @DanielGoldfarb, Thanks for your help!!

I have tried external axes mode. The chart looks good. But the x-axis turned out to be a numbers instead of date.

df = pd.read_json(os.getcwd()+'/screener/misc/curl.json')

    df.columns = ["Date", "Open", "High", "Low", "Close", "Volume", "Oi"]
    df.index = pd.to_datetime(df["Date"])
    data = pd.DataFrame(df[["Open", "High", "Low", "Close"]]) # I've also tried with/without Date column
    fig = mpf.figure(style="charles")
    ax = fig.add_subplot(1, 1, 1)
    ax.grid(False)
    mpf.plot(data, type='candle', ax=ax, volume=False, returnfig=True)
    # mpf.show() <-- This works fine (but opens in separate window)
    res = mpld3.fig_to_html(fig)
    return HttpResponse(res)

Data Sample image

image I got that, this issue has to be fixed in mpld3. But as we have look into it already, you can show some light here.

Thanks

DanielGoldfarb commented 3 years ago

@balajiperumaal Balaji, there are two things that I would suggest you try. First some background:

When plotting dates or datetimes on the x-axis, matplotlib presumes time is continuous. Therefore all datetimes between the minimum x-axis datetime value and the maximum x-axis datetime value are included along the x-axis. In other words, non-trading dates will be part of the plot, and there will be gaps in the plot for weekends and holidays.

Most people prefer not to see these gaps. Therefore the default value for kwarg show_nontrading is False.

But as mentioned above, matplotlib presumes that time is continuous (a somewhat reasonable assumption).

Therefore the way to implement not plotting non-trading days is to make the x-axis not datetimes, but simply the row number in the dataframe. This ensures that there are no gaps in the plot. However, if we want to format the x-axis with datetimes, then we need to map from dataframe row number to the datetime for that row. You can see mplfinance doing this in the code here.

With that background understanding, here are two suggestions to try:

  1. Set show_nontrading=True. You will see gaps for weekends and holidays, but I believe (not sure) mpld3 will recognize the matplotlib dates and format the x-axis accordingly.

  2. If you want to leave show_nontrading=False, then try setting the formatter for the x-axis. I am not sure if mpld3 will respect this, but it's worth a try. (I did noticed, when I was experimenting with matplotlib and mpld3, that mpld3 sometimes likes to do its own formating of axis ticks, ignoring what matplotlib would otherwise do.) Here is how you can set the axis formatter similar to what mplfinance normally does:

    
    from mplfinance._utils import IntegerIndexDateTimeFormatter
    import matplotlib.dates as mdates

dates = mdates.date2num(df.index.tz_localize(None).to_pydatetime()) formatter = IntegerIndexDateTimeFormatter(dates,'%b %d') # '%b %d' can be any strftime() format you want ax.xaxis.set_major_formatter(formatter)


I would be interested to know if this works.  (Note: `'%b %d'` can be any [`strftime()` format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes) that you want.)
balajiperumaal commented 3 years ago

Hi @DanielGoldfarb

I tried using formatter with no luck. When I use show_nontrading=True the chart looks like below (Still numbers instead of date)

image

Thanks for your elaborated answer

DanielGoldfarb commented 3 years ago

@balajiperumaal Please show the output from the following:

print(data.index)
print(type(data.index))
print(type(data.index[0]))

I assume your code is still as shown here. If not, please indicate full code. Also, if possible, please post your data input file somewhere that I can access it. If time permits I will look into this during the week.

balajiperumaal commented 3 years ago

Hi @DanielGoldfarb Thanks again for the quick response.

here is the screenshot of the required variables.

image

The result of this code, image

Sample Input File

I've also tried to remove tz_localize(None) as it is already a timezone. But still it didn't work.

Thanks

haziq-zur commented 2 years ago

Hi @balajiperumaal , previously I also encounter this kind of issue but with plotly graphing packages. In my case, I had to reorganize the data back into time format after the indexing is completed. Maybe you can also try this fix.

df.reset_index(inplace=True)
df['Date'] = df['Date'].map(mdates.date2num)
df['Date'] = df['Date'].map(mdates.num2date)

On a side note, I have my columns in the right order but there is still a problem. The date doesn’t have the right format and since it is the index, I cannot manipulate it. Therefore, I reset the index and then convert the datetime to a number by using:

df.reset_index(inplace=True)
df['Date'] = df['Date'].map(mdates.date2num)