【问题标题】:How do I get the y axis display of a matplotlib OHLC candlestick chart to show the OHLC values rather than the y axis cursor position如何获得 matplotlib OHLC 烛台图的 y 轴显示以显示 OHLC 值而不是 y 轴光标位置
【发布时间】:2019-05-27 11:36:06
【问题描述】:

我正在使用 matplotlib 创建 OHLC 烛台股票价格图表。我正在使用 mpl_finance 的烛台_ohlc 模块来创建图表。创建图表很简单,但是图表底部的 x 和 y 轴显示显示给定光标位置的日期和 y 轴值,但我希望 x 和 y 轴显示显示日期和打开的日期,高、低、关闭 (ohlc) 值,而不仅仅是日期和 y 轴光标位置值。报价数据集采用元组列表的格式,其中每个元组包含作为数字的日期,后跟开盘价、最高价、最低价、收盘价和成交量。我正在尝试使用 matplotlib 的 format_coord 函数来指定 ohlc 值,但我无法弄清楚如何让 format_coord 函数接受包含日期和相​​关 ohlc 值的列表作为输入,然后给出所需的日期和 OHLC 输出。以下是我编写的一些简化代码,展示了我的问题: 下面的代码现在已经过修改,可以完全正常工作:

import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY
from mpl_finance import candlestick_ohlc
from matplotlib.dates import date2num, num2date


