简体   繁体   English

向 MPLFinance 图表添加第三个子图会导致重叠吗?

[英]Adding a Third Subplot to MPLFinance Chart Results in Overlapping?

I'm trying to create a candlestick chart that shows the candles in the top plot, the volume in the middle plot, and a technical indicator on the bottom plot (all 100% width, 3 rows of plots).我正在尝试创建一个蜡烛图,在顶部显示蜡烛图 plot,在中间显示交易量 plot,在底部显示技术指标 plot(所有 100% 宽度,3 行图)。 I will be adding more than 3 plots, but I figured adding more would follow the same logic as adding the third.我将添加超过 3 个地块,但我认为添加更多地块将遵循与添加第三个地块相同的逻辑。

I've created an MPLFinance candlestick chart with animation and an onClick play/pause functionality.我创建了一个具有 animation 和onClick播放/暂停功能的 MPLFinance 烛台图表。 The code below runs perfectly, however the plots seem to be overlapping with one another.下面的代码运行完美,但是这些图似乎相互重叠。 It might be because of how I'm defining their shapes, but I'm not 100% sure and haven't found my answer yet.这可能是因为我如何定义它们的形状,但我不是 100% 确定并且还没有找到我的答案。

Just change the line df = pd.read_csv("./data/SPY5m.csv") near the bottom to point to your own OHLCV file with Date, Open, High, Low, Close, Volume columns in it.只需更改底部附近的行df = pd.read_csv("./data/SPY5m.csv")以指向您自己的 OHLCV 文件,其中包含Date, Open, High, Low, Close, Volume列。

import mplfinance as mpf
import matplotlib.animation as animation
import pandas as pd

class ChartClass():

    def __init__(self, DF):

        self.DF = DF
        
        # Rename some columns for MPLFinance, and set the Datetime index
        new_names = {
            'Open': 'open',
            'High': 'high',
            'Low': 'low',
            'Close': 'close',
            'Volume': 'volume',
        }
        self.DF.rename(columns=new_names, inplace=True)
        self.DF['Date'] = pd.to_datetime(self.DF['Date'])
        self.DF.set_index('Date', inplace=True)

        # Create the fig and subplots
        fig = mpf.figure(style='charles', figsize=(9,8))
        self.ax1 = fig.add_subplot(2,1,1) # Candlesticks
        self.ax2 = fig.add_subplot(3,1,2) # Volume
        self.ax3 = fig.add_subplot(4,1,3) # For third indicator's subplot? These numbers are wrong I'm sure
        
        # Define an animation function for replaying the candlesticks
        # with pause onClick capabilities
        self.animation = animation.FuncAnimation(fig, self.update, interval=100) # frames=200, blit=True
        self.paused = False
        fig.canvas.mpl_connect('button_press_event', self.toggle_pause)
        mpf.show()
        
    # Function to toggle the pause/resume of the animation
    def toggle_pause(self, *args, **kwargs):
        if self.paused:
            self.animation.resume()
        else:
            self.animation.pause()
        self.paused = not self.paused

    # Function to update the plots each interval
    def update(self, ival):
        if (20+ival) > len(self.DF):
            print('no more data to plot')
            self.animation.event_source.interval *= 3
            if self.animation.event_source.interval > 12000:
                exit()
            return
        
        # Display the latest section of OHLCV data (and the indicator for the third subplot)
        data = self.DF.iloc[0:(20+ival)]
        self.ax1.clear()
        self.ax2.clear()
        self.ax3.clear()
        indicator_for_third_subplot = mpf.make_addplot(data["MA9"], ax=self.ax3)
        
        mpf.plot(data,
                 ax=self.ax1,
                 volume=self.ax2,
                 type='candle',
                 show_nontrading=False,
                 addplot=indicator_for_third_subplot,
                 )

# Import the dataframe
df = pd.read_csv("./data/SPY5m.csv")

# Create an indicator column. Yes, I'm aware MPLFinance has an
# moving average parameter builtin, however I will be using other
# indicators from TALIB that I'd like to subplot instead, so pretend
# that `mva`(?) parameter doesn't exist
df['MA9'] = df['Close'].rolling(9).mean()

# Start the animation
ChartClass(DF=df)

图片

Notice the volume appears to be cut off at the bottom and doesn't show all the way to 0?请注意音量似乎在底部被截断并且没有一直显示到 0?

(Should mention, this is running on Windows 10 , Python 3.10.8 , mplfinance==0.12.9b1 , pandas==1.4.3 , and matplotlib==3.5.1 ) (应该提到,这是在Windows 10Python 3.10.8mplfinance==0.12.9b1pandas==1.4.3matplotlib==3.5.1上运行)

