matplotlib / mplfinance

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

Subplot and panel work fine when they use separately, but mix up when I try to use them in same figure, how can I solve? #649

Closed archerhu77 closed 6 months ago

archerhu77 commented 7 months ago

Ask anything you want about mplfinance usage, project philosophy and/or priorities, or anything else related to mplfinance.

[My target] I try to draw a figure(or big plot), it'll have two subplot in vertical. Each subplot have main plot and panel, main plot draw candlestick and panel draw RSI,MACD. And I'll put the figure.canvas in some area in PySide6(or PyQT).

[The first step] I try to write some code to buld a single plot what I want like this, and it works fine:

------------------------------- single plot start ----------------------------

-- coding:utf-8 --

import matplotlib.pyplot as plt import mplfinance as mpf import numpy as np import pandas as pd import talib import sys

Generate data

dates = pd.date_range('20220101', periods=100) open_prices = np.random.randint(100, 200, size=100) high_prices = open_prices + np.random.randint(0, 10, size=100) low_prices = open_prices - np.random.randint(0, 10, size=100) close_prices = np.random.randint(100, 200, size=100) volume = np.random.randint(1000, 5000, size=100)

df1 = pd.DataFrame({'Open': open_prices, 'High': high_prices, 'Low': low_prices, 'Close': close_prices, 'Volume': volume}, index=dates) df1['rsi6'] = talib.RSI(df1['Close'], timeperiod=6) df1['upper'], df1['middler'], df1['lower'] = talib.BBANDS(df1['Close'], timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)

add_plot1 = [mpf.make_addplot(df1[['upper','middler','lower']]), mpf.make_addplot(df1['rsi6'],panel=1,ylabel='RSI6')] mpf.plot(df1,title='stock1',addplot=add_plot1,panel_ratios=(3,1))

------------------------------- single plot end ----------------------------

[second step] I try to place two different plot to subplot, like following:

------------------------------- single plot start ----------------------------

-- coding:utf-8 --

import matplotlib.pyplot as plt import mplfinance as mpf import numpy as np import pandas as pd import talib import sys

Generate data

dates = pd.date_range('20220101', periods=100) open_prices = np.random.randint(100, 200, size=100) high_prices = open_prices + np.random.randint(0, 10, size=100) low_prices = open_prices - np.random.randint(0, 10, size=100) close_prices = np.random.randint(100, 200, size=100) volume = np.random.randint(1000, 5000, size=100)

df1 = pd.DataFrame({'Open': open_prices, 'High': high_prices, 'Low': low_prices, 'Close': close_prices, 'Volume': volume}, index=dates) df1['rsi6'] = talib.RSI(df1['Close'], timeperiod=6) df1['upper'], df1['middler'], df1['lower'] = talib.BBANDS(df1['Close'], timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)

Generate more data

dates = pd.date_range('20230414', periods=50) open_prices = np.random.randint(100, 200, size=50) high_prices = open_prices + np.random.randint(0, 10, size=50) low_prices = open_prices - np.random.randint(0, 10, size=50) close_prices = np.random.randint(100, 200, size=50) volume = np.random.randint(1000, 5000, size=50)

df2 = pd.DataFrame({'Open': open_prices, 'High': high_prices, 'Low': low_prices, 'Close': close_prices, 'Volume': volume}, index=dates) df2['rsi6'] = talib.RSI(df2['Close'], timeperiod=6) df2['upper'], df2['middler'], df2['lower'] = talib.BBANDS(df2['Close'], timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)

fig = mpf.figure(figsize=(16, 8)) ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2)

