在上一篇文章中已經充分測試了用 mplfinance 來繪製 K 線圖的方法, 本篇要來測試在 K 線圖上疊圖, 以及在其底下添加副圖的方法, 本系列之前的文章參考 :
mplfinance 教學文件參考 :
1. yfinance 套件用法摘要 :
本篇測試使用 yfinance 的股價資料源, 用法摘要整理如下 :
匯入 yfinance 與 pandas 套件 :
import yfinance as yf
import pandas as pd
呼叫 download() 函式取得交易資料的 DataFrame :
df=yf.download(symbol [, start, end] [, period, interval])
參數用法如下表 :
download() 參數 | 說明 |
symbol | 股票代號 (字串), 美股例如 'AMD' (超微), 台股後面要加 '.tw', 例如 '0050.tw' |
start | 起始日期 YYYY-MM-DD (字串), 例如 '2022-08-22' |
end | 結束日期 YYYY-MM-DD (字串), 例如 '2022-09-06', 注意, 不包含此日資料 |
period | 期間, 可用 d (日), mo(月), y(年), ytd, max(全部), 例如 5d (5 天), 3mo(三個月) |
interval | 頻率, 可用 m(分), h(小時), d(日), wk(周), mo(月), 例如 1m(一分線) |
注意, 傳回值不包含 end 那天的資料.
設定 Pandas 的選項以便能顯示全部欄位 :
pd.set_option('display.max_columns', None) # 顯示全部欄位
pd.set_option('display.width', 1000) # 設定總欄寬為 1000 px
例如 :
>>> import yfinance as yf
>>> import pandas as pd
>>> import mplfinance as mpf
下載指定期間之日頻交易資料 :
>>> df=yf.download('0050.tw', start='2024-07-01', end='2024-08-21')
[*********************100%%**********************] 1 of 1 completed
>>> df.columns
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'], dtype='object')
>>> df.shape
(37, 6)
可見共有 37 筆日收盤資料 :
>>> df
Open High ... Adj Close Volume
Date ...
2024-07-01 186.550003 188.050003 ... 185.651352 7319803
2024-07-02 185.949997 186.750000 ... 184.407715 5444336
2024-07-03 187.600006 187.600006 ... 186.646271 6478757
2024-07-04 190.600006 192.649994 ... 191.521362 12644207
2024-07-05 192.600006 192.850006 ... 190.824921 9733333
2024-07-08 191.899994 198.500000 ... 196.844162 17352467
2024-07-09 197.449997 200.050003 ... 196.645172 15488789
2024-07-10 196.949997 198.399994 ... 197.341629 7168611
2024-07-11 200.649994 202.800003 ... 201.719254 9796730
2024-07-12 198.199997 198.350006 ... 195.351791 14158267
2024-07-15 197.649994 198.100006 ... 195.699997 9790185
2024-07-16 196.550003 199.000000 ... 196.850006 13255538
2024-07-17 196.399994 196.500000 ... 194.100006 14489778
2024-07-18 188.600006 190.899994 ... 190.600006 17880013
2024-07-19 188.750000 189.000000 ... 186.250000 16453910
2024-07-22 185.399994 185.399994 ... 180.699997 21211846
2024-07-23 183.699997 186.300003 ... 186.300003 12527079
2024-07-24 186.300003 186.300003 ... 186.300003 0
2024-07-25 186.300003 186.300003 ... 186.300003 0
2024-07-26 178.050003 179.449997 ... 179.000000 25906618
2024-07-29 181.800003 182.000000 ... 180.600006 9946264
2024-07-30 179.250000 181.300003 ... 180.600006 13107381
2024-07-31 178.850006 181.149994 ... 180.850006 9078554
2024-08-01 185.000000 185.100006 ... 184.000000 14061699
2024-08-02 178.149994 178.899994 ... 174.699997 29706815
2024-08-05 166.649994 166.649994 ... 158.750000 55577204
2024-08-06 167.300003 170.399994 ... 167.500000 63140961
2024-08-07 169.850006 174.750000 ... 174.149994 31824740
2024-08-08 171.050003 172.399994 ... 170.550003 25841105
2024-08-09 175.000000 176.899994 ... 175.850006 23353756
2024-08-12 177.250000 179.800003 ... 178.050003 17571739
2024-08-13 179.300003 179.500000 ... 178.500000 10858460
2024-08-14 180.350006 181.850006 ... 180.750000 18714319
2024-08-15 180.600006 181.000000 ... 179.350006 11481647
2024-08-16 183.000000 183.649994 ... 183.399994 13205915
2024-08-19 183.500000 184.699997 ... 183.649994 9280593
2024-08-20 184.600006 185.199997 ... 183.750000 11060075
[37 rows x 6 columns]
雖然可將 df 直接傳入 mpf.lot() 繪製 K 線圖, 但 mplfinance 預設是繪製美式 K 線, 若要繪製台灣股市的 K 線圖須先用 make_marketcolors() 與 make_mpf_style() 函式自訂樣式字典 :
>>> color=mpf.make_marketcolors(up='red', down='green', inherit=True)
>>> font={'font.family': 'Microsoft JhengHei'}
>>> style=mpf.make_mpf_style(base_mpf_style='default', marketcolors=color, rc=font)
然後在呼叫 plot() 時將樣式字典傳給 style 參數 :
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style)
如果傳入 volume-True 則會在底下繪製成交量 :
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, volume=True)
事實上, volume=True 是啟動 K 線圖內建的 panel=1 的成交量副圖.
以上帶量的 K 線圖完整程式碼如下 :
# mplfinance_candle_plot_1.py
import yfinance as yf
import pandas as pd
import mplfinance as mpf
df=yf.download('0050.tw', start='2024-07-01', end='2024-08-21')
color=mpf.make_marketcolors(up='red', down='green', inherit=True)
font={'font.family': 'Microsoft JhengHei'}
style=mpf.make_mpf_style(base_mpf_style='default', marketcolors=color, rc=font)
mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, volume=True)
接下來要在 K 線圖上疊圖與添加副圖.
不論是疊圖或副圖, 都是利用 plot() 函式的 addplot 參數達成的, 此參數值為一個透過 make_addplot() 產生的圖形字典, 如果有多個疊圖或副圖, 則須將這些字典組成串列傳給 addplot 參數. make_addplot() 函式的常用參數如下 :
mpf.make_addplot(data [, color=None, type='line', panel=0')
參數說明 :
- data : 圖形資料, 可以是串列, ndarray 或 Series 物件, 長度需與 K 線資料相同
- color : 圖形顏色, 同 plot() 之 color 參數
- type : 圖形類型, 預設為 'line' 折線圖
- panel : 0=疊圖, 1~9=副圖
- width : 疊圖或副圖中線條寬度 (px, 預設=3)
- ylabel : 副圖的 Y 軸標籤
- ylim : 副圖的 Y 軸範圍 [ymin, ymax]
疊圖與副圖之差別是透過 make_addplot() 的 panel 參數來區分的, panel=0 為疊圖 (此為預設, 未指定 panel 預設為疊圖); panel=1~9 為副圖, 一張 K 線圖可以套上多個疊圖, 但最多只能添加 9 個副圖. 完整的 make_addplot() 參數參考 :
搜尋 'valid_addplot_kwargs' 即可找到全部關鍵字參數的說明.
2. 在 K 線圖上繪製疊圖 :
所謂疊圖是直接在 K 線圖上面套印其他圖形, 例如要在 0050 的 K 線圖上畫兩條水平線標示固定的買進與賣出價位, 繪製布林通道線, 或者是在 K 線圖上標示進出場策略等.
下面是在 0050 K 線圖中加上一條水平線的範例, 以上面的 DataFrame 為例, 先呼叫 make_addplot() 製作一個與 df 列數相同 (即 df,shape[0]), 價格為 170 的圖形字典 :
>>> addplot_1=mpf.make_addplot([170]*df.shape[0]) # shape[0] 是列數
>>> type(addplot_1)
<class 'dict'>
>>> addplot_1
{'data': [170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170], 'scatter': False, 'type': 'line', 'mav': None, 'panel': 0, 'marker': 'o', 'markersize': 18, 'color': None, 'linestyle': None, 'linewidths': None, 'edgecolors': None, 'width': None, 'bottom': 0, 'alpha': 1, 'secondary_y': 'auto', 'y_on_right': None, 'ylabel': None, 'ylim': None, 'title': None, 'ax': None, 'yscale': None, 'stepwhere': 'pre', 'marketcolors': None, 'fill_between': None, 'label': None}
可見此圖形字典除資料串列 data 外還有許多屬性, 常用的屬性如下 :
圖形字典屬性 | 說明 |
panel | 繪圖框編號 (0~9, 0 為疊圖, 1~9 為副圖, 但 1 為內建成交量副圖) |
color | 線條顏色 |
marker | 資料點標記符號, 例如 'o' 為圓點, 's' 為方點 |
ylabel | Y 軸標籤 (字串) |
ylim | Y 軸座標範圍 (串列) |
width | 線條寬度 (px) |
這些屬性可以直接在呼叫 make_addplot() 時直接傳進去, 也可以事後修改圖形字典. 其實這些屬性的名稱與用法都來自 Matplotlib, 參考 :
例如線條顏色 color, 預設 None 為黑色, 我們可以將其改為紅色 'red' :
>>> addplot_1['color']='red'
>>> addplot_1
{'data': [170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170], 'scatter': False, 'type': 'line', 'mav': None, 'panel': 0, 'marker': 'o', 'markersize': 18, 'color': 'red', 'linestyle': None, 'linewidths': None, 'edgecolors': None, 'width': None, 'bottom': 0, 'alpha': 1, 'secondary_y': 'auto', 'y_on_right': None, 'ylabel': None, 'ylim': None, 'title': None, 'ax': None, 'yscale': None, 'stepwhere': 'pre', 'marketcolors': None, 'fill_between': None, 'label': None}
然後於呼叫 plot() 函式時將圖形字典傳給 addplot 參數即可在 K 線圖上加上此疊圖 :
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, addplot=addplot_1)
結果如下 :
如果要疊多個圖, 則可將這些疊圖的圖形字典組成一個串列傳給 addplot 參數, 下列範例是在 K 線圖上繪製 price=170 與 price=190 兩條水平線 :
>>> addplot_1=mpf.make_addplot([170]*df.shape[0]) # shape[0] 是列數
>>> addplot_1['color']='red'
>>> addplot_2=mpf.make_addplot([190]*df.shape[0]) # shape[0] 是列數
>>> addplot_2['color']='green'
>>> addplot=[addplot_1, addplot_2] # 圖形字典串列
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, addplot=addplot)
此處將兩個圖形字典組成之串列傳給 addplot 參數, 結果如下 :
完整程式碼如下 :
# mplfinance_candle_plot_2.py
import yfinance as yf
import pandas as pd
import mplfinance as mpf
df=yf.download('0050.tw', start='2024-07-01', end='2024-08-21')
color=mpf.make_marketcolors(up='red', down='green', inherit=True)
font={'font.family': 'Microsoft JhengHei'}
style=mpf.make_mpf_style(base_mpf_style='default', marketcolors=color, rc=font)
addplot_1=mpf.make_addplot([170]*df.shape[0])
addplot_1['color']='red'
addplot_2=mpf.make_addplot([190]*df.shape[0])
addplot_2['color']='green'
addplot=[addplot_1, addplot_2]
mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, addplot=addplot)
事實上 K 線圖最常用的疊圖是均線圖, mplefinance 已經內建了移動均線的疊圖功能, 只要在呼叫 mpf.plot() 時傳入 mav 參數指定日數即可, 例如疊上一個 3 日移動均線 :
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, mav=3)
如果要疊上多個移動均線就傳入日期串列給 mav, 例如 :
這樣 3, 5, 7 日的移動均線就疊在 K 線圖上了.
3. 在 K 線圖上繪製副圖 :
副圖是在 K 線圖下方添加其他圖形, K 線圖本身已經內建一個成交量副圖 (顯示 df 中 Volume 欄位之成交量), 預設為不顯示, 但可以在呼叫 plot() 函式時傳入 volume=True 參數顯示此內建副圖. 副圖通常用來顯示技術指標之用, 在呼叫 make_addplot() 時必須傳入 panel=1~9 的參數值才會成為下方的副圖, 否則 panel 預設是 0 (疊圖).
下面以繪製動量指標中的 RSI (相對強弱指標, Relative Strength Index) 為例測試, 從盤後資料計算技術指標要用到第三方的 Ta-Lib 套件, 它無法透過 pip 安裝, 在 Windows 需下載與作業系統版本相關的 whl 檔進行安裝, 在 Linux 則需從原始碼編譯安裝, 方法參考 :
Ta-Lib 的教學文件參考 :
Ta-Lib 在 Python 中的套件名稱為 talib, 它有兩種用法, 一是函數式 (functional) 用法, talib 底下提供許多技術指標函式例如 RSI() 或 MACD() 等, 匯入方式例如 :
from talib import RSI, MACD
但呼叫要時需依個別指標不同傳入所需的 OHLCV 價量資料之 Numpy 陣列作為參數.
第二種用法為物件式用法, Ta-Lib 將各指標之類別例如 RSI 或 MACD 等放在 talib.abstract 模組下, 使用前要先匯入要用到的類別, 例如 :
from talib.abstract import RSI, MACD
使用時呼叫其建構式例如 RSI() 或 MACD() 並傳入含有 OHLCV 價量資料的 DataFrame, 建構式會自動從中取用所需之欄位進行計算, 結果會傳回一個 Series 物件. 不過, 物件式用法也可以像函數式用法那樣傳入陣列參數而非 DataFrame, 故其用法比函數式要有彈性.
但要注意的是, 使用物件式用法前必須先將 DataFrame 中的 OHLCV 價量欄位欄名都改成全小寫的 open, high, low, close, 與 volume. 由於 yfinance 下載的盤後資料, 其 OHLCV 欄名都是首字母大寫, 如果將它直接傳給建構式會出現錯誤 :
>>> from talib.abstract import RSI
>>> df=yf.download('0050.tw', start='2024-07-01', end='2024-08-21')
>>> RSI(df)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "talib/_abstract.pxi", line 444, in talib._ta_lib.Function.__call__
File "talib/_abstract.pxi", line 303, in talib._ta_lib.Function.set_function_args
File "talib/_abstract.pxi", line 251, in talib._ta_lib.Function.set_input_arrays
Exception: input_arrays parameter missing required data key: close
這是因為 talib 套件的技術指標建構式會依演算法去 DataFrame 中抓取 open, high, low, close, 與 volume 欄位之資料, 但卻找不到所致 (因欄名皆首字母大寫). 這可以利用串列生成式很簡單地將 DataFrame 的 OHLCV 欄名都改成小寫, 例如 :
>>> df.columns=[column.lower() for column in df.columns]
>>> df.columns
Index(['open', 'high', 'low', 'close', 'adj close', 'volume'], dtype='object')
這樣再次呼叫建構式就不會出錯了 :
>>> rsi=RSI(df)
>>> rsi
Date
2024-07-01 NaN
2024-07-02 NaN
2024-07-03 NaN
2024-07-04 NaN
2024-07-05 NaN
2024-07-08 NaN
2024-07-09 NaN
2024-07-10 NaN
2024-07-11 NaN
2024-07-12 NaN
2024-07-15 NaN
2024-07-16 NaN
2024-07-17 NaN
2024-07-18 NaN
2024-07-19 49.538859
2024-07-22 42.798346
2024-07-23 50.166346
2024-07-24 50.166346
2024-07-25 50.166346
2024-07-26 41.469555
2024-07-29 43.770438
2024-07-30 43.770438
2024-07-31 44.168163
2024-08-01 49.057524
2024-08-02 38.373022
2024-08-05 27.365055
2024-08-06 37.891139
2024-08-07 44.476722
2024-08-08 41.887384
2024-08-09 46.798076
2024-08-12 48.734619
2024-08-13 49.142400
2024-08-14 51.231230
2024-08-15 49.859024
2024-08-16 53.720763
2024-08-19 53.956501
2024-08-20 54.057317
dtype: float64
>>> type(rsi)
<class 'pandas.core.series.Series'>
這樣便可將傳回的 rsi 傳入 make_addplot() 作為 data, 指定 panel=1 繪製副圖了 :
>>> addplot_rsi=mpf.make_addplot(rsi, panel=1, ylabel='RSI')
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, addplot=addplot_rsi)
完整程式碼如下 :
# mplfinance_candle_plot_3.py
import yfinance as yf
import pandas as pd
import mplfinance as mpf
from talib.abstract import RSI
df=yf.download('0050.tw', start='2024-07-01', end='2024-08-21')
df.columns=[column.lower() for column in df.columns]
rsi=RSI(df)
addplot_rsi=mpf.make_addplot(rsi, panel=1, ylabel='RSI')
color=mpf.make_marketcolors(up='red', down='green', inherit=True)
font={'font.family': 'Microsoft JhengHei'}
style=mpf.make_mpf_style(base_mpf_style='default', marketcolors=color, rc=font)
mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, addplot=addplot_rsi)
注意, panel=1 是 K 線圖內建成交量副圖固定使用的繪圖框, 如果同時也開啟成交量副圖的話 (即傳入 volume=True 參數), 則上面同樣設為 panel=1 的 RSI 副圖會被成交量副圖覆蓋 :
>>> addplot_rsi=mpf.make_addplot(rsi, panel=1, ylabel='RSI')
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, volume=True, addplot=addplot_rsi)
結果如下 :
可見只顯示成交量副圖, 共用 panel=1 的 RSI 副圖被覆蓋掉了. 因此要同時顯示成交量副圖與自訂副圖時, 自訂副圖最好是從 panel=2 開始用, 例如將上面的 RSI 副圖改為 panel=2 :
>>> addplot_rsi=mpf.make_addplot(rsi, panel=2, ylabel='RSI')
>>> mpf.plot(df, type='candle', title='台灣五十(0050)', style=style, volume=True, addplot=addplot_rsi)
結果如下 :
這樣就會顯示兩個副圖了.
沒有留言:
張貼留言