這篇文章裡,我們將會學習如何利用python來分析金融資料。我們主要會利用mpl_finance、pandas與matplotlib三個套件庫
我們先匯入需要用到的套件庫
1234567 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格式
1234 # 匯入資料,把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. 我們將計算的過程包裝成一個函數。
123 #計算MA線
def
moving_average(data,period):
return
data[
'Close'
].rolling(period).mean()
另一方面,計算KD線的規則相對複雜,我們先看一下程式瑪
123456789101112131415161718192021222324252627282930 #計算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,所以我們寫下以下的函數
1234567 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格式。接下來就是繪製的主程式。
12345678910111213141516171819202122232425262728293031323334353637 # 畫股價圖
#畫股價線圖與蠟燭圖
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值畫在上面。
最後我們執行
123 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