2025年11月16日 星期日

Python 學習筆記 : 技術分析套件 pandas-ta 用法 (三)

本篇旨在測試如何用 pandas-ta 套件計算技術指標 (以 RSI 為例) 並繪製 K 線圖. 

本系列全部文章索引參考 :



5. 技術指標函式用法 :  

pandas-ta 技術指標函式有兩種呼叫方式 :
  • 直接呼叫指標函式 : 例如 ta.rsi()
  • 透過 DataFrame 的擴展屬性 ta 呼叫 : 例如 df.ta.rsi() 
這兩種方式的傳入參數, 傳回值, 與適用情境不同, 摘要如下表 : 


呼叫方式 說明
df.ta.xxx() 傳入參數 :
共通參數 + 指標特有參數, 未傳入 OHLCV 時會自動抓 DataFrame 欄位
回傳值 :
Series (單欄輸出) 或 DataFrame (多欄輸出) 
計算結果會自動加回原 DataFrame 欄位 (傳入 append=True 時, 此為預設)
適用情境 :
直接對 DataFrame 計算指標, 適合指標批量計算 
ta.xxx() 傳入參數 :
計算指標所需之 OHLCV 價量資料欄位 (Series) + 指標特有參數
回傳值 :
Series (單欄輸出) 或 DataFrame (多欄輸出) 
適用情境 :
直接呼叫函式計算指標值, 傳回 Series (單欄輸出) 或 DataFrame (多欄輸出), 適合單一 Series 或自定義策略計算, 靈活度高


當我們用 import pandas_ta as ta 匯入 pandas-ta 套件時, 它會在背後自動對 pandas.DataFrame 類別做 monkey patch (動態修改/擴充/覆寫現有模組/類別/物件的作法, 毋須修改 Pandas 原始碼), 具體而言是在 DataFrame 類別上添加一個 ta 屬性 (稱為擴展屬性), 這個 ta 屬性的值是一個封裝了所有指標函式的介面物件 (自訂的 Accessor), 它內部會把呼叫 xxx() 方法映射到 pandas-ta 的 xxx() 指標函式, 因此任何現有或新建的 DataFrame 都可以直接用 df.ta.xxx() 來呼叫這些指標函式. 這種作法常用於在不更改套件原始碼情況下擴充功能或修補 bug.

使用 df.ta.xxx() 計算指標時, pandas-ta 會自動從 DataFrame 中選擇計算該指標所需的欄位, 毋須明確傳入 close, high, low, close, volumn 等價量參數來指定欄位來源, pandas-ta 會自動在 DataFrame 裡搜尋各種可識別的欄名 (內部使用正規式模糊比對, 大小寫不敏感), 下列是可自動識別的 OHLCV 欄名 :
  • "open", "high", "low", "close", "volume"
  • "Open", "High", "Low", "Close", "Volume"
  • "O", "H", "L", "C", "V"
  • "o", "h", "l", "c", "v"
  • "o", "h", "lo", "cl", "vol"
如果欄位名稱相似, 例如有 Adj Close 與 Close 兩個欄位, 則 pandas-ta 內部有一個優先順序表, 第一個欄位會被拿來做計算 :


 類別  欄位名稱優先順序
Open "open", "Open"
High "high", "High"
Low "low", "Low"
Close "close", "Close", "adj_close", "Adj Close"
Volume "volume", "Volume", "vol", "Vol"


最保險的做法是用 open, high, low, close, volume 參數明確地指定 DataFrame 中的欄位名稱, 特別是欄位名稱是中文時, 例如 df.ta.rsi(close='收盤價'). 當 df 中同時有 Adj Close 與 Close 欄位時, df.ta.rsi(close='Adj Close') 會用還原權值收盤價計算 RSI; 而 df.ta.rsi(close='Close') 則會用未還原權值收盤價計算 RSI. 

透過擴展屬性 ta 呼叫指標方法 df.ta.xxx() 時, 其傳入參數還有如下表之共通參數 (所有技術指標都有) 可傳入, 主要是用來指定計算結果與 df 欄位的處理方式 : 


 共通參數  說明
