带布林带的K线图无法渲染:MWE运行报错求助
带布林带的K线图绘制报错:过多刻度问题
我写了一个最小可复现示例(MWE)用来绘制带布林带的SPY标的K线图,但无法正常运行,控制台只提示“过多刻度(too many ticks)”。我用的是2017年4月23日至7月1日共3个月的数据,实在搞不懂问题出在哪。以下是我的代码:
import yfinance as yf from stockstats import StockDataFrame # Convert to StockDataFrame # Need to pass a copy of candlestick_data to StockDataFrame.retype # Otherwise the original candlestick_data will be modified df = yf.download('SPY',start='2017-04-23', end = '2017-07-01') stockstats = StockDataFrame.retype(df) # 5-day exponential moving average on closing price ema_5 = stockstats["close_5_ema"] # 20-day exponential moving average on closing price ema_20 = stockstats["close_20_ema"] # 50-day exponential moving average on closing price ema_50 = stockstats["close_50_ema"] # Upper Bollinger band boll_ub = stockstats["boll_ub"] # Lower Bollinger band boll_lb = stockstats["boll_lb"] # 7-day Relative Strength Index rsi_7 = stockstats['rsi_7'] # 14-day Relative Strength Index rsi_14 = stockstats['rsi_14'] import datetime import matplotlib.pyplot as plt from matplotlib.dates import date2num, WeekdayLocator, DayLocator, DateFormatter, MONDAY from mplfinance.original_flavor import candlestick_ohlc # Create a new Matplotlib figure fig, ax = plt.subplots() # Prepare a candlestick plot candlestick_ohlc(ax, df.values, width=0.6) # Plot stock indicators in the same plot ax.plot(df.index, ema_5, lw=1, label='EMA (5)') ax.plot(df.index, ema_20, lw=1, label='EMA (20)') ax.plot(df.index, ema_50, lw=1, label='EMA (50)') ax.plot(df.index, boll_ub, lw=2, linestyle="--", label='Bollinger upper') ax.plot(df.index, boll_lb, lw=2, linestyle="--", label='Bollinger lower') ax.xaxis.set_major_locator(WeekdayLocator(MONDAY)) # major ticks on # the mondays ax.xaxis.set_minor_locator(DayLocator()) # minor ticks on the days ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d')) ax.xaxis_date() # treat the x data as dates # rotate all ticks to vertical plt.setp(ax.get_xticklabels(), rotation=90, horizontalalignment='right') ax.set_ylabel('Price (US $)') # Set y-axis label # Limit the x-axis range from 2017-4-23 to 2017-7-1 datemin = datetime.date(2017, 4, 23) datemax = datetime.date(2017, 7, 1) ax.set_xlim(datemin, datemax) plt.legend() # Show figure legend plt.tight_layout() plt.show()
问题根源与修复方案
核心问题
candlestick_ohlc的格式要求:该函数需要输入的OHLC数据第一列是数值型日期(经date2num转换),但你直接传入df.values,其中日期是datetime格式,导致matplotlib无法正确解析x轴刻度,引发“过多刻度”警告。- 冗余刻度设置:同时启用
WeekdayLocator和DayLocator会生成大量重叠刻度,加剧了刻度拥挤问题。
修改后的代码
import yfinance as yf from stockstats import StockDataFrame import datetime import matplotlib.pyplot as plt from matplotlib.dates import date2num, WeekdayLocator, DateFormatter, MONDAY from mplfinance.original_flavor import candlestick_ohlc # 下载数据并转换为StockDataFrame(传入副本避免修改原数据) df = yf.download('SPY', start='2017-04-23', end='2017-07-01') stockstats = StockDataFrame.retype(df.copy()) # 计算指标 ema_5 = stockstats["close_5_ema"] ema_20 = stockstats["close_20_ema"] ema_50 = stockstats["close_50_ema"] boll_ub = stockstats["boll_ub"] boll_lb = stockstats["boll_lb"] # 转换K线数据格式:将日期转为matplotlib兼容的数值型 ohlc_data = df.reset_index()[['Date', 'Open', 'High', 'Low', 'Close']].values ohlc_data[:, 0] = date2num(ohlc_data[:, 0]) # 创建画布 fig, ax = plt.subplots(figsize=(12, 6)) # 绘制K线图 candlestick_ohlc(ax, ohlc_data, width=0.6, colorup='g', colordown='r') # 绘制指标 ax.plot(df.index, ema_5, lw=1, label='EMA (5)') ax.plot(df.index, ema_20, lw=1, label='EMA (20)') ax.plot(df.index, ema_50, lw=1, label='EMA (50)') ax.plot(df.index, boll_ub, lw=2, linestyle="--", label='布林带上轨') ax.plot(df.index, boll_lb, lw=2, linestyle="--", label='布林带下轨') # 设置x轴刻度:仅保留周一主刻度,避免拥挤 ax.xaxis.set_major_locator(WeekdayLocator(MONDAY)) ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d')) ax.xaxis_date() # 调整刻度标签角度,减少重叠 plt.setp(ax.get_xticklabels(), rotation=45, horizontalalignment='right') ax.set_ylabel('价格(美元)') # 设置x轴范围 datemin = datetime.date(2017, 4, 23) datemax = datetime.date(2017, 7, 1) ax.set_xlim(datemin, datemax) plt.legend() plt.tight_layout() plt.show()
关键修改点
- 用
date2num将日期转换为数值型,满足candlestick_ohlc的格式要求 - 移除
DayLocator()小刻度,避免x轴刻度过多重叠 - 传入
df.copy()给StockDataFrame.retype,符合你注释中“不修改原数据”的要求 - 将刻度标签旋转角度改为45度,比90度更美观且可读性更强
内容的提问来源于stack exchange,提问作者Vincent ISOZ
相关产品推荐
相关产品推荐

