之前研究過使用 matplotlib.finance 中的 candlestick_ohlc() 方法繪制k線圖,但是有個很無語的問題,繪制出來的k線不是連續(xù)的——周末兩天沒有被去掉,因此總是留下一個空檔。更不要說遇上春節(jié)這樣的節(jié)假日,k線將留下很大一段無用的空白。非常影響觀察k線走勢。
google查詢了一陣,零零散散看到一些解決思路,某些代碼可用;另外一些不知是因為當時使用的開源包和現(xiàn)在有所差異還是什么的,總之把大段代碼復制過來測試,得到的只是一堆報錯信息。不過,總歸解決思路還是明確了。
解決思路
由于candlestick_ohlc() 方法內(nèi)部是將一個連續(xù)的日期作為x軸的刻度送到matplotlib的繪圖引擎中的,如果不是采用修改 candlestick_ohlc() 的源碼,那么比較合理方法就是不要將日期數(shù)據(jù)送到 candlestick_ohlc() 方法中,并且重新自定義 x 軸的刻度。
股票數(shù)據(jù)重構(gòu)
matplotlib 官方給出的candlestick_ohlc() 的推薦使用方式是這樣:
mpf.candlestick_ochl(ax,data_mat,colordown='#53c156', colorup='#ff1717',width=0.3,alpha=1)
其中 ax 是繪制圖形的 axis 對象,data_mat 是所有的股票數(shù)據(jù)。股票數(shù)據(jù)是一個二維矩陣,每一行都是按照 date,open,close,high,low,volume 的順序排列的。這里 date 的值并不是 string,也不是 datetime,而是 pandas.TimeStamp。其實TimeStamp就是一個整型數(shù)字,類似于unix 系統(tǒng)中的 timestamp。
所以在構(gòu)建股票數(shù)據(jù)時,date 這個位置我們可以將它賦值為從0開始的連續(xù)自然數(shù),這樣 candlestick_ochl() 方法繪圖時,就不會把 date 轉(zhuǎn)化為一個連續(xù)的日期(還包含周末那種)。所以,重構(gòu)后的股票數(shù)據(jù)大致應該是這樣:
[
(0, 16.14, 16.24, 16.36, 16.14, 481999.28),
(1, 16.24, 16.32, 16.38, 16.2, 424100.84),
(2, 16.32, 16.33, 16.39, 16.32, 276957.25),
(3, 16.3, 16.17, 16.38, 16.16, 277753.09)
]
每一行都是一個元組,元組里分別是 date, open, close, high, close 數(shù)據(jù)。
這樣一來,繪制的圖形就變成了:
可以看到,k線圖形變得連貫了。但是x軸的刻度卻變成了自然數(shù),而非日期。所以,x 軸的刻度需要單獨處理一下。
x軸刻度設(shè)定
假定所有的日期字符串都在 data['date'] 中,簡單把所有日期數(shù)據(jù)甩給matplotlib,x軸的刻度就會密密麻麻的擠在一起。
ax.set_xticks(range(len(date_tickers)))
ax.set_xticklabels(date_tickers)
那么如何讓 matplotlib 在繪圖時只保留主要刻度呢?
如果只是這樣:
ax.set_xticklabels(date_tickers)
乍一看,問題解決了!但是仔細一看,刻度不對!最后一個日期居然還是 2017-1-12 日,而k線已經(jīng)是60天的數(shù)據(jù)了。
正確的姿勢應該是用:
import matplotlib.ticker as ticker
# 先設(shè)定一個日期轉(zhuǎn)換方法
def format_date(x,pos=None):
# 由于前面股票數(shù)據(jù)在 date 這個位置傳入的都是int
# 因此 x=0,1,2,...
# date_tickers 是所有日期的字符串形式列表
if x<0 or x>len(date_tickers)-1:
return ''
return date_tickers[int(x)]
# 用 set_major_formatter() 方法來修改主刻度的文字格式化方式
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
這樣一來,就變成我想要的效果了:
但是,還有一點不滿意,matplotlib自動生成的主刻度的間距,我認為太寬了。那么,我還可以用:
ax.xaxis.set_major_locator(ticker.MultipleLocator(6))
來強制指定每隔6個刻度,設(shè)定一個主刻度。圖形效果就變成了這樣:
完整的代碼
import matplotlib.pyplot as plt
import matplotlib.finance as mpf
import numpy as np
import pandas as pd
from matplotlib.pylab import date2num
import matplotlib.ticker as ticker
import time
data=pd.read_csv(u'assets/興業(yè)銀行.csv',usecols=['date','open','close','high','low','volume'])
data[data['volume']==0]=np.nan
data=data.dropna()
data.sort_values(by='date',ascending=True,inplace=True)
# 原始的csv 讀入進來 DataFrame 的 columns 順序不符合candlestick_ochl 要求的順序
# columns 的順序一定是 date, open, close, high, low, volume
# 這樣才符合 candlestick_ochl 繪圖要求的數(shù)據(jù)結(jié)構(gòu)
# 下面這個是改變列順序最優(yōu)雅的方法
data=data[['date','open','close','high','low','volume']]
data=data.head(62)
# 生成橫軸的刻度名字
date_tickers=data.date.values
weekday_quotes=[tuple([i]+list(quote[1:])) for i,quote in enumerate(data.values)]
# print weekday_quotes
fig,ax=plt.subplots(figsize=(1200/72,480/72))
def format_date(x,pos=None):
if x<0 or x>len(date_tickers)-1:
return ''
return date_tickers[int(x)]
ax.xaxis.set_major_locator(ticker.MultipleLocator(6))
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
ax.grid(True)
# fig.autofmt_xdate()
mpf.candlestick_ochl(ax,weekday_quotes,colordown='#53c156', colorup='#ff1717',width=0.2)
plt.show()