append 是否將產生的技術指標欄位加入原 DataFrame(預設 False)。
correction 是否修正輸出欄位名稱中的重複名稱、特殊字元等(有些指標預設為 True)。
asobject True 時回傳 Indicator 類型物件;False(預設)時回傳 DataFrame。
offset 指標輸出向後或向前平移(shift),通常用於策略排程或對齊用途。
fillna 是否以 0 填補缺失值(NaN)。預設 False。
fill_method pandas fillna 的填補方法,例如 'ffill'、'bfill'。
mamode 若指標需使用移動平均,可指定類型(sma、ema、wma 等)。部分指標才適用。
suffix 自訂輸出欄位名稱後綴(如 RSI_14 → RSI_14_CUSTOM)。
prepend 是否在欄位前加入前綴(某些版本支援)。通常用於避免欄位衝突。
_trend 部分趨勢類指標會使用的內部參數,用於指定趨勢計算模式。


注意, 共通參數 append 在新版 padas-ta 中預設值已經改為 False, 呼叫 df.ta.xxx() 時必須傳入 append=True 才會將結果自動加入 DataFrame 的新增欄位裡

使用 ta.xxx() 直接呼叫技術指標函式則無共通參數, 其傳入參數為計算指標所需之 OHCLV 欄位 (Series), 以及各指標特定之參數, 例如 :

ta.rsi(df['close'], length=14) 

其中第一參數 df['close'] 為計算 RSI 所需要的收盤價欄位 (Series, 必要參數); 第二參數 length 為 RSI 的特定參數 (可有可無之關鍵字參數). 

一個技術指標有哪些參數可傳入可以呼叫 help() 並傳入 ta.xxx 查看, 例如 ta.rsi() :

>>> help(ta.rsi)  
Help on function rsi in module pandas_ta.momentum.rsi:

rsi(close, length=None, scalar=None, talib=None, drift=None, offset=None, **kwargs)
    Relative Strength Index (RSI)
    
    The Relative Strength Index is popular momentum oscillator used to measure the
    velocity as well as the magnitude of directional price movements.
    
    Sources:
        https://www.tradingview.com/wiki/Relative_Strength_Index_(RSI)
    
    Calculation:
        Default Inputs:
            length=14, scalar=100, drift=1
        ABS = Absolute Value
        RMA = Rolling Moving Average
    
        diff = close.diff(drift)
        positive = diff if diff > 0 else 0
        negative = diff if diff < 0 else 0
    
        pos_avg = RMA(positive, length)
        neg_avg = ABS(RMA(negative, length))
    
        RSI = scalar * pos_avg / (pos_avg + neg_avg)
    
    Args:
        close (pd.Series): Series of 'close's
        length (int): It's period. Default: 14
        scalar (float): How much to magnify. Default: 100
        talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib
            version. Default: True
        drift (int): The difference period. Default: 1
        offset (int): How many periods to offset the result. Default: 0
    
    Kwargs:
        fillna (value, optional): pd.DataFrame.fillna(value)
        fill_method (value, optional): Type of fill method
    
    Returns:
        pd.Series: New feature generated.


6. 計算 RSI 指標並於 K 線圖中繪製其曲線 :  

下面測試以實際從 yfinance 取得的股票資料用 pandas-ta 來計算 RSI 指標

首先匯入 yfinance 與 pandas-ta 套件 : 

>>> import yfinance as yf     
>>> import pandas_ta as ta   

下載 yfinance 股票價量資料 : 

>>> df=yf.download('0050.TW', start='2024-11-06', end='2025-01-09', auto_adjust=False)   
[*********************100%***********************]  1 of 1 completed

注意, 新版 yfinance API 預設傳回已還原權值收盤價的多層欄位 DataFrame (auto_adjust 預設 True),  df['Close'] 為已還原權值收盤價, 如果要同時取得已還原與未還原權值收盤價, auto_adjust 需設為 False (這樣會有 Close 與 Adj Close 兩個欄位). 

由於多層欄位 DataFrame 無法直接傳給 mplfinance 繪製 K 線圖, 故用下列指令將 DataFrame 改為傳統的單層欄位 : 

>>> df.columns=df.columns.map(lambda x: x[0])   

顯示前 10 筆資料 :

>>> df.head()   
            Adj Close      Close       High        Low       Open     Volume
Date                                                                        
2024-11-06  47.797909  48.799999  49.512501  48.375000  48.512501   66825504
2024-11-07  48.348858  49.362499  49.512501  48.625000  48.724998   53994208
2024-11-08  48.728401  49.750000  50.000000  49.674999  49.849998   51800604
2024-11-11  48.752892  49.775002  49.775002  49.200001  49.712502   49456052
2024-11-12  47.504074  48.500000  48.962502  48.500000  48.799999  105088348