UPDATE更新

Here is the latest sort of working update, other than the chart doesn't expand with the replay like before.这是最新的工作更新,除了图表没有像以前那样随着重播而扩展。 Could be because I'm re-using the self.axes defined in the __init__ down in the update function?可能是因为我在更新 function 中重新使用__init__中定义的self.axes

class ChartClass():

    def __init__(self, DF):

        self.DF = DF
        
        # Rename some columns for MPLFinance, and set the datetime index
        new_names = {
            'Open': 'open',
            'High': 'high',
            'Low': 'low',
            'Close': 'close',
            'Volume': 'volume',
        }
        self.DF.rename(columns=new_names, inplace=True)
        self.DF['Date'] = pd.to_datetime(self.DF['Date'])
        self.DF.set_index('Date', inplace=True)
        
        # Create an indicator subplot on its own new panel
        subplot = [
            mpf.make_addplot(self.DF['MA9'], 
                             type='line', 
                             panel=2, 
                             color='lime', 
                             alpha=1, 
                             secondary_y=False,
                             ),
        ]
        
        s = mpf.make_mpf_style(base_mpf_style='charles',
                               rc={'figure.facecolor':'lightgray'})

        self.fig, self.axes = mpf.plot(self.DF,
                                       type='candle',
                                       addplot=subplot,
                                       figscale=1.5,
                                       figratio=(7,5),
                                       title='\n\nTITLE',
                                       style=s,
                                       volume=True,
                                       volume_panel=1,
                                       panel_ratios=(3,1,1),
                                       returnfig=True)

        # Deref the axes
        self.ax_main = self.axes[0]
        self.ax_volu = self.axes[1]
        self.ax_ma = self.axes[2]

        # Define an animation function for replaying the candlesticks
        # with pause onClick capabilities
        self.animation = animation.FuncAnimation(self.fig, self.update, interval=100) # frames=200, blit=True
        self.paused = False
        self.fig.canvas.mpl_connect('button_press_event', self.toggle_pause)
        mpf.show()
        
    # Function to toggle the pause/resume of the animation
    def toggle_pause(self, *args, **kwargs):
        if self.paused:
            self.animation.resume()
        else:
            self.animation.pause()
        self.paused = not self.paused

    # Function to update the plots each interval
    def update(self, ival):
        if (20+ival) > len(self.DF):
            print('no more data to plot')
            self.animation.event_source.interval *= 3
            if self.animation.event_source.interval > 12000:
                exit()
            return
        
        # .iloc the latest data
        data = self.DF.iloc[0:(20+ival)]

        # Subplot using the newly iloc'd data
        subplot = [
            mpf.make_addplot(data['MA9'], 
                             type='line', 
                             panel=2, 
                             color='lime', 
                             alpha=1, 
                             secondary_y=False,
                             ax=self.ax_ma,
                             ),
        ]

        # Clear the axes
        for ax in self.axes:
            ax.clear()

        mpf.plot(data.iloc[0:(20+ival)],
                 type='candle',
                 addplot=subplot,
                 ax=self.ax_main,
                 volume=self.ax_volu)

# Import the dataframe
df = pd.read_csv("./data/SPY5m.csv")

# Create an indicator column
df['MA9'] = df['Close'].rolling(9).mean()

# Start the animation
ChartClass(DF=df)

第二张图片

The simplest way to do this is to allow mplfinance to line up the subplots for you (although you can do it yourself, it is easier to let mplfinance do it for you).最简单的方法是让 mplfinance 为您排列子图(虽然您可以自己做,但让 mplfinance 为您做更容易)。 Read through the mplfinance tutorial on "panels" to see details on how to create three (or more) subplots.通读“面板”上的 mplfinance 教程,了解有关如何创建三个(或更多)子图的详细信息。

After carefully reading the above mentioned tutorial, see also, this example of an animated MACD technical indicator similar to what you are trying to accomplish.仔细阅读上述教程后,另请参阅此动画 MACD 技术指标示例,类似于您要完成的内容。

wildcat89 , wanted to say thanks for the elegant class structure example. wildcat89 ,想对优雅的 class 结构示例表示感谢。 I'll be strongly considering adopting this approach in some of my own work.我会认真考虑在我自己的一些工作中采用这种方法。