add_plot1 = [ mpf.make_addplot(df1[['upper', 'middler', 'lower']], ax=ax1), mpf.make_addplot(df1['rsi6'], ylabel='RSI6', ax=ax1, panel=0) # Note the change here ] add_plot2 = [ mpf.make_addplot(df2[['upper', 'middler', 'lower']], ax=ax2), mpf.make_addplot(df2['rsi6'], ylabel='RSI6', ax=ax2, panel=0) ]

mpf.plot(df1, ax=ax1, type='candle', addplot=add_plot1, panel_ratios=(3, 1)) mpf.plot(df2, ax=ax2, type='candle', addplot=add_plot2, panel_ratios=(3, 1)) mpf.show()

------------------------------- single plot start ----------------------------

When I run this code, I found that rsi6 draw in main plot test1

But I still want the panel independent and share x-axis with it's main plot like this: test2

How can I modify my code to do that? Thanks!

DanielGoldfarb commented 7 months ago

I suggest, do not do subplots. Do only panels.

Officially there can only be one "main" plot. However you can make a "second" main plot by passing type='candle' to one of your make_addplot() calls.

archerhu77 commented 7 months ago

I suggest, do not do subplots. Do only panels.

Officially there can only be one "main" plot. However you can make a "second" main plot by passing type='candle' to one of your make_addplot() calls.

But if only one main plot with three panel, that will make all three panel share x-axis whith main plot. And that's not I want. I want to show a plot with it's panel and other plot with it's panel, they are daily klines and 60min klines, and they have different x-axis.

I found a way to do that what generate two image and merge together, but I think it's not a best way(or offical way) to do that. And it is not any clue in offical document told me that couldn't put subplot and panel together.

archerhu77 commented 7 months ago

I found a way to do similar thing in following code:

fig = mpf.figure(figsize=(8,6)) ax1 = fig.add_subplot(2,2,1) ax2 = fig.add_subplot(2,2,2) av1 = fig.add_subplot(2,2,3) av2 = fig.add_subplot(2,2,4)

mpf.plot(df1,type='candle',ax=ax1, mav=(5),axtitle='stock1') mpf.plot(df2,type='candle',ax=ax2, mav=(5),axtitle='stock2') av1.plot(df1.index,df1['rsi6']) av2.plot(df2.index,df2['rsi6']) mpf.show()

It's make a 2x2 subplots, use mpf draw candlestick, use matplotlib.pyplot to draw rsi6. But there is some problem like: ax1 and av1 couldn't share x-axis, the result seems amateurish, etc. This is not a satisfactory solution.

DanielGoldfarb commented 7 months ago

The number of panels is entirely up to you ... you decide and code it accordingly.

Take a look, for example, at cell In [24] near the bottom of https://github.com/matplotlib/mplfinance/blob/master/examples/addplot.ipynb

archerhu77 commented 7 months ago

The number of panels is entirely up to you ... you decide and code it accordingly.

Take a look, for example, at cell In [24] near the bottom of https://github.com/matplotlib/mplfinance/blob/master/examples/addplot.ipynb

Thanks for your reply. I try to check the code you said. Yes, it can set serval panel in a plot, but they will share same x-axis. That's not the situation what I said.

I want the result is: create a one column four row plot. the first row is candlestick 1(which is daily k-lines), the second row is rsi6 for the first row, they will share x-axis. And the third row is candlestick 2(which is 60min k-lines, so it's will use a different x-axis), the fourth row is rsi6 for the third row, the third and fourth row will share x-axis.

DanielGoldfarb commented 7 months ago

I'm sorry, I didn't read carefully enough. I didn't realize you wanted two different x-axes. Yes, the way mplfinance is written, panels will always share x-axes, so you will have to use external axes mode. I will go back and read more carefully and see if I can find a better solution for you.

archerhu77 commented 7 months ago

I'm sorry, I didn't read carefully enough. I didn't realize you wanted two different x-axes. Yes, the way mplfinance is written, panels will always share x-axes, so you will have to use external axes mode. I will go back and read more carefully and see if I can find a better solution for you.

Thank you very much.

I tried lots of solution but all failed. It seems your solution posted at other issue what generate two image and merge together is the only way to do that.(https://github.com/matplotlib/mplfinance/issues/318#issuecomment-766284141)

archerhu77 commented 6 months ago

It seems to generate two image of candlestick and combine to one in fig is the only way to solve it. But it's clearly it will bring some shortcomings(for example it's hard to add mouse cross because it's not a candlestick but a image).

I will close this issue, thanks @DanielGoldfarb