一步一步教你用Python畫出專業的K線圖

漂亮的界面是量化程序的臉面,直觀專業的界面能幫助我們了解股票的走勢和形象地展示量化交易的結果。本文介紹如何使用matplotlib做出專業的K線圖和展示各種技術指標:

matplotlib是受MATLAB的啟發構建的。MATLAB是數據繪圖領域廣泛使用的語言和工具。MATLAB語言是面向過程的。利用函數的調用,MATLAB中可以輕鬆的利用一行命令來繪製直線,然後再用一系列的函數調整結果。

matplotlib有一套完全仿照MATLAB的函數形式的繪圖介面,十分方便實用。下面我們一步步使用它來繪出上面的圖片。

準備工作

在上文中(量化回測的基礎:乾淨的,準確的歷史股票數據),我們已經為大家準備好了日k線數據,大家可以去下載。下面我們來為繪圖做一些準備工作:

from pandas import DataFrame, Seriesimport pandas as pd; import numpy as npimport matplotlib.pyplot as pltfrom matplotlib import dates as mdatesfrom matplotlib import ticker as mtickerfrom matplotlib.finance import candlestick_ohlcfrom matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY,YEARLYfrom matplotlib.dates import MonthLocator,MONTHLYimport datetime as dtimport pylabdaylinefilespath = "G:\dayline\"stock_b_code = "000001" #平安銀行MA1 = 10MA2 = 50startdate = dt.date(2016, 6, 29)enddate = dt.date(2017, 1, 30)def readstkData(rootpath, stockcode, sday, eday): returndata = pd.DataFrame() for yearnum in range(0,int((eday - sday).days / 365.25)+1): theyear = sday + dt.timedelta(days = yearnum * 365) # build file name filename = rootpath + theyear.strftime("%Y") + "\" + str(stockcode).zfill(6) + ".csv" try: rawdata = pd.read_csv(filename, parse_dates = True, index_col = 0, encoding = "gbk") except IOError: raise Exception("IoError when reading dayline data file: " + filename) returndata = pd.concat([rawdata, returndata]) # Wash data returndata = returndata.sort_index() returndata.index.name = "DateTime" returndata.drop("amount", axis=1, inplace = True) returndata.columns = ["Open", "High", "Close", "Low", "Volume"] returndata = returndata[returndata.index < eday.strftime("%Y-%m-%d")] return returndata

這裡定義了幾個全局變數:

  1. daylinefilespath 是下載的日k線數據
  2. stock_b_code 是要顯示的股票代碼,大家可以換成自己想要的股票
  3. MA1和MA2是移動平均線的日期間隔,例如10是10日移動平均線
  4. startdate和enddate是想要顯示的日期範圍

函數readstkData讀入數據並做了必要的合併和清洗。注意這裡年做了簡化處理,並不完全準確。

繪出日K線

Talk is cheap,我們直接上代碼:

def main(): days = readstkData(daylinefilespath, stock_b_code, startdate, enddate) # drop the date index from the dateframe & make a copy daysreshape = days.reset_index() # convert the datetime64 column in the dataframe to "float days" daysreshape["DateTime"]=mdates.date2num(daysreshape["DateTime"].astype(dt.date)) # clean day data for candle view daysreshape.drop("Volume", axis=1, inplace = True) daysreshape = daysreshape.reindex(columns=["DateTime","Open","High","Low","Close"]) Av1 = movingaverage(daysreshape.Close.values, MA1) Av2 = movingaverage(daysreshape.Close.values, MA2) SP = len(daysreshape.DateTime.values[MA2-1:]) fig = plt.figure(facecolor="#07000d",figsize=(15,10)) ax1 = plt.subplot2grid((6,4), (1,0), rowspan=4, colspan=4, axisbg="#07000d") candlestick_ohlc(ax1, daysreshape.values[-SP:], width=.6, colorup="#ff1717", colordown="#53c156") Label1 = str(MA1)+" SMA" Label2 = str(MA2)+" SMA" ax1.plot(daysreshape.DateTime.values[-SP:],Av1[-SP:],"#e1edf9",label=Label1, linewidth=1.5) ax1.plot(daysreshape.DateTime.values[-SP:],Av2[-SP:],"#4ee6fd",label=Label2, linewidth=1.5) ax1.grid(True, color="w") ax1.xaxis.set_major_locator(mticker.MaxNLocator(10)) ax1.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d")) ax1.yaxis.label.set_color("w") ax1.spines["bottom"].set_color("#5998ff") ax1.spines["top"].set_color("#5998ff") ax1.spines["left"].set_color("#5998ff") ax1.spines["right"].set_color("#5998ff") ax1.tick_params(axis="y", colors="w") plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune="upper")) ax1.tick_params(axis="x", colors="w") plt.ylabel("Stock price and Volume") plt.show()if __name__ == "__main__": main()

這裡有幾點要注意:

  1. 為了調用matplotlib的蠟燭圖函數,我們把數據重新排布了一下,並修改了日期的格式。以滿足candlestick_ohlc函數的要求。
  2. 創立了ax1來顯示日k線,後面還會建立其他幾個子圖。

我們來運行一下:

效果還行,不但有K線,還疊加了MA1和MA2。下面我們來繪製其他技術參數。

繪製RSI

下面我們在頂部加入RSI:

# plot an RSI indicator on top maLeg = plt.legend(loc=9, ncol=2, prop={"size":7}, fancybox=True, borderaxespad=0.) maLeg.get_frame().set_alpha(0.4) textEd = pylab.gca().get_legend().get_texts() pylab.setp(textEd[0:5], color = "w") ax0 = plt.subplot2grid((6,4), (0,0), sharex=ax1, rowspan=1, colspan=4, axisbg="#07000d") rsi = rsiFunc(daysreshape.Close.values) rsiCol = "#c1f9f7" posCol = "#386d13" negCol = "#8f2020" ax0.plot(daysreshape.DateTime.values[-SP:], rsi[-SP:], rsiCol, linewidth=1.5) ax0.axhline(70, color=negCol) ax0.axhline(30, color=posCol) ax0.fill_between(daysreshape.DateTime.values[-SP:], rsi[-SP:], 70, where=(rsi[-SP:]>=70), facecolor=negCol, edgecolor=negCol, alpha=0.5) ax0.fill_between(daysreshape.DateTime.values[-SP:], rsi[-SP:], 30, where=(rsi[-SP:]<=30), facecolor=posCol, edgecolor=posCol, alpha=0.5) ax0.set_yticks([30,70]) ax0.yaxis.label.set_color("w") ax0.spines["bottom"].set_color("#5998ff") ax0.spines["top"].set_color("#5998ff") ax0.spines["left"].set_color("#5998ff") ax0.spines["right"].set_color("#5998ff") ax0.tick_params(axis="y", colors="w") ax0.tick_params(axis="x", colors="w") plt.ylabel("RSI")

我們在ax1的上面加上了ax0子圖,用以繪製RSI曲線。程序比較簡單,我們來運行一下:

繪製成交量

所有的股票軟體都有成交量圖,我們當然也不能免俗。我們不再增加子圖,而是把成交量疊加在k線圖下面,用淺藍色標識,這樣既清楚又節省空間:

volumeMin = 0 ax1v = ax1.twinx() ax1v.fill_between(daysreshape.DateTime.values[-SP:],volumeMin, days.Volume.values[-SP:], facecolor="#00ffe8", alpha=.4) ax1v.axes.yaxis.set_ticklabels([]) ax1v.grid(False) ###Edit this to 3, so it"s a bit larger ax1v.set_ylim(0, 3*days.Volume.values.max()) ax1v.spines["bottom"].set_color("#5998ff") ax1v.spines["top"].set_color("#5998ff") ax1v.spines["left"].set_color("#5998ff") ax1v.spines["right"].set_color("#5998ff") ax1v.tick_params(axis="x", colors="w") ax1v.tick_params(axis="y", colors="w")

注意這裡不是家裡一個新的ax,而是從ax1上加入了個twinx,從而和k線共用x軸。效果如下:

繪製MACD

我們再在最下面畫出MACD圖。經過了前面那些步驟,你一定知道只要增加個新的子圖就好了。是的,程序比較簡單:

# plot an MACD indicator on bottom ax2 = plt.subplot2grid((6,4), (5,0), sharex=ax1, rowspan=1, colspan=4, axisbg="#07000d") fillcolor = "#00ffe8" nslow = 26 nfast = 12 nema = 9 emaslow, emafast, macd = computeMACD(daysreshape.Close.values) ema9 = ExpMovingAverage(macd, nema) ax2.plot(daysreshape.DateTime.values[-SP:], macd[-SP:], color="#4ee6fd", lw=2) ax2.plot(daysreshape.DateTime.values[-SP:], ema9[-SP:], color="#e1edf9", lw=1) ax2.fill_between(daysreshape.DateTime.values[-SP:], macd[-SP:]-ema9[-SP:], 0, alpha=0.5, facecolor=fillcolor, edgecolor=fillcolor) plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune="upper")) ax2.spines["bottom"].set_color("#5998ff") ax2.spines["top"].set_color("#5998ff") ax2.spines["left"].set_color("#5998ff") ax2.spines["right"].set_color("#5998ff") ax2.tick_params(axis="x", colors="w") ax2.tick_params(axis="y", colors="w") plt.ylabel("MACD", color="w") ax2.yaxis.set_major_locator(mticker.MaxNLocator(nbins=5, prune="upper")) for label in ax2.xaxis.get_ticklabels(): label.set_rotation(45)

運行效果如下:

收尾

不知你注意到沒有,上圖有幾個問題:

  1. x軸日期出現了三次
  2. 沒有title

下面我們做一些美化工作,並演示如何加入一個提示點,將來你可以用它來展示自己量化操作的節點:

plt.suptitle(stock_b_code,color="w") plt.setp(ax0.get_xticklabels(), visible=False) plt.setp(ax1.get_xticklabels(), visible=False) # Mark big event # TODO: Make a real case here ax1.annotate("BreakNews!",(daysreshape.DateTime.values[155],Av1[155]), xytext=(0.8, 0.9), textcoords="axes fraction", arrowprops=dict(facecolor="white", shrink=0.05), fontsize=10, color = "w", horizontalalignment="right", verticalalignment="bottom") plt.subplots_adjust(left=.09, bottom=.14, right=.94, top=.95, wspace=.20, hspace=0) plt.show()

好了,大功告成:

結語

大家可能注意到了,有很多代碼在配色。是的,如果我們把所有的colorxxx都刪掉,也可以顯示出大致的曲線,但是卻非常難看。這也是為什麼要改變預設matplotlib配色的原因。

大家可以實驗一下更新前面的幾個全局變數,比如換一換股票、MA或者開始結束日期什麼的,這裡給大家一個思考題:

在readstkData()切片時為什麼只切了結束時間沒有切開始時間?SP變數是幹什麼用的?

歡迎關注本專欄!


推薦閱讀:

[18] Python元組
如何看待微軟 Pyjion 的進展以及 CPython 性能優化的未來?
安卓運行Python的神器:QPython
Python中你可能不知道的platform
Kaggle HousePrice : LB 0.11666(前15%), 用搭積木的方式(2.實踐-特徵工程部分)

TAG:Python | 量化交易 | 软件开发 |