如何用matplotlib.finance绘制无间隙日内1分钟OHLC K线并设置自定义刻度
解决跨时段股票K线无间隙+自定义X轴刻度的问题
我刚碰到过类似的期货跨交易日K线绘制需求,你的核心痛点在于candlestick2_ohlc是基于序列索引(0,1,2...)来绘制K线的,不是真实的时间轴,所以直接用mdates的定位器完全不生效——它只认matplotlib的日期数值,不认我们的序列位置。
下面是完整的解决方案,既能保留无间隙的K线,又能实现你要的整点主刻度、15分钟次刻度:
def generate_pseudo_data(): # datetime index data idx = pd.date_range('2017-09-13 21:01:00', '2017-09-13 23:00:00', freq='1min') idx = idx.append(pd.date_range('2017-09-14 09:01:00', '2017-09-14 10:15:00', freq='1min')) idx = idx.append(pd.date_range('2017-09-14 10:31:00', '2017-09-14 11:30:00', freq='1min')) idx = idx.append(pd.date_range('2017-09-14 13:31:00', '2017-09-14 15:00:00', freq='1min')) # OHLC inc = np.random.randint(-2, 3, size=idx.shape).cumsum() opens = 3500 + inc closes = opens + np.random.randint(-3, 3, idx.shape) range_max = np.max(np.concatenate([opens.reshape(-1, 1), closes.reshape(-1, 1)], axis=1), axis=1) highs = range_max + np.random.randint(0, 5, size=idx.shape) range_min = np.min(np.concatenate([opens.reshape(-1, 1), closes.reshape(-1, 1)], axis=1), axis=1) lows = range_min - np.random.randint(0, 5, size=idx.shape) bar_df = pd.DataFrame({'open': opens, 'high': highs, 'low': lows, 'close': closes}, index=idx) return bar_df from datetime import datetime, time import pandas as pd import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.finance import candlestick2_ohlc, candlestick_ohlc import matplotlib.dates as mdates from matplotlib import ticker bar_df = generate_pseudo_data() fig, ax = plt.subplots(figsize=(12,6)) figManager = plt.get_current_fig_manager() figManager.window.showMaximized() # 绘制无间隙K线 candlestick2_ohlc(ax, bar_df.open, bar_df.high, bar_df.low, bar_df.close, width=0.6, colorup='r', colordown='c', alpha=1) # -------------------------- 核心:自定义刻度配置 -------------------------- # 1. 准备所有K线的时间序列和对应的序列索引 x_dates = bar_df.index x_indices = np.arange(len(x_dates)) # 2. 筛选主刻度位置:整点的K线对应的索引 major_positions = x_indices[x_dates.minute == 0] # 3. 筛选次刻度位置:每15分钟的K线对应的索引(0/15/30/45分) minor_positions = x_indices[x_dates.minute.isin([0,15,30,45])] # 4. 定义主刻度定位器:返回整点的索引位置 def major_locator_func(value, pos): # 只返回在major_positions里的位置 if value in major_positions: return value return None ax.xaxis.set_major_locator(ticker.FuncLocator(major_locator_func)) # 5. 定义次刻度定位器:返回15分钟间隔的索引位置 def minor_locator_func(value, pos): if value in minor_positions: return value return None ax.xaxis.set_minor_locator(ticker.FuncLocator(minor_locator_func)) # 6. 定义刻度格式化器:把索引转成时间字符串(比如22:00、09:15) def date_formatter(value, pos): try: return x_dates[int(value)].strftime('%H:%M') except IndexError: return '' ax.xaxis.set_major_formatter(ticker.FuncFormatter(date_formatter)) ax.xaxis.set_minor_formatter(ticker.FuncFormatter(date_formatter)) # 7. 美化:旋转主刻度标签,避免重叠,调整次刻度字体大小 plt.xticks(rotation=45) ax.tick_params(axis='x', which='minor', labelsize=8, color='#999999') # 调整布局,防止标签被截断 plt.tight_layout() plt.show()
关键部分解释:
- 因为
candlestick2_ohlc的X轴是序列索引(0到N-1),所以我们必须基于这些索引来定位刻度,而不是真实的时间戳 - 先筛选出符合条件的时间点对应的索引位置,再用
FuncLocator告诉matplotlib这些位置要显示刻度 - 次刻度用更小的字体和灰色区分,让图表层次更清晰
- 最后用
tight_layout()防止X轴标签被截断
这样就能完美实现你要的效果:K线完全无间隙,X轴主刻度显示整点,次刻度显示每15分钟的时间点。
内容的提问来源于stack exchange,提问作者StayFoolish




