2024年8月22日 星期四

Python 學習筆記 : 用 mplfinance 套件繪製金融圖表 (二) 疊圖與副圖

在上一篇文章中已經充分測試了用 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)  

結果如下 : 



這樣就會顯示兩個副圖了. 

沒有留言 :