首先直接呼叫 pandas-ta 的技術指標函式 ta.rsi(), 其必要參數為收盤價, 另傳入特定參數 length 來指定週期, 傳回值為 RSI 指標值 (Series), 存入 DataFrame 新增欄位 RSI 中 : 

>>> df['RSI']=ta.rsi(df['Close'], length=14)   

檢視前 20 筆, 因 length=14, 故要第 15 筆紀錄才會有 RSI 值 :

>>> df.head(20)  
            Adj Close      Close       High  ...       Open     Volume        RSI
Date                                         ...                                 
2024-11-06  47.797909  48.799999  49.512501  ...  48.512501   66825504        NaN
2024-11-07  48.348858  49.362499  49.512501  ...  48.724998   53994208        NaN
2024-11-08  48.728401  49.750000  50.000000  ...  49.849998   51800604        NaN
2024-11-11  48.752892  49.775002  49.775002  ...  49.712502   49456052        NaN
2024-11-12  47.504074  48.500000  48.962502  ...  48.799999  105088348        NaN
2024-11-13  47.197987  48.187500  48.500000  ...  48.462502   36778360        NaN
2024-11-14  46.769474  47.750000  48.125000  ...  48.075001   48586460        NaN
2024-11-15  47.100044  48.087502  48.212502  ...  48.012501   29542324        NaN
2024-11-18  46.438900  47.412498  47.924999  ...  47.862499   53578064        NaN
2024-11-19  47.075554  48.062500  48.237499  ...  47.674999   30473284        NaN
2024-11-20  46.879662  47.862499  47.862499  ...  47.862499     312000        NaN
2024-11-21  46.365444  47.337502  47.537498  ...  47.500000   63506468        NaN
2024-11-22  47.271446  48.262501  48.299999  ...  47.775002   31862348        NaN
2024-11-25  47.100044  48.087502  48.687500  ...  48.674999   31306652        NaN
2024-11-26  46.487877  47.462502  47.575001  ...  47.525002   45563288  40.597565
2024-11-27  45.814495  46.775002  47.362499  ...  47.287498   56290744  36.769954
2024-11-28  45.741032  46.700001  46.775002  ...  46.750000   52436456  36.367131
2024-11-29  45.851223  46.812500  47.000000  ...  46.237499   32512880  37.473635
2024-12-02  46.940880  47.924999  47.924999  ...  47.125000   42176312  47.243384
2024-12-03  47.577534  48.575001  48.712502  ...  48.575001   39812424  51.965859

[20 rows x 7 columns]

接下來用 kbar 套件來繪製 K 線圖, 此套件依賴 mplfinance 來繪製 K 線圖, 但介面更簡潔, 可用 pip install kbar 安裝, 參考 :


匯入 kbar.KBar 類別, 呼叫其建構式並傳入 DataFrame 建立 KBar 物件 : 

>>> from kbar import KBar    
>>> kb=KBar(df)   
設定字型為: Microsoft JhengHei

呼叫 KBar 物件的 addplot() 方法新增副圖繪製 RSI 曲線 (副圖要從 panel 2 起算) : 

>>> kb.addplot(df['RSI'], panel=2, ylabel='RSI')  

呼叫 KBar 物件的 plot() 方法繪製 K 線圖 (顯示成交量副圖與 5 日均線疊圖) :

>>> kb.plot(volume=True, mav=5)    
使用指定字型: Microsoft JhengHei
字型候選清單: ['Microsoft JhengHei', 'DejaVu Sans', 'Arial']

結果如下 : 




其次, 使用 df.ta.xxx() 來計算 RSI 指標, 重新下載股票資料 :

>>> df=yf.download('0050.TW', start='2024-11-06', end='2025-01-09', auto_adjust=False)    
[*********************100%***********************]  1 of 1 completed

將 DataFrame 改為傳統的單層欄位 : 

>>> df.columns=df.columns.map(lambda x: x[0])   

顯示前 10 筆資料 (與上面一樣) :

>>> df.head()   
            Adj Close      Close       High        Low       Open     Volume
Date                                                                        
2024-11-06  47.797909  48.799999  49.512501  48.375000  48.512501   66825504
2024-11-07  48.348858  49.362499  49.512501  48.625000  48.724998   53994208
2024-11-08  48.728401  49.750000  50.000000  49.674999  49.849998   51800604
2024-11-11  48.752892  49.775002  49.775002  49.200001  49.712502   49456052
2024-11-12  47.504074  48.500000  48.962502  48.500000  48.799999  105088348