And Daniel, there's a big jump from doing simple matplotlib animations to using the dataframe approaches of mplfinance , so your examples gave me a HUGE "steam catapult" jump-start.丹尼尔,从制作简单的matplotlib动画到使用 mplfinance 的 dataframe 方法有很大的mplfinance ,所以你的例子给了我一个巨大的“蒸汽弹射器”快速启动。

I have one follow-up question for both of you.我有一个跟进问题要问你们两个。 I've noticed in both of your examples a section (in your update(...) method and Daniel's animate(...) method) that looks like this:我注意到在你的两个例子中有一个部分(在你的update(...)方法和 Daniel 的animate(...)方法中)看起来像这样:

if (20+ival) > len(self.DF): 
    print('no more data to plot') 
    self.animation.event_source.interval *= 3 
    if self.animation.event_source.interval > 12000: 
        exit() 
    return

...and I've NOT seen that functionality in the non- mplfinance (ie straight-up matplotlib -based) animation examples I've looked into. ...而且我还没有在我研究过的非mplfinance (即直接基于matplotlib )animation 示例中看到该功能。

Trying to figure out that little section of these examples has led me down a rabbit hole of arcane, uhhh... how do I say it?.. zero good explanations.试图弄清楚这些例子的一小部分让我陷入了一个神秘的兔子洞,呃......我怎么说呢?......零个好的解释。

It looks to me like it's intended to accelerate any loop processing and get a quicker exit when the dataframe runs out, but as I've looked into the animation... "stuffs", it seems like an increased interval would actually... slow the loop down?在我看来,它旨在加速任何循环处理并在 dataframe 用完时更快地退出,但是当我查看 animation 时......“东西”,似乎增加间隔实际上......放慢循环?

So... uhhh.... I iz confuzed.所以……呃……我很困惑。

Is there a decent explanation anywhere as to what that little section of code is intended to do?关于那一小段代码的用途,是否有任何合适的解释?

All the best!一切顺利!

@tanks_foda_helpz @tanks_foda_helpz

That's a very good question, and I probably should add a comment to explain this part of the example code in the mplfinance repository .这是一个很好的问题,我可能应该添加评论来解释mplfinance 存储库中示例代码的这一部分。

Understand that the examples are simulations .了解示例是模拟 The simulation aspect is perhaps somewhat more obvious in this particular example .此特定示例中,模拟方面可能更为明显。

In a real-world (non-simulation) scenario one would continue getting real-time data from some API, and would not run out of data .在真实世界(非模拟)场景中,人们会继续从某个 API 获取实时数据,并且不会用完数据 Although , possibly one might encounter a situation where the market is closed or suspended, and therefore may want to write a block of code similar to the code you are questioning.虽然,可能有人会遇到市场关闭或暂停的情况,因此可能想编写一段类似于您所质疑的代码的代码。

if nxt is None:
    print('no more data to plot')
    ani.event_source.interval *= 3
    if ani.event_source.interval > 12000:
        exit()

The purpose of this block of code is to indicate to the user running the simulation that the simulation is over.此代码块的目的是向运行模拟的用户指示模拟已结束。 There is no more data to simulate, thus the user should not expect the plot to continue updating/growing.没有更多数据可以模拟,因此用户不应期望 plot 继续更新/增长。

I didn't want to exit immediately, lest users (as I myself did) want to continue looking over the plot to ensure that it was correct.我不想立即退出,以免用户(就像我自己一样)想要继续查看 plot 以确保它是正确的。 At the same time, I also did not want to spew 'no more data to plot' four times a second to the console, so I gradually increased the time interval so that the no-more-data message would come out less and less frequently.同时,我也不想每秒向控制台喷出四次'no more data to plot' ,所以我逐渐增加了时间间隔,这样 no-more-data 消息出现的频率就会越来越低. Eventually we do exit the simulation (just in case the user has not yet aborted with <Ctrl>-C or some other mechanism).最终我们确实退出了模拟(以防万一用户尚未使用<Ctrl>-C或其他机制中止)。

That's it.就是这样。 Nothing particularly related to the idea of real-time updating of market data, except as mentioned: possibly, if you are able to determine when the market is closed, or that trading has been suspended, then you may want to write a message to the user indicating that the market is closed or suspended (and so don't expect any more updates to the plot).与实时更新市场数据的想法没有什么特别相关的,除了提到的:可能,如果你能够确定市场何时关闭,或者交易已经暂停,那么你可能想写一条消息给用户表示市场已关闭或暂停(因此不要期望该图有任何更新)。

Sorry for the confusion.对困惑感到抱歉。 Next time I am updating the library I will add a comment to the code there.下次我更新库时,我会在代码中添加注释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM