這篇文章裡,我們將會學習如何利用python來分析金融資料。我們主要會利用mpl_finance、pandas與matplotlib三個套件庫
我們先匯入需要用到的套件庫
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import dates as mdates
from matplotlib import ticker as mticker
from mpl_finance import candlestick_ohlc
from matplotlib.dates import DateFormatter
import datetime as dt
這邊我們使用yahoo finance的台積電股票資料,檔案可在這邊下載到
https://drive.google.com/open?id=189v3i7aAfC4zqF0h26-2QFtfmVWFc91f
建立一個python筆記本或是腳本後,將腳本放在與股票資料同一個資料夾。我們就可以用以下的程式碼將股票資料讀為pandas.DataFrame格式
# 匯入資料,把csv檔(連結下方)放到與程式檔同一資料夾
df = pd.read_csv('file.csv',parse_dates=['Date'],index_col=0).dropna()
df = df[(df.index > '2018-06-01')]
df_plot = df[['Open','High','Low','Close']]
這邊我們用read_csv(pandas提供的指令)讀入csv檔案,並選取2018年6月後的股票資料,為了等一下畫股價圖的需求,我們另外建立一個DataFrame,並取名為df_plot
接下來我們來計算MA與KD線,我們的計畫畫兩個子圖,一個子圖顯示日線與交易量,另一個子圖顯示KD線,其中MA線的計算相當簡單,我們使用pandas裡面內建的rolling方法即可回傳一個MA線的DataFrame. 我們將計算的過程包裝成一個函數。
#計算MA線
def moving_average(data,period):
return data['Close'].rolling(period).mean()
另一方面,計算KD線的規則相對複雜,我們先看一下程式瑪
#計算KD線
'''
Step1:計算RSV:(今日收盤價-最近9天的最低價)/(最近9天的最高價-最近9天的最低價)
Step2:計算K: K = 2/3 X (昨日K值) + 1/3 X (今日RSV)
Step3:計算D: D = 2/3 X (昨日D值) + 1/3 X (今日K值)
'''
def KD(data):
data_df = data.copy()
data_df['min'] = data_df['Low'].rolling(9).min()
data_df['max'] = data_df['High'].rolling(9).max()
data_df['RSV'] = (data_df['Close'] - data_df['min'])/(data_df['max'] - data_df['min'])
data_df = data_df.dropna()
# 計算K
# K的初始值定為50
K_list = [50]
for num,rsv in enumerate(list(data_df['RSV'])):
K_yestarday = K_list[num]
K_today = 2/3 * K_yestarday + 1/3 * rsv
K_list.append(K_today)
data_df['K'] = K_list[1:]
# 計算D
# D的初始值定為50
D_list = [50]
for num,K in enumerate(list(data_df['K'])):
D_yestarday = D_list[num]
D_today = 2/3 * D_yestarday + 1/3 * K
D_list.append(D_today)
data_df['D'] = D_list[1:]
use_df = pd.merge(data,data_df[['K','D']],left_index=True,right_index=True,how='left')
return use_df
第一部份:計算出RSV
我們需要先計算RSV,根據定義,我們要將最近9天的最低價與最高價計算出,這邊我們使用pandas的rolling方法配合max,min即可計算出,我們將其存到新的一欄。有了max以及min欄位,我們就可以計算出RSV欄位。
第二部份:計算K線
我們先令K的初始值為50,然後要根據定義去迭代。我想把計算的K值存到一個列表中,所以先建了一個列表K_list,並填入第一個元素為50,接下來我們對RSV欄位跑迴圈,因為每次進入這個迴圈我們都能得到當天的RSV值,我們從K_list中取得昨天的K值並能計算出今天的K值,我們最後再把計算出的K值存回DataFrame一個新的column中,計算D線也是用同樣的邏輯。
有了KD線的計算函數之後,我們要來畫圖了!在這邊,我們要用mpl_finance這個套件,這是原本的matplotlib finance裡面獨立出來的一個套件,可以使用pip安裝,我們將要使用mpl_finance裡面的candlestick_ohlc函數來畫蠟燭圖,為了滿足格式需要(有時間、開盤價、收盤價、最高價與最低價排列好的DataFrame),我們想要新生成一個DataFrame,所以我們寫下以下的函數
def prepare_data(data):
data_df = data.copy()
data_df['DateTime'] = data_df.index
data_df = data_df.reset_index()
data_df = data_df[['DateTime','Open','High','Low','Close']]
data_df['DateTime'] = mdates.date2num(data_df['DateTime'].astype(dt.date))
return data_df
這個函數會將我們的原本的DataFrame抓出DateTime、Open、High、Low以及Close這幾個欄位排列好,並將原本是日期格式的日期,轉為需要candlestick_ohlc使用mdates.date2num格式。接下來就是繪製的主程式。
# 畫股價圖
# 顏色:https://matplotlib.org/users/colors.html
#畫股價線圖與蠟燭圖
def plot_stock_price(data):
Ma_10 = moving_average(data,10)
Ma_50 = moving_average(data,50)
Length = len(data['DateTime'].values[50-1:])
fig = plt.figure(facecolor='white',figsize=(15,10))
ax1 = plt.subplot2grid((6,4), (0,0),rowspan=4, colspan=4, facecolor='w')
candlestick_ohlc(ax1, data.values[-Length:],width=0.6,colorup='red',colordown='green')
Label1 = '10 MA Line'
Label2 = '50 MA Line'
ax1.plot(data.DateTime.values[-Length:],Ma_10[-Length:],'black',label=Label1, linewidth=1.5)
ax1.plot(data.DateTime.values[-Length:],Ma_50[-Length:],'navy',label=Label2, linewidth=1.5)
ax1.legend(loc='upper center', ncol=2)
ax1.grid(True, color='black')
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax1.yaxis.label.set_color("black")
ax1.tick_params(axis='y', colors='black')
ax1.tick_params(axis='x', colors='black')
plt.ylabel('Stock price and Volume')
plt.suptitle('Stock Code:2330',color='black',fontsize=16)
#畫交易量
ax1v = ax1.twinx()
ax1v.fill_between(data.DateTime.values[-Length:],0, df.Volume.values[-Length:], facecolor='navy', alpha=.4)
ax1v.axes.yaxis.set_ticklabels([])
ax1v.grid(False)
ax1v.set_ylim(0, 3*df.Volume.values.max())
ax1v.tick_params(axis='x', colors='black')
ax1v.tick_params(axis='y', colors='black')
#加入KD線在下方
ax2 = plt.subplot2grid((6,4), (4,0), sharex=ax1, rowspan=1, colspan=4, facecolor='white')
ax2.plot(daysreshape.DateTime.values[-Length:], df.K[-Length:],color='black')
ax2.plot(daysreshape.DateTime.values[-Length:], df.D[-Length:],color='navy')
plt.ylabel('KD Value', color='black')
首先,我們利用我們寫好的計算移動平均的函數先計算出10天與50天的移動平均,接下來我們新增一張畫布(fig),然後畫第一張子圖(ax1),裡面我們先利用candlestick_ohlc將股價圖畫在ax1上,接下來在將兩條MA線畫上,我們也將x軸的日期格式在轉回日期表示。
接下來我們畫交易量,交易量因為要跟股價圖分享同一個x軸,所以我們輸入指令ax1v = ax1.twinx(),然後我們使用fill_between畫出交易量。
最後我們加入KD線的子圖(ax2),並將K值與D值畫在上面。
最後我們執行
daysreshape = prepare_data(df_plot)
plot_stock_price(daysreshape)
plt.show()
即可看到成品
當我輸入最後一行程式時
【
daysreshape = prepare_data(df_plot)
plot_stock_price(daysreshape)
plt.show()
】
—————————————————————————
TypeError Traceback (most recent call last)
in
—-> 1 daysreshape = prepare_data(df_plot)
2 plot_stock_price(daysreshape)
3 plt.show()
跑出來error
TypeError: dtype ” not understood
這大概是甚麼問題呢?
謝謝!!
看起來是在prepare_data這邊有問題,感覺是執行mdates.date2num(data_df[‘DateTime’].astype(dt.date))這行時的類別有問題,可以試試看停在這行看看執行的情況。
1.
計算RSV没有乘100,設定K_list初始值是50就邏輯不通
這樣設定是要設昨天的RSV值是嗎?
2.
https://hant-kb.kutu66.com/mip/12535885
我有找到這個資料不知道是否可以使用
我嘗試把KD線在下方區塊省略掉但是後面會有NameError: name ‘daysreshape’ is not defined
1.我有點忘了,但應該是根據RSV的定義https://www.wikiwand.com/zh-tw/%E9%9A%8F%E6%9C%BA%E6%8C%87%E6%A0%87
可以看到(close – min)/(max-min)之後還要乘100,但程式沒有乘,所以可以在data_df[‘RSV’] = (data_df[‘Close’] – data_df[‘min’])/(data_df[‘max’] – data_df[‘min’] 這行多乘100修改
2.可以試試,原來是在讀資料就有問題? NameError代表找不到daysreshape這個東西,在原本的程式裡是這行控制daysreshape = prepare_data(df_plot)
所以代表您把prepare_data這行省略了?
1.程式開始不是都將K、D值分配一個數字,那如果這樣乘上100的話,那K、D值要怎麼預設?是參考昨天的K、D值還是??
2.
最後程式是這樣
daysreshape = prepare_data(df_plot)
plot_stock_price(daysreshape)
plt.show()
我看有問題好像是plot_stock_price(daysreshape)這行,上面會出現驚嘆號
NameError: name ‘daysreshape’ is not defined
1.因為第一天沒有昨天的資料,所以直接設起始值為50,但也沒一定要這麼做!
2.恩恩,但是NameError代表的意思是程式找不到daysreshape,而定義daysreshape在daysreshape = prepare_data(df_plot)這行程式,所以可能是這行執行出了問題
那這樣定義daysreshape中,在daysreshape = prepare_data(df_plot)這行程式是錯在哪裡呢?
可以看看直接執行daysreshape = prepare_data(df_plot)會發生什麼事
如果沒執行錯誤的話,可以檢查一下拼寫有沒有錯,不然不應該有NameError
我想要請問一下就是假設我資料不要跟您影片的資料一樣,假設我想從2019/01/01開始畫圖,在這當中需要修改甚麼嗎? 或者是我寫程式從2019/01/01開始到今天為止呢,假如我已經寫好並產生CSV檔,這樣能嗎?
只修改日期的話應該是這行df = df[(df.index > ‘2018-06-01’)] 可以選開始畫的時間!
from mpl_finance import candlestick_ohlc只能plot兩個圖,The New API- matplotlib/mplfinance有諸多簡化與改進,詳見
https://github.com/matplotlib/mplfinance
https://github.com/matplotlib/mplfinance/blob/master/examples/panels.ipynb 能plot0-9 panels
十分感謝您的留言,大家都可以參考看看!
K線圖上會出現非交易日的空白區域(像是週六週日沒有交易, 但那兩天還是會顯示在圖上, 而且是空白的), 導致K線圖不連續, 請問該如何畫出不含空白區域的K線圖呢
恩恩,剛查了一下,這篇使用的套件沒有內建去除周末的方法,但是有延伸的技巧來解決這個問題,
可以參考這篇所使用的方法:
https://stackoverflow.com/questions/42155916/how-to-remove-weekends-in-matplotlib-candlestick-chart