呼叫 df.ta.rsi() 方法指定以 Close 欄位計算 RSI 指標, 結果為單欄故傳回 Series : 

>>> df.ta.rsi(length=14, close='Close', append=True)  
Date
2024-11-06          NaN
2024-11-07          NaN
2024-11-08          NaN
2024-11-11          NaN
2024-11-12          NaN
2024-11-13          NaN
2024-11-14          NaN
2024-11-15          NaN
2024-11-18          NaN
2024-11-19          NaN
2024-11-20          NaN
2024-11-21          NaN
2024-11-22          NaN
2024-11-25          NaN
2024-11-26    40.597565
2024-11-27    36.769954
2024-11-28    36.367131
2024-11-29    37.473635
2024-12-02    47.243384
2024-12-03    51.965859
2024-12-04    53.930191
2024-12-05    55.711091
2024-12-06    54.102319
2024-12-09    54.573219
2024-12-10    51.393309
2024-12-11    47.754493
2024-12-12    52.543689
2024-12-13    52.853987
2024-12-16    54.560375
2024-12-17    56.671434
2024-12-18    56.671434
2024-12-19    49.474000
2024-12-20    45.339886
2024-12-23    55.650059
2024-12-24    56.690637
2024-12-25    57.852403
2024-12-26    57.437595
2024-12-27    58.815685
2024-12-30    55.538436
2024-12-31    49.952179
2025-01-02    45.834974
2025-01-03    50.840252
2025-01-06    62.583698
2025-01-07    65.802355
2025-01-08    58.460939
Name: RSI_14, dtype: float64

因為 df.ta.xxx() 的共通參數 append 設為 True, 所以計算結果除了傳回來外, 還會加入 df 中 (欄名 pandas-ta 會自動決定) : 

>>> df.head(20)   
                Close       High        Low       Open     Volume     RSI_14   
Date                                                                        
2024-11-06  47.797909  48.495779  47.381637  47.516314   66825504        NaN
2024-11-07  48.348858  48.495779  47.626503  47.724448   53994208        NaN
2024-11-08  48.728401  48.973268  48.654941  48.826346   51800604        NaN
2024-11-11  48.752892  48.752892  48.189698  48.691675   49456052        NaN
2024-11-12  47.504074  47.957078  47.504074  47.797913  105088348        NaN
2024-11-13  47.197987  47.504070  47.136770  47.467341   36778360        NaN
2024-11-14  46.769474  47.136774  46.549096  47.087801   48586460        NaN
2024-11-15  47.100044  47.222477  46.732745  47.026584   29542324        NaN
2024-11-18  46.438900  46.940877  46.426660  46.879660   53578064        NaN
2024-11-19  47.075554  47.246960  46.573577  46.696010   30473284        NaN
2024-11-20  46.879662  46.879662  46.879662  46.879662     312000        NaN
2024-11-21  46.365444  46.561334  46.181794  46.524606   63506468        NaN
2024-11-22  47.271446  47.308175  46.793958  46.793958   31862348        NaN
2024-11-25  47.100044  47.687722  47.075556  47.675478   31306652        NaN
2024-11-26  46.487877  46.598066  46.230766  46.549093   45563288  40.597564
2024-11-27  45.814495  46.389929  45.790007  46.316468   56290744  36.769955
2024-11-28  45.741032  45.814492  45.410461  45.790004   52436456  36.367116
2024-11-29  45.851223  46.034873  45.177841  45.288030   32512880  37.473643
2024-12-02  46.940880  46.940880  46.157308  46.157308   42176312  47.243408
2024-12-03  47.577534  47.712211  47.442856  47.577534   39812424  51.965878

可見計算結果是放在新增欄位 RSI_14 中. 

用 kbar 模組繪製 K 線圖 : 

>>> from kbar import KBar    
>>> kb=KBar(df)   
設定字型為: Microsoft JhengHei

呼叫 KBar 物件的 addplot() 方法新增副圖繪製 RSI 曲線 (副圖要從 panel 2 起算),注意, 欄位名稱是 RSI_14 : 

>>> kb.addplot(df['RSI_14'], panel=2, ylabel='RSI')  

呼叫 KBar 物件的 plot() 方法繪製 K 線圖 (顯示成交量副圖與 5 日均線疊圖) :

>>> kb.plot(volume=True, mav=5)    
使用指定字型: Microsoft JhengHei
字型候選清單: ['Microsoft JhengHei', 'DejaVu Sans', 'Arial']

結果與上面用 ta.rsi() 計算結果繪製的相同 : 



沒有留言 :