def ohlc_daily_date_axis():
    mondays = WeekdayLocator(MONDAY)  
    alldays = DayLocator()            
    weekFormatter = DateFormatter('%b %d %Y')  # e.g., Jan 12 2018

    quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
              (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
              (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
              (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
              (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
              (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
              (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
              (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
              (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
              (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]

    fig, ax = plt.subplots(figsize=(18,5))
    plt.subplots_adjust(bottom=0.2)
    ax.xaxis.set_major_locator(mondays)
    ax.xaxis.set_minor_locator(alldays)
    ax.xaxis.set_major_formatter(weekFormatter)

    candlestick_ohlc(ax, quotes, width=0.6)

    ax.xaxis_date()
    ax.autoscale_view()
    plt.setp(plt.gca().get_xticklabels(), rotation=45, 
horizontalalignment='right')
    #the following line puts the ohlc data in the y axis display
    ax.format_coord = get_ohlc_from_date_xy
    # the following line puts the ohlc data in the x axis display
    #ax.fmt_xdata = get_ohlc_from_date_x

    plt.show()

def get_ohlc_from_date_x(dateasnum):
    print('dateasnum: ', int(dateasnum))
    quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
              (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
              (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
              (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
              (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
              (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
              (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
              (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
              (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
              (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
    for i in range(len(quotes)):
        if int(dateasnum) == quotes[i][0]:
            open = quotes[i][1]
            high = quotes[i][2]
            low = quotes[i][3]
            close = quotes[i][4]
            vol = quotes[i][5]
    dte = str(num2date(dateasnum).date())
    print('type(dte): ', type(dte))
    print('open: ', open)
    ohlc_str = dte + ' open: ' + str(open) + ' high: ' + str(high) + ' 
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol)) 
+ '    '

    return ohlc_str



def get_ohlc_from_date_xy(dateasnum,y):
    print('dateasnum: ', int(dateasnum))
    quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
              (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
              (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
              (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
              (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
              (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
              (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
              (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
              (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
              (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
    for i in range(len(quotes)):
        if int(dateasnum) == quotes[i][0]:
            open = quotes[i][1]
            high = quotes[i][2]
            low = quotes[i][3]
            close = quotes[i][4]
            vol = quotes[i][5]
    dte = str(num2date(dateasnum).date())
    #print('type(dte): ', type(dte))
    #print('open: ', open)
    ohlc_str = 'open: ' + str(open) + ' high: ' + str(high) + ' 
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol))

    return dte, ohlc_str



# This def does not work
def format_coord(x,y, quotes):
    for i in range(len(quotes)):
        if int(x) == quotes[i]:
            open = quotes[i][1]
            high = quotes[i][2]
            low = quotes[i][3]
            close = quotes[i][4]
            vol = quotes[i][5]
    y = 'open: ' + open # I'm just using open to simplify things
    x = DateFormatter('%b %d %Y')
    return (x,y)


if __name__ == '__main__':
    ohlc_daily_date_axis()

如果我按原样运行此代码,则会收到以下错误(这是我使用不正确的 def format_coord(x,y, quotes) 方法时遇到的错误):

File "/Users/Me/Mee/python_db_programs/learn_matplotlib_test.py", line 33, 
in ohlc_daily_date_axis
    ax.format_coord = format_coord(quotes)
TypeError: format_coord() missing 2 required positional arguments: 'y' 
and 'quotes'

如果我注释掉 ax.format_coord = format_coord(quotes) 行,那么代码运行良好,但在 x 和 y 显示中没有我想要的日期和 ohlc 值。任何有关如何进行的帮助将不胜感激。

我最终没有尝试更改 y 显示,而是将 ohlc 值添加到 x 显示。这意味着我将 ax.format_coord = format_coord(quotes) 更改为仅格式化 x 坐标的命令,即 ax.fmt_xdata 然后编写了一个使用引号列表获取每个日期对应的 ohlc 数据的 def:

ax.fmt_xdata = get_ohlc_from_date

而不是

ax.format_coord = format_coord(quotes)

然后添加这个def:

def get_ohlc_from_date(dateasnum):
    print('dateasnum: ', int(dateasnum))
    quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
              (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
              (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
              (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
              (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
              (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
              (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
              (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
              (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
              (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
    for i in range(len(quotes)):
        if int(dateasnum) == quotes[i][0]:
            open = quotes[i][1]
            high = quotes[i][2]
            low = quotes[i][3]
            close = quotes[i][4]
            vol = quotes[i][5]
    dte = str(num2date(dateasnum).date())
    print('type(dte): ', type(dte))
    print('open: ', open)
    ohlc_str = dte + ' open: ' + str(open) + ' high: ' + str(high) + ' 
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol)) 
+ '    '

    return ohlc_str

另外,因为我使用了 matplotlibs dateasnum 函数,所以我也必须导入它:

from matplotlib.dates import num2date

虽然这不会用 ohlc 值替换 y 轴坐标,但它确实提供了 x 和 y 轴显示中的 ohlc 值

在弄清楚如何将 ohlc 值添加到 x 轴显示后,我意识到我用来将 ohlc 值添加到 x 轴显示的逻辑可以应用于 y 轴显示,从而允许显示 ohlc 值在 y 轴参数中。这是通过使用 ax.format_coord = format_coord 命令并创建一个新的 def 来完成的,该 def 将 ohlc 值分配给 y 轴返回值。我已经修改了我发布的原始代码,以便根据 ax.format_coord = format_coord 行或 ax.fmt_xdata = get_ohlc_from_date 行是否被注释掉来确定 ohlc 值是作为 x 轴显示的一部分显示还是作为一部分显示y轴显示

【问题讨论】:

  • 这里的问题到底是什么?
  • 嗨,我试图弄清楚如何让 y 轴显示来显示 OHLC 值而不是 y 轴光标位置。我能够解决问题并随后修改代码以显示解决方案。
  • 好的,你误解了问答的概念。您提出问题,您或其他任何人都可以提供答案。因此,请将所有回答您问题的内容放在 answer 中。
  • 嗨,我不清楚我应该如何回答我提出的问题。在这种情况下,我改变了我原来的问题,以便它提供我的答案。我看到我应该做的是点击页面底部的回答你的问题按钮,然后在那里发布正确的代码。回想起来,这似乎很明显。感谢您指出我的错误。
  • “回想起来”是什么意思?现在就做?!

标签: python-3.x matplotlib candlestick-chart


【解决方案1】:

以下是允许 matplotlib OHLC 烛台图的 x 和 y 轴读数以在 Y 轴读数而不是 Y 轴光标位置显示 OHLC 值的解决方案。

import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, 
MONDAY
from mpl_finance import candlestick_ohlc
from matplotlib.dates import date2num, num2date


def ohlc_daily_date_axis():
    mondays = WeekdayLocator(MONDAY)  
    alldays = DayLocator()            
    weekFormatter = DateFormatter('%b %d %Y')  # e.g., Jan 12 2018

    quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
              (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
              (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
              (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
              (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
              (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
              (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
              (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
              (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
              (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]

    fig, ax = plt.subplots(figsize=(18,5))
    plt.subplots_adjust(bottom=0.2)
    ax.xaxis.set_major_locator(mondays)
    ax.xaxis.set_minor_locator(alldays)
    ax.xaxis.set_major_formatter(weekFormatter)

    candlestick_ohlc(ax, quotes, width=0.6)

    ax.xaxis_date()
    ax.autoscale_view()
    plt.setp(plt.gca().get_xticklabels(), rotation=45, 
horizontalalignment='right')
    #the following line puts the ohlc data in the y axis display
    ax.format_coord = get_ohlc_from_date_xy
    # the following line puts the ohlc data in the x axis display
    #ax.fmt_xdata = get_ohlc_from_date_x

    plt.show()

def get_ohlc_from_date_x(dateasnum):
    print('dateasnum: ', int(dateasnum))
    quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
              (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
              (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
              (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
              (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
              (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
              (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
              (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
              (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
              (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
    for i in range(len(quotes)):
        if int(dateasnum) == quotes[i][0]:
            open = quotes[i][1]
            high = quotes[i][2]
            low = quotes[i][3]
            close = quotes[i][4]
            vol = quotes[i][5]
    dte = str(num2date(dateasnum).date())
    print('type(dte): ', type(dte))
    print('open: ', open)
    ohlc_str = dte + ' open: ' + str(open) + ' high: ' + str(high) + ' 
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol)) 
+ '    '

    return ohlc_str



def get_ohlc_from_date_xy(dateasnum,y):
    print('dateasnum: ', int(dateasnum))
    quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
              (737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
              (737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
              (737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
              (737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
              (737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
              (737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
              (737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
              (737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
              (737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
    for i in range(len(quotes)):
        if int(dateasnum) == quotes[i][0]:
            open = quotes[i][1]
            high = quotes[i][2]
            low = quotes[i][3]
            close = quotes[i][4]
            vol = quotes[i][5]
    dte = str(num2date(dateasnum).date())
    #print('type(dte): ', type(dte))
    #print('open: ', open)
    ohlc_str = 'open: ' + str(open) + ' high: ' + str(high) + ' 
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol))

    return dte, ohlc_str






if __name__ == '__main__':
    ohlc_daily_date_axis()

【讨论】:

    【解决方案2】:

    你可以使用Plotly's CandleStick Chart,内置你想要的一切。

    Here is a example

    元组到数据框的序列

    为此,您需要将数据放在包含 ["Date", "High", "Low", "Open", "Close"] 列的数据框中,查看 pandas.DataFrame.from_records 以将数据导入 DataFrame,因为它从元组序列创建 DataFrame 对象。

    其他

    • 您可能需要将日期转换为日期时间,请查看pandas.to_datetime 进行转换

    问题的答案

    来自 plotly 的documentation

    hoverinfo – 确定在悬停时显示哪些跟踪信息。如果设置了 none 或 skip ,则悬停时不显示任何信息。但是,如果没有设置,单击和悬停事件仍然会触发。

    另外值得一读: Hover Text and Formatting in Python

    最终考虑

    我知道它不是你想要的 matplotlib 制作的,但我认为它作为一个答案是相关的。

    代码

    def generatePlotly(df):
        layout = go.Layout(
            plot_bgcolor="#FFF",      # Sets background color to white
            hovermode="x",
            hoverdistance=100,        # Distance to show hover label of data point
            spikedistance=1000,       # Distance to show spike
            xaxis=dict(
                title="Data",         # X Axis Title
                linecolor="#BCCCDC",  # Sets color of X-axis line
                showgrid=False,       # Removes X-axis grid lines
                showspikes=True,      # Show spike line for X-axis
                gridcolor="#BCCCDC",  # Grid color, if enabled
                # Format spike - Show a Line at the pointer
                spikethickness=2,
                spikedash="dot",
                spikecolor="#999999",
                spikemode="across",
                fixedrange=True,
                spikesnap="cursor",
            ),
            yaxis=dict(
                title="Preço (R$)",   # Y Axis Title
                linecolor="#BCCCDC",  # Sets color of Y-axis line
                showgrid=False,       # Removes Y-axis grid lines
                gridcolor="#BCCCDC",  # Grid color, if enabled
                showspikes=True,      # Show spike line for X-axis
                # Format spike - Show a Line at the pointer
                spikethickness=2,
                spikedash="dot",
                spikecolor="#999999",
                spikemode="across",
                fixedrange=True,
                side="right",
                spikesnap="cursor",
            ),
            margin=go.layout.Margin(
                l=0,  # left margin
                r=0,  # right margin
                b=0,  # bottom margin
                t=0,  # top margin
            ),
        )
    
        fig = go.Figure(
            data=[
                go.Candlestick(
                    x=df["Date"],      # Your data
                    open=df["Open"],   
                    high=df["High"],
                    low=df["Low"],
                    close=df["Close"],
                )
            ],
            layout=layout,
        )
        # Remove rangeslider from the chart, you can just comment the next line
        fig.update_layout(xaxis_rangeslider_visible=False)
    
        # Legend position
        fig.update_layout(legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01)) 
    
        ############################################################
        # For this part look at EXTRAS at the bottom of the answer #
        ############################################################
        # build complete timepline from start date to end date
        dt_all = pd.date_range(start=df["Date"].iloc[0], end=df["Date"].iloc[-1])
        # retrieve the dates that ARE in the original datset
        dt_obs = [d.strftime("%Y-%m-%d") for d in pd.to_datetime(df["Date"])]
        # define dates with missing values
        dt_breaks = [d for d in dt_all.strftime("%Y-%m-%d").tolist() if not d in dt_obs]
        fig.update_xaxes(
            rangebreaks=[
                # dict(bounds=["sat", "mon"]),  # hide weekends
                dict(values=dt_breaks)
            ]
        )
    
        # Hover Distance and Hover Info
        # fig.update_layout(hoverdistance=0)
        # fig.update_traces(xaxis="x", hoverinfo="none")
    
        fig.show()
    

    附加内容

    Plotly: How to style a plotly figure so that it doesn't display gaps for missing dates?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      • 1970-01-01
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      • 2014-10-30
      • 2021-03-22
      相关资源
      最近更新 更多