2025年1月18日 星期六

博客來買書一本 : 讓 AI 好好說話!從頭打造 LLM (大型語言模型) 實戰秘笈

晚上滑臉書看到陳南宗推薦的 LLM 好書 : "讓 AI 好好說話!從頭打造 LLM (大型語言模型) 實戰秘笈", 此書是 Sebastian Raschka 寫的 Build a Large Language Model (From Scratch) 繁中翻譯本 (旗標出版) :


去年英文版面世時陳大師便大力推薦, 現在有中文版當然甚好. 由於博客來每年元旦都會發送壽星生日折價券 50 元, 不就正好把它用掉嗎? 




小七至興店取貨付款 487 元 (71 折). 

Python 學習筆記 : 技術指標套件 ta 用法 (一)

今天在重讀 "Python 金融市場大賺錢聖經" 這本書 (我的是第一版, 目前已出至第三版) 第三章時看到作者介紹 Ta-Lib 之外的另一個技術分析套件 ta, 它最棒的一個特色是提供了一些可一次計算出全部指標的快捷函式, 例如呼叫 add_all_ta_features() 即可一次計算出所有它支援的 80 種技術指標, 非常好用.

關於 ta 的發展與特性摘要說明如下 :
  • 最早是由巴西開發者 David Cañadas Gonzalo 所創建, 於 2018 年以 MIT 授權開放原始碼. 
  • 底層基於 Numpy 與 Pandas, 主要用來進行金融時間序列的高效運算. 
  • 最新版本支援 80 種以上股票與外匯市場技術指標, 涵蓋趨勢, 動能, 與波動性. 
 教學文件參考 : 


關於 Ta-lib 套件參考 :



一. 安裝 ta 套件 : 

與 Ta-Lib 安裝方式不同 (在 Windows 下需要用 whl 檔安裝; 在 Linux 下要自行從原始碼編譯), ta 套件可直接用 pip 指令安裝, 先用 pip show 檢查是否有安裝過此套件 :

D:\python\test>pip show ta   
Name: ta
Version: 0.5.25
Summary: Technical Analysis Library in Python
Home-page: https://github.com/bukosabino/ta
Author: Dario Lopez Padial (Bukosabino)
Author-email: Bukosabino@gmail.com
License: The MIT License (MIT)
Location: c:\users\tony1\appdata\local\programs\thonny\lib\site-packages
Requires: numpy, pandas
Required-by: FinMind   

原來之前安裝 FinMind 時就已經因為相依套件原因被安裝了, 版本是 0.5.25, 所以用 -U 更新到最新版本 (不知會不會影響 FinMind, 後面再測試看看) :

D:\python\test>pip install ta -U    
Requirement already satisfied: ta in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (0.5.25)
Collecting ta
  Downloading ta-0.11.0.tar.gz (25 kB)
  Preparing metadata (setup.py) ... done
Requirement already satisfied: numpy in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ta) (1.24.3)
Requirement already satisfied: pandas in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ta) (2.0.3)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas->ta) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas->ta) (2023.3)
Requirement already satisfied: tzdata>=2022.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas->ta) (2023.3)
Requirement already satisfied: six>=1.5 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from python-dateutil>=2.8.2->pandas->ta) (1.16.0)
Building wheels for collected packages: ta
  Building wheel for ta (setup.py) ... done
  Created wheel for ta: filename=ta-0.11.0-py3-none-any.whl size=29422 sha256=058b68e1473a19f6c12c845769f870b008f4c7d3970a7989ebfe7636970457cd
  Stored in directory: c:\users\tony1\appdata\local\pip\cache\wheels\5f\67\4f\8a9f252836e053e532c6587a3230bc72a4deb16b03a829610b
Successfully built ta
Installing collected packages: ta
  Attempting uninstall: ta
    Found existing installation: ta 0.5.25
    Uninstalling ta-0.5.25:
      Successfully uninstalled ta-0.5.25
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
finmind 1.6.9 requires ta~=0.5.25, but you have ta 0.11.0 which is incompatible.
Successfully installed ta-0.11.0

用 dir() 檢視套件內容 :

>>> import ta 
>>> dir(ta)  
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'add_all_ta_features', 'add_momentum_ta', 'add_others_ta', 'add_trend_ta', 'add_volatility_ta', 'add_volume_ta', 'momentum', 'others', 'trend', 'utils', 'volatility', 'volume', 'wrapper']


>>> from members import list_members    
>>> list_members(ta)    
add_all_ta_features <class 'function'>
add_momentum_ta <class 'function'>
add_others_ta <class 'function'>
add_trend_ta <class 'function'>
add_volatility_ta <class 'function'>
add_volume_ta <class 'function'>
momentum <class 'module'>
others <class 'module'>
trend <class 'module'>
utils <class 'module'>
volatility <class 'module'>
volume <class 'module'>
wrapper <class 'module'>

ta 套件內的模組整理如下表 : 


 ta 的模組 說明
 momentum 包含所有動能類指標 (例如 RSI, MACD, KD 等) 的模組
 others 包含其他類技術指標 (例如每日報酬率與波動率等) 的模組
 trend 包含趨勢類技術指標 (例如 SMA, EMA, ADX, CCI 等) 的模組
 utils 提供實用工具 (例如處理空值, 標準化數據等) 的模組
 volatility  包含波動性類指標 (例如布林通道, ATR 等) 的模組
 volume 包含成交量類指標 (例如 OBV, ADI, Chaikin 等) 的模組
 wrapper 提供封裝的類別與函式, 用於更高層級的應用


ta 目前支援總共 80 種技術指標, 區分為如下五種類型 :
ta 支援函數式 (functional) 與物件導向式 (object-oriented) 兩種 API 介面, 因此將這五種類型的指標分別實作為函式與類別, 分別收納於 trend, voatility, volume, momentum, 與 others 模組中. 可以用 dir() 或上面使用的自訂模組 members 來檢視這些模組內收容了哪些指標 (其中全小寫的是指標函式, 首字母大寫的是指標類別) : 

趨勢指標模組 :

>>> dir(ta.trend)    
['ADXIndicator', 'AroonIndicator', 'CCIIndicator', 'DPOIndicator', 'EMAIndicator', 'IchimokuIndicator', 'IndicatorMixin', 'KSTIndicator', 'MACD', 'MassIndex', 'PSARIndicator', 'SMAIndicator', 'STCIndicator', 'TRIXIndicator', 'VortexIndicator', 'WMAIndicator', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_ema', '_get_min_max', '_sma', 'adx', 'adx_neg', 'adx_pos', 'aroon_down', 'aroon_up', 'cci', 'dpo', 'ema_indicator', 'ichimoku_a', 'ichimoku_b', 'ichimoku_base_line', 'ichimoku_conversion_line', 'kst', 'kst_sig', 'macd', 'macd_diff', 'macd_signal', 'mass_index', 'np', 'pd', 'psar_down', 'psar_down_indicator', 'psar_up', 'psar_up_indicator', 'sma_indicator', 'stc', 'trix', 'vortex_indicator_neg', 'vortex_indicator_pos', 'wma_indicator']

動量指標模組 : 

>>> dir(ta.momentum)    
['AwesomeOscillatorIndicator', 'IndicatorMixin', 'KAMAIndicator', 'PercentagePriceOscillator', 'PercentageVolumeOscillator', 'ROCIndicator', 'RSIIndicator', 'StochRSIIndicator', 'StochasticOscillator', 'TSIIndicator', 'UltimateOscillator', 'WilliamsRIndicator', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_ema', 'awesome_oscillator', 'kama', 'np', 'pd', 'ppo', 'ppo_hist', 'ppo_signal', 'pvo', 'pvo_hist', 'pvo_signal', 'roc', 'rsi', 'stoch', 'stoch_signal', 'stochrsi', 'stochrsi_d', 'stochrsi_k', 'tsi', 'ultimate_oscillator', 'williams_r']

量能指標模組 : 

>>> dir(ta.volume)   
['AccDistIndexIndicator', 'ChaikinMoneyFlowIndicator', 'EaseOfMovementIndicator', 'ForceIndexIndicator', 'IndicatorMixin', 'MFIIndicator', 'NegativeVolumeIndexIndicator', 'OnBalanceVolumeIndicator', 'VolumePriceTrendIndicator', 'VolumeWeightedAveragePrice', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_ema', 'acc_dist_index', 'chaikin_money_flow', 'ease_of_movement', 'force_index', 'money_flow_index', 'negative_volume_index', 'np', 'on_balance_volume', 'pd', 'sma_ease_of_movement', 'tp', 'volume_price_trend', 'volume_weighted_average_price']

波動指標模組 : 

>>> dir(ta.volatility)    
['AverageTrueRange', 'BollingerBands', 'DonchianChannel', 'IndicatorMixin', 'KeltnerChannel', 'UlcerIndex', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'average_true_range', 'bollinger_hband', 'bollinger_hband_indicator', 'bollinger_lband', 'bollinger_lband_indicator', 'bollinger_mavg', 'bollinger_pband', 'bollinger_wband', 'donchian_channel_hband', 'donchian_channel_lband', 'donchian_channel_mband', 'donchian_channel_pband', 'donchian_channel_wband', 'keltner_channel_hband', 'keltner_channel_hband_indicator', 'keltner_channel_lband', 'keltner_channel_lband_indicator', 'keltner_channel_mband', 'keltner_channel_pband', 'keltner_channel_wband', 'np', 'pd', 'ulcer_index']

其他指標模組 : 

>>> dir(ta.others)   
['CumulativeReturnIndicator', 'DailyLogReturnIndicator', 'DailyReturnIndicator', 'IndicatorMixin', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'cumulative_return', 'daily_log_return', 'daily_return', 'np', 'pd']

others 模組的函式嚴格來說不算是技術指標, 比較像是功能函式, 其中 daily_return() 與 daily_log_return() 分別用來計算日報酬率與對數日報酬率; 而 cumulative_return() 則用來計算累積報酬率, 計算結果都會在 DataFrame 中添加一欄來儲存. 

另外, 工具模組 utils 提供了一個很常用 dropna() 函式可用來去除 DataFrame 中的 NaN : 

>>> dir(ta.utils)  
['IndicatorMixin', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_ema', '_get_min_max', '_sma', 'dropna', 'math', 'np', 'pd']

使用方法例如 : 

from ta.utils import dropna   
dropna(df)     

另外, ta 套件還提供可一次產生全部指標的快捷函式, 如下表所示 :


 ta 的快捷函式 說明
 add_all_ta_features()  計算所有支援的技術指標後添加到 DataFrame 中. 傳入參數 : 
 df (必要之位置參數): 不含 NaN 值的價量 DatFrame
 open, high, low, close, volume (關鍵字參數) : OHLCV 價量欄位名稱
 fillna (關鍵字參數) : 是否以預設值填充 NaN, True/False (預設)
 傳回值 : 添加技術指標欄位值後的 DataFrame
 add_momentum_ta() 添加動能類技術指標 (例如 RSI, MACD 等) 到 DataFrame. 傳入參數 : 
 df (必要之位置參數): 不含 NaN 值的價量 DatFrame
 close, volume (關鍵字參數) : 收盤價, 成交量欄位名稱
 fillna (關鍵字參數) : 是否以預設值填充 NaN, True/False (預設)
 傳回值 : 添加動能類技術指標欄位值後的 DataFrame
 add_others_ta() 添加其他類技術指標 (例如每日報酬率與波動率等) 到 DataFrame. 
 傳入參數 : 
 df (必要之位置參數): 不含 NaN 值的價量 DatFrame
 close (關鍵字參數) : 開盤價欄位名稱
 fillna (關鍵字參數) : 是否以預設值填充 NaN, True/False (預設)
 傳回值 : 添加其他類技術指標欄位值後的 DataFrame
 add_trend_ta() 添加趨勢類技術指標 (例如移動平均線, ADX) 到 DataFrame. 傳入參數 :
 df (必要之位置參數): 不含 NaN 值的價量 DatFrame
 high, low, close (關鍵字參數) : 最高價, 最低價, 收盤價之欄位名稱
 fillna (關鍵字參數) : 是否以預設值填充 NaN, True/False (預設)
 傳回值 : 添加趨勢類技術指標欄位值後的 DataFrame
 add_volatility_ta() 添加波動類技術指標 (例如布林通道) 到 DataFrame. 傳入參數 :
 df (必要之位置參數): 不含 NaN 值的價量 DatFrame
 high, low, close (關鍵字參數) : 最高價, 最低價, 收盤價之欄位名稱
 fillna (關鍵字參數) : 是否以預設值填充 NaN, True/False (預設)
 傳回值 : 添加動能類技術指標欄位值後的 DataFrame
 add_volume_ta() 添加量能類技術指標 (例如 OBV) 到 DataFrame. 傳入參數 :
 high, low, close, volume (關鍵字參數) : OHLCV 價量欄位名稱
 fillna (關鍵字參數) : 是否以預設值填充 NaN, True/False (預設)
 傳回值 : 添加技術指標欄位值後的 DataFrame


這些函式都是快捷函式, 其中 add_all_ta_features() 可一次計算全部 80 種指標並將結果添加到 DataFrame 的欄位中; 其他函式則是僅一次計算出某一類之所有指標並添加到 DataFrame 欄位中. 


二. 取得股票資料 : 

ta 套件支援直接傳入 Pandas 的 DataFrame 來計算指標, 可利用 yfinance (不需 API key) 或 FinMind 與 FinLab (需先註冊取得 API key) 取得包含 OHLCV 欄位的股票資料, 參考 : 


以下測試使用較方便的 yfinance. 

以 0050 ETF 為例從 yfinance 取得其盤後資料 :

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

檢視最後 5 筆資料 : 

>>> df.tail()     
                  Open        High  ...   Adj Close    Volume
Date                                ...                      
2024-12-27  197.600006  199.050003  ...  198.899994   6987417
2024-12-30  198.149994  198.649994  ...  197.800003   8677266
2024-12-31  196.699997  196.899994  ...  195.750000   7836534
2025-01-02  195.649994  195.649994  ...  194.050003  12797702
2025-01-03  196.000000  196.949997  ...  196.000000   7113371

[5 rows x 6 columns]

檢視欄位名稱 :

>>> df.columns    
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'], dtype='object')

ta 套件不會像 Ta-Lib 那樣要求欄位名稱必須小寫, 而是在計算指標時要指定所需之價量欄位名稱, 因此毋須對欄名做任何處理. 


三. ta 的兩種用法 : 

ta 支援函數式 (functional) 與物件導向式 (object-oriented) API, 由於 ta 與 Pandas 深度結合, 其 API 以物件導向式 API 為主. 不過它的函數式 API 提供了可以一次計算所有指標的六個快捷函式, 這是有別於 Ta-Lib 的一大特色. 


1. ta 的函數式 API :      

ta 的指標函式有下列三種 : 
  • 個別指標函式 : ta.xxx_yyy() (xxx 為類型名稱, yyy 為指標函式名稱)
  • 類型指標函式 : ta.add_xxx_ta() (xxx 為類型名稱, 計算該類型的所有指標)
  • 全部指標函式 : ta.add_all_ta_features() (計算全部 80 種指標)
後兩種為快捷函式 (無參數, 使用預設值計算指標), 可一次計算同類型所有指標或全部 80 個指標, 這些指標函式說明文件參考下列索引 :



(1). 個別指標函式 :  

首先來測試個別指標, 函式名稱格式為 ta.xxx_yyy() 函式, 其中 xxx 表示五大指標類型 trend, volatility, volume, momentum, 與 others, 而 yyy 則是指標函式名稱, 例如 :
 以移動平均指標 SMA 為例, 可從官網教學文件查得其 API 如下 :




可知 SMA 指標函式有三個參數 : close (必要), window, 與 fillna, 收盤價 close 資料型態為 Pandas 的 Series 物件, window 為移動窗口日數 (預設是 12 天均價), fillna 若社為 True 會自動刪除值為 NaN 之收盤價 : 

>>> sma12=ta.trend.sma_indicator(df['Close'])    
>>> sma12   
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    193.629167
2024-11-22    193.450001
2024-11-25    193.025002
2024-11-26    192.262502
2024-11-27    191.262502
2024-11-28    190.662502
2024-11-29    190.204169
2024-12-02    190.262502
2024-12-03    190.425002
2024-12-04    190.908335
2024-12-05    191.262502
2024-12-06    191.620836
2024-12-09    192.175002
2024-12-10    192.304169
2024-12-11    192.350001
2024-12-12    192.791667
2024-12-13    193.475000
2024-12-16    194.250000
2024-12-17    195.070834
2024-12-18    195.520835
2024-12-19    195.529167
2024-12-20    195.291668
2024-12-23    195.354168
2024-12-24    195.525002
2024-12-25    195.725001
2024-12-26    196.029167
2024-12-27    196.529167
2024-12-30    196.750001
2024-12-31    196.787501
2025-01-02    196.616667
2025-01-03    196.525000
2025-01-06    196.945833
2025-01-07    197.779166
2025-01-08    198.508333
Name: sma_12, dtype: float64

傳入 window 參數可指定移動窗口, 例如 5 日移動均線 : 

>>> sma5=ta.trend.sma_indicator(df['Close'], window=5)    
>>> sma5   
Date
2024-11-06           NaN
2024-11-07           NaN
2024-11-08           NaN
2024-11-11           NaN
2024-11-12    196.950000
2024-11-13    196.460001
2024-11-14    195.170001
2024-11-15    193.840002
2024-11-18    191.950000
2024-11-19    191.600000
2024-11-20    191.339999
2024-11-21    191.010001
2024-11-22    191.150000
2024-11-25    191.690002
2024-11-26    191.210004
2024-11-27    190.340005
2024-11-28    189.830005
2024-11-29    188.670004
2024-12-02    188.540002
2024-12-03    189.430002
2024-12-04    191.100000
2024-12-05    193.039999
2024-12-06    194.739999
2024-12-09    195.600000
2024-12-10    195.660001
2024-12-11    195.150000
2024-12-12    194.879999
2024-12-13    194.789999
2024-12-16    194.810001
2024-12-17    195.310001
2024-12-18    196.150003
2024-12-19    196.000003
2024-12-20    195.460004
2024-12-23    195.690002
2024-12-24    195.830002
2024-12-25    196.089999
2024-12-26    196.860001
2024-12-27    198.119998
2024-12-30    198.229999
2024-12-31    197.819998
2025-01-02    196.950000
2025-01-03    196.500000
2025-01-06    197.150000
2025-01-07    198.469998
2025-01-08    199.589999
Name: sma_5, dtype: float64

可見數值與用 Ta-Lib 的 talib.SMA() 計算出來的是一樣的, 但不同之處為 Ta-Lib 函式的傳回值為 ndarray 物件, 而 ta 函式傳回的是 Series 物件. 

OBV 指標函式 ta.volume.on_balance_volume() 的 API 如下所示 :




可見 OBV 指標的必要參數有兩個 : 收盤價 close 與成交量 volume, 資料型態均為 Pandas 的 Series 物件, 因為 OBV 是固定比較前後日之收盤價計算出來的, 所以沒有週期參數 : 

>>> obv=ta.volume.on_balance_volume(df['Close'], df['Volume'])      
>>> obv   
Date
2024-11-06    16706376
2024-11-07    30204928
2024-11-08    43155079
2024-11-11    55519092
2024-11-12    29247005
2024-11-13    20052415
2024-11-14     7905800
2024-11-15    15291381
2024-11-18     1896865
2024-11-19     9515186
2024-11-20     9437186
2024-11-21    -6439431
2024-11-22     1526156
2024-11-25    -6300507
2024-11-26   -17691329
2024-11-27   -31764015
2024-11-28   -44873129
2024-11-29   -36744909
2024-12-02   -26200831
2024-12-03   -16247725
2024-12-04    -9792476
2024-12-05    -2346273
2024-12-06    -9155779
2024-12-09    -4515915
2024-12-10    -8993222
2024-12-11   -15707911
2024-12-12   -10680670
2024-12-13    -6160243
2024-12-16     1183349
2024-12-17    10477578
2024-12-18    17031455
2024-12-19     3986408
2024-12-20    -7136986
2024-12-23     4039080
2024-12-24    12234982
2024-12-25    18141105
2024-12-26    13337544
2024-12-27    20324961
2024-12-30    11647695
2024-12-31     3811161
2025-01-02    -8986541
2025-01-03    -1873170
2025-01-06    22754623
2025-01-07    38698467
2025-01-08    26842750
Name: obv, dtype: int64

MFI 指標函式 ta.volume.money_flow_index() 的 API 如下 : 




可見 MFI 指標的必要參數有 4 個 : high, low, close, 與 volume, 資料型態均為 Pandas 的 Series 物件, 週期參數 window 預設為 14 日 (與 Ta-Lib 一樣) : 

>>> mfi=ta.volume.money_flow_index(df['High'], df['Low'], df['Close'], df['Volume'])   
>>> mfi    
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    39.301682
2024-11-26    36.522781
2024-11-27    27.843909
2024-11-28    19.482690
2024-11-29    25.247484
2024-12-02    35.842102
2024-12-03    42.918545
2024-12-04    49.655030
2024-12-05    49.740230
2024-12-06    52.229974
2024-12-09    51.119383
2024-12-10    49.334007
2024-12-11    53.017548
2024-12-12    51.856243
2024-12-13    46.397760
2024-12-16    54.858813
2024-12-17    66.148226
2024-12-18    70.160985
2024-12-19    59.032024
2024-12-20    48.676280
2024-12-23    49.330576
2024-12-24    50.220182
2024-12-25    43.808475
2024-12-26    49.368843
2024-12-27    50.541067
2024-12-30    48.551503
2024-12-31    48.016515
2025-01-02    40.670406
2025-01-03    45.672339
2025-01-06    52.673952
2025-01-07    55.040936
2025-01-08    52.999103
Name: mfi_14, dtype: float64

接下來可以利用 mplfinance 套件來繪製 K 線圖與上面的幾個指標, 我寫了一個基於 mplfinance 的 kbar.py 模組來簡化 K 線圖的繪製 :

# kbar.py
import mplfinance as mpf

class KBar():
    def __init__(self, df):
        self.df=df
        self.addplots=[]
    def addplot(self, data, **kwargs):
        plot=mpf.make_addplot(data, **kwargs)
        self.addplots.append(plot)
    def plot(self, embedding=False, **kwargs):
        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)
        kwargs['type']='candle'
        kwargs['style']=style
        kwargs['addplot']=self.addplots
        if embedding:
            fig, axes=mpf.plot(self.df, returnfig=True, **kwargs)
            return fig, axes
        else:
            mpf.plot(self.df, **kwargs)

匯入上面自訂的 kbar 模組, 呼叫 kbar.KBar() 建構式並傳入價量資料建立一個 KBar 物件, 然後把 SMA 指標畫在 K 線圖上疊圖, 把 OBV 與 MFI 畫在副圖 : 

>>> import kbar     
>>> kb=kbar.KBar(df)    
>>> kb.addplot(sma5, panel=0)    # 疊圖
>>> kb.addplot(obv, panel=1, ylabel='OBV')    # 副圖 1
>>> kb.addplot(mfi, panel=2, ylabel='MFI')     # 副圖 2
>>> kb.plot(title='台灣五十')   

結果如下 : 




(2). 趨勢指標的快捷函式 ta.add_trend_ta() :  

快捷函式中的類型指標函式格式為 ta.add_xxx_ta_(), 其中 xxx 表示五大指標類型 trend, volatility, volume, momentum, 與 others, 呼叫此種函式會一次計算出該類型的所有指標, 結果會儲存在 DataFrame 的新增指標欄位中. 

呼叫 ta.add_trend_ta() 會產生趨勢類型之指標數據, 其參數格式如下 : 

ta.add_trend_ta(
    df: pd.DataFrame,   # 價量資料 (必要)
    high: str,    # 最高價欄名 (必要)
    low: str,     # 最低價欄名 (必要)
    close: str,   # 收盤價欄名 (必要)
    fillna: bool = False,  # 是否在技術指標計算後自動填充缺失值
    colprefix: str = ""     # 在新增的技術指標攔名前添加前綴以避免欄名衝突
    ) 

此函式會以預設 window 參數值去計算所有趨勢類型之指標, 將結果添加到 DataFrame 中 (一個指標一個欄位) 後傳回 (可以用一個變數來接收傳回值, 但沒必要) : 

>>> ta.add_trend_ta(df, high='High', low='Low', close='Close')    

檢視傳回的 DataFrame 欄位 : 

>>> df.columns   
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'trend_macd',
       'trend_macd_signal', 'trend_macd_diff', 'trend_sma_fast',
       'trend_sma_slow', 'trend_ema_fast', 'trend_ema_slow',
       'trend_vortex_ind_pos', 'trend_vortex_ind_neg', 'trend_vortex_ind_diff',
       'trend_trix', 'trend_mass_index', 'trend_dpo', 'trend_kst',
       'trend_kst_sig', 'trend_kst_diff', 'trend_ichimoku_conv',
       'trend_ichimoku_base', 'trend_ichimoku_a', 'trend_ichimoku_b',
       'trend_stc', 'trend_adx', 'trend_adx_pos', 'trend_adx_neg', 'trend_cci',
       'trend_visual_ichimoku_a', 'trend_visual_ichimoku_b', 'trend_aroon_up',
       'trend_aroon_down', 'trend_aroon_ind', 'trend_psar_up',
       'trend_psar_down', 'trend_psar_up_indicator',
       'trend_psar_down_indicator'],
      dtype='object')

可見 df 除了原來的價量欄位外, 後面新增了所有支援之趨勢指標欄位 :  


 趨勢指標  df 指標欄位名稱
 MACD (Moving Average Convergence Divergence)  trend_macd,
 trend_macd_signal,
 trend_macd_diff
 SMA (Simple Moving Average)  trend_sma_fast,
 trend_sma_slow
 EMA (Exponential Moving Average)  trend_ema_fast,
 trend_ema_slow
 Vortex Indicator  trend_vortex_ind_pos,
 trend_vortex_ind_neg,
 trend_vortex_ind_diff
 TRIX (Triple Exponential Average)  trend_trix
 Mass Index  trend_mass_index
 DPO (Detrended Price Oscillator)  trend_dpo
 KST (Know Sure Thing)  trend_kst,
 trend_kst_sig,
 trend_kst_diff
 Ichimoku Cloud  trend_ichimoku_conv,
 trend_ichimoku_base,
 trend_ichimoku_a,
 trend_ichimoku_b
 STC (Schaff Trend Cycle)  trend_stc
 ADX (Average Directional Index)  trend_adx,
 trend_adx_pos,
 trend_adx_neg
 CCI (Commodity Channel Index)  trend_cci
 Visual Ichimoku  trend_visual_ichimoku_a,
 trend_visual_ichimoku_b
 Aroon Indicator  trend_aroon_up,
 trend_aroon_down, trend_aroon_ind
 Parabolic SAR  trend_psar_up,
 trend_psar_down,
 trend_psar_up_indicator, 
 trend_psar_down_indicator


檢視其中的 trend_sma_fast 欄位可知它就是預設 window=12 的 SMA 指標 :

>>> df['trend_sma_fast']    
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    193.629167
2024-11-22    193.450001
2024-11-25    193.025002
2024-11-26    192.262502
2024-11-27    191.262502
2024-11-28    190.662502
2024-11-29    190.204169
2024-12-02    190.262502
2024-12-03    190.425002
2024-12-04    190.908335
2024-12-05    191.262502
2024-12-06    191.620836
2024-12-09    192.175002
2024-12-10    192.304169
2024-12-11    192.350001
2024-12-12    192.791667
2024-12-13    193.475000
2024-12-16    194.250000
2024-12-17    195.070834
2024-12-18    195.520835
2024-12-19    195.529167
2024-12-20    195.291668
2024-12-23    195.354168
2024-12-24    195.525002
2024-12-25    195.725001
2024-12-26    196.029167
2024-12-27    196.529167
2024-12-30    196.750001
2024-12-31    196.787501
2025-01-02    196.616667
2025-01-03    196.525000
2025-01-06    196.945833
2025-01-07    197.779166
2025-01-08    198.508333
Name: trend_sma_fast, dtype: float64


(3). 動能指標的快捷函式 ta.add_momentum_ta() :  

呼叫 ta.add_momentum_ta() 來產生動能類型之指標數據, 其參數格式如下 : 

ta.add_momentum_ta(
    df: pd.DataFrame,   # 價量資料 (必要)
    high: str,    # 最高價欄名 (必要)
    low: str,     # 最低價欄名 (必要)
    close: str,   # 收盤價欄名 (必要)
    volume: str,   # 成交量 (必要)
    fillna: bool = False,  # 是否在技術指標計算後自動填充缺失值
    colprefix: str = ""     # 在新增的技術指標攔名前添加前綴以避免欄名衝突
    ) 

可見量能類型指標多了一個必要參數 volume, 此函式會以預設 window 參數值去計算所有動能類型之指標. 

為了清楚看出此函式新增了那些欄位, 重新下載價量資料讓 df 回復為原始欄位 :

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

ta.add_momentum_ta() 會將指標計算結果添加到 DataFrame 後傳回 (也可以用一個變數來接收傳回值, 但沒必要) : 

>>> ta.add_momentum_ta(df, high='High', low='Low', close='Close', volume='Volume')    

檢視 DataFrame 欄位 : 

>>> df.columns      
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'momentum_rsi',
       'momentum_stoch_rsi', 'momentum_stoch_rsi_k', 'momentum_stoch_rsi_d',
       'momentum_tsi', 'momentum_uo', 'momentum_stoch',
       'momentum_stoch_signal', 'momentum_wr', 'momentum_ao', 'momentum_roc',
       'momentum_ppo', 'momentum_ppo_signal', 'momentum_ppo_hist',
       'momentum_pvo', 'momentum_pvo_signal', 'momentum_pvo_hist',
       'momentum_kama'],
      dtype='object')

可見 df 除了原來的價量欄位外, 後面新增了所有支援之動能指標欄位 : 


 動能指標  df 指標欄位名稱
 RSI (Relative Strength Index)  momentum_rsi
 Stochastic RSI  momentum_stoch_rsi,
 momentum_stoch_rsi_k,
 momentum_stoch_rsi_d
 TSI (True Strength Index)  momentum_tsi
 UO (Ultimate Oscillator)  momentum_uo
 Stochastic Oscillator  momentum_stoch,
 momentum_stoch_signal
 Williams %R  momentum_wr
 AO (Awesome Oscillator)  momentum_ao
 ROC (Rate of Change)  momentum_roc
 PPO (Percentage Price Oscillator)  momentum_ppo,
 momentum_ppo_signal,
 momentum_ppo_hist
 PVO (Percentage Volume Oscillator)  momentum_pvo,
 momentum_pvo_signal,
 momentum_pvo_hist
 KAMA (Kaufman’s Adaptive Moving Average)  momentum_kama


檢視其中的 RSI 指標 :

>>> df['momentum_rsi']    
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    45.732159
2024-11-26    39.560536
2024-11-27    34.107863
2024-11-28    33.564358
2024-11-29    35.231532
2024-12-02    48.890462
2024-12-03    54.877909
2024-12-04    57.262711
2024-12-05    59.373997
2024-12-06    57.200326
2024-12-09    57.755502
2024-12-10    53.565209
2024-12-11    48.923488
2024-12-12    54.538551
2024-12-13    54.894578
2024-12-16    56.835996
2024-12-17    59.199976
2024-12-18    59.199976
2024-12-19    50.533601
2024-12-20    45.727610
2024-12-23    57.082949
2024-12-24    58.197052
2024-12-25    59.434195
2024-12-26    58.964392
2024-12-27    60.424311
2024-12-30    56.745051
2024-12-31    50.565547
2025-01-02    46.083754
2025-01-03    51.404401
2025-01-06    63.603546
2025-01-07    66.879390
2025-01-08    59.112553
Name: momentum_rsi, dtype: float64

可見這是用預設 window=14 計算出來的 RSI 指標, 可以用上面的 kbar.py 模組將 RSI 繪製到 K 線圖下方的副圖上 : 

>>> import kbar     
>>> kb=kbar.KBar(df)    
>>> kb.addplot(df['momentum_rsi'], panel=2, ylabel='RSI')     # 副圖 2
>>> kb.plot(volume=True, mav=5)   

此處因為用 volume=True 開啟繪製成交量, 它預設占用 panel=1 的副圖, 故 RSI 要畫在 panel=2 的副圖 (否則會疊在一起), 結果如下 :




(4). 波動指標的快捷函式 ta.add_volatility_ta() :  

呼叫 ta.add_volatility_ta() 來產生波動類型之指標數據, 其參數格式如下 (與趨勢類相同) : 

ta.add_volatility_ta(
    df: pd.DataFrame,   # 價量資料 (必要)
    high: str,    # 最高價欄名 (必要)
    low: str,     # 最低價欄名 (必要)
    close: str,   # 收盤價欄名 (必要)
    fillna: bool = False,  # 是否在技術指標計算後自動填充缺失值
    colprefix: str = ""     # 在新增的技術指標攔名前添加前綴以避免欄名衝突
    ) 

此函式會以預設 window 參數值去計算所有波動類型之指標. 

為了清楚看出此函式新增了那些欄位, 重新下載價量資料讓 df 回復為原始欄位 :

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

ta.add_volatility_ta() 會將指標計算結果添加到 DataFrame 後傳回 (也可以用一個變數來接收傳回值, 但沒必要) : 

>>> ta.add_volatility_ta(df, high='High', low='Low', close='Close')    

檢視 DataFrame 欄位 : 

>>> df.columns      
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'volatility_bbm',
       'volatility_bbh', 'volatility_bbl', 'volatility_bbw', 'volatility_bbp',
       'volatility_bbhi', 'volatility_bbli', 'volatility_kcc',
       'volatility_kch', 'volatility_kcl', 'volatility_kcw', 'volatility_kcp',
       'volatility_kchi', 'volatility_kcli', 'volatility_dcl',
       'volatility_dch', 'volatility_dcm', 'volatility_dcw', 'volatility_dcp',
       'volatility_atr', 'volatility_ui'],
      dtype='object')

可見 df 除了原來的價量欄位外, 後面新增了所有支援之波動指標欄位 :


 波動指標 (Volatility)  df 指標欄位名稱
 Bollinger Bands  volatility_bbm,
 volatility_bbh,
 volatility_bbl,
 volatility_bbw,
 volatility_bbp,
 volatility_bbhi,
 volatility_bbli
 Keltner Channel  volatility_kcc,
 volatility_kch,
 volatility_kcl,
 volatility_kcw,
 volatility_kcp,
 volatility_kchi,
 volatility_kcli
 Donchian Channel  volatility_dcl,
 volatility_dch,
 volatility_dcm,
 volatility_dcw,
 volatility_dcp
 ATR (Average True Range)  volatility_atr
 UI (Ulcer Index)  volatility_ui


以布林傑通道 (Bollinger Bands) 指標為例, 它是以上下限與中線來判斷價格的波動範圍和潛在的支撐或壓力區域, 指標欄位如下 :
  • volatility_bbm (中線) : 收盤價的移動平均線 (SMA, 預設 20 日)
  • volatility_bbh (上限) : 中線加上某個標準差 (預設 2 ) 
  • volatility_bbl (下限) : 中線減掉某個標準差 (預設 2 ) 
布林傑通道指標上下限與中線 :

>>> df['volatility_bbm']    # 中線 (20 日 SMA)
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           NaN
2024-11-27           NaN
2024-11-28           NaN
2024-11-29           NaN
2024-12-02           NaN
2024-12-03    192.297501
2024-12-04    192.310001
2024-12-05    192.262502
2024-12-06    192.100002
2024-12-09    191.945001
2024-12-10    191.975002
2024-12-11    191.982501
2024-12-12    192.190001
2024-12-13    192.337501
2024-12-16    192.660001
2024-12-17    192.902502
2024-12-18    193.185002
2024-12-19    193.437502
2024-12-20    193.415002
2024-12-23    193.660001
2024-12-24    194.057501
2024-12-25    194.622501
2024-12-26    195.195000
2024-12-27    195.777500
2024-12-30    196.082500
2024-12-31    196.155000
2025-01-02    196.085001
2025-01-03    196.060001
2025-01-06    196.380000
2025-01-07    196.800000
2025-01-08    197.137500
Name: volatility_bbm, dtype: float64
>>> df['volatility_bbh']     # 上限 (2 個標準差)
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           NaN
2024-11-27           NaN
2024-11-28           NaN
2024-11-29           NaN
2024-12-02           NaN
2024-12-03    199.240373
2024-12-04    199.274597
2024-12-05    199.097987
2024-12-06    198.422342
2024-12-09    197.699815
2024-12-10    197.778404
2024-12-11    197.790277
2024-12-12    198.137401
2024-12-13    198.437816
2024-12-16    198.839368
2024-12-17    199.372313
2024-12-18    199.866401
2024-12-19    199.898141
2024-12-20    199.884012
2024-12-23    200.317523
2024-12-24    200.706958
2024-12-25    200.707604
2024-12-26    200.304884
2024-12-27    199.634244
2024-12-30    199.545919
2024-12-31    199.525593
2025-01-02    199.567542
2025-01-03    199.537442
2025-01-06    200.748224
2025-01-07    202.386677
2025-01-08    202.962262
Name: volatility_bbh, dtype: float64
>>> df['volatility_bbl']     # 下限 (2 個標準差)
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           NaN
2024-11-27           NaN
2024-11-28           NaN
2024-11-29           NaN
2024-12-02           NaN
2024-12-03    185.354630
2024-12-04    185.345406
2024-12-05    185.427016
2024-12-06    185.777661
2024-12-09    186.190188
2024-12-10    186.171599
2024-12-11    186.174726
2024-12-12    186.242600
2024-12-13    186.237185
2024-12-16    186.480634
2024-12-17    186.432690
2024-12-18    186.503604
2024-12-19    186.976862
2024-12-20    186.945992
2024-12-23    187.002480
2024-12-24    187.408045
2024-12-25    188.537398
2024-12-26    190.085117
2024-12-27    191.920756
2024-12-30    192.619081
2024-12-31    192.784407
2025-01-02    192.602459
2025-01-03    192.582560
2025-01-06    192.011776
2025-01-07    191.213323
2025-01-08    191.312738
Name: volatility_bbl, dtype: float64

可以用上面的 kbar.py 模組將布林傑通道的中線與上下限以疊圖方式繪製到 K 線圖上 : 

>>> import kbar     
>>> kb=kbar.KBar(df)    
>>> kb.addplot(df['volatility_bbh'], color='red', width=1)       # 上限疊圖
>>> kb.addplot(df['volatility_bbm'], color='blue', width=1)     # 中線疊圖
>>> kb.addplot(df['volatility_bbl'], color='green', width=1)     # 下限疊圖
>>> kb.plot(volume=True)   
 
此處因為要套疊布林傑通道線, 因此不宜傳入 mav 參數以免混淆, 結果如下 :




(5). 量能指標的快捷函式 ta.add_volume_ta() :  

呼叫 ta.add_volume_ta() 來產生量能類型之指標數據, 其參數格式如下 (與動能類相同) : 

ta.add_volume_ta(
    df: pd.DataFrame,   # 價量資料 (必要)
    high: str,    # 最高價欄名 (必要)
    low: str,     # 最低價欄名 (必要)
    close: str,   # 收盤價欄名 (必要)
    volume: str,   # 成交量 (必要)
    fillna: bool = False,  # 是否在技術指標計算後自動填充缺失值
    colprefix: str = ""     # 在新增的技術指標攔名前添加前綴以避免欄名衝突
    ) 

此函式會以預設 window 參數值去計算所有量能類型之指標數據. 

為了清楚看出此函式新增了那些欄位, 重新下載價量資料讓 df 回復為原始欄位 :

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

ta.add_volume_ta() 會將指標計算結果添加到 DataFrame 後傳回 (也可以用一個變數來接收傳回值, 但沒必要) : 

>>> ta.add_volume_ta(df, high='High', low='Low', close='Close', volume='Volume')    

檢視 DataFrame 欄位 : 

>>> df.columns      
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'volume_adi',
       'volume_obv', 'volume_cmf', 'volume_fi', 'volume_em', 'volume_sma_em',
       'volume_vpt', 'volume_vwap', 'volume_mfi', 'volume_nvi'],
      dtype='object')

可見 df 除了原來的價量欄位外, 後面新增了所有支援之量能指標欄位 :

 量能指標 (Volume)  df 指標欄位名稱
 Accumulation/Distribution Line (ADL)  volume_adi
 On-Balance Volume (OBV)  volume_obv
 Chaikin Money Flow (CMF)  volume_cmf
 Force Index (FI)  volume_fi
 Ease of Movement (EM)  volume_em
 Smoothed Ease of Movement (SMA_EM)  volume_sma_em
 Volume Price Trend (VPT)  volume_vpt
 Volume Weighted Average Price (VWAP)  volume_vwap
 Money Flow Index (MFI)  volume_mfi
 Negative Volume Index (NVI)  volume_nvi


檢視其中的 OBV 指標 :

>>> df['volume_obv']     
Date
2024-11-06    16706376
2024-11-07    30204928
2024-11-08    43155079
2024-11-11    55519092
2024-11-12    29247005
2024-11-13    20052415
2024-11-14     7905800
2024-11-15    15291381
2024-11-18     1896865
2024-11-19     9515186
2024-11-20     9437186
2024-11-21    -6439431
2024-11-22     1526156
2024-11-25    -6300507
2024-11-26   -17691329
2024-11-27   -31764015
2024-11-28   -44873129
2024-11-29   -36744909
2024-12-02   -26200831
2024-12-03   -16247725
2024-12-04    -9792476
2024-12-05    -2346273
2024-12-06    -9155779
2024-12-09    -4515915
2024-12-10    -8993222
2024-12-11   -15707911
2024-12-12   -10680670
2024-12-13    -6160243
2024-12-16     1183349
2024-12-17    10477578
2024-12-18    17031455
2024-12-19     3986408
2024-12-20    -7136986
2024-12-23     4039080
2024-12-24    12234982
2024-12-25    18141105
2024-12-26    13337544
2024-12-27    20324961
2024-12-30    11647695
2024-12-31     3811161
2025-01-02    -8986541
2025-01-03    -1873170
2025-01-06    22754623
2025-01-07    38698467
2025-01-08    26842750
Name: volume_obv, dtype: int64

這與上面呼叫個別指標函式 ta.volume.on_balance_volume() 的結果是一樣的. 

檢視 MFI 指標 :

>>> df['volume_mfi']   
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    39.301682
2024-11-26    36.522781
2024-11-27    27.843909
2024-11-28    19.482690
2024-11-29    25.247484
2024-12-02    35.842102
2024-12-03    42.918545
2024-12-04    49.655030
2024-12-05    49.740230
2024-12-06    52.229974
2024-12-09    51.119383
2024-12-10    49.334007
2024-12-11    53.017548
2024-12-12    51.856243
2024-12-13    46.397760
2024-12-16    54.858813
2024-12-17    66.148226
2024-12-18    70.160985
2024-12-19    59.032024
2024-12-20    48.676280
2024-12-23    49.330576
2024-12-24    50.220182
2024-12-25    43.808475
2024-12-26    49.368843
2024-12-27    50.541067
2024-12-30    48.551503
2024-12-31    48.016515
2025-01-02    40.670406
2025-01-03    45.672339
2025-01-06    52.673952
2025-01-07    55.040936
2025-01-08    52.999103
Name: volume_mfi, dtype: float64

這與上面呼叫個別指標函式 ta.volume.money_flow_index() 的結果是一樣的. 

用上面的 kbar.py 模組將 OBV 與 MFI 分別繪製在 K 線圖下方的副圖 2 與副圖 3 : 

>>> import kbar     
>>> kb=kbar.KBar(df)    
>>> kb.addplot(df['volume_obv'] , panel=2, ylabel='OBV')    # 副圖 2
>>> kb.addplot(df['volume_mfi'] , panel=3, ylabel='MFI')     # 副圖 3
>>> kb.plot(volume=True, mav=5)   

結果如下 : 




(6). 其它指標的快捷函式 ta.add_others_ta() :  

呼叫 ta.others_ta() 來產生其它類型之指標數據 (報酬率), 其參數格式如下 : 

ta.add_volume_ta(
    df: pd.DataFrame,   # 價量資料 (必要)
    close: str,   # 收盤價欄名 (必要)
    fillna: bool = False,  # 是否在技術指標計算後自動填充缺失值
    colprefix: str = ""     # 在新增的技術指標攔名前添加前綴以避免欄名衝突
    ) 

可見此函式只需要指定收盤價欄位即可, 用來計算三種報酬率數據. 

為了清楚看出此函式新增了那些欄位, 重新下載價量資料讓 df 回復為原始欄位 :

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

ta.add_others_ta() 會將指標計算結果添加到 DataFrame 後傳回 (也可以用一個變數來接收傳回值, 但沒必要) : 

>>> ta.add_others_ta(df, close='Close')    

檢視 DataFrame 欄位 : 

>>> df.columns      
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'others_dr',
       'others_dlr', 'others_cr'],
      dtype='object')

可見 df 除了原來的價量欄位外, 後面新增了三個指標欄位 :


 其它指標 (Others)  df 指標欄位名稱
 Daily Return (dr) 每日報酬率  others_dr
 Daily Log Return (dlr) 每日對數報酬率  others_dlr
 Cumulative Return (cr) 累積報酬率  others_cr


檢視此三種報酬率欄位 :

>>> df['others_dr']     # 日報酬率
Date
2024-11-06         NaN
2024-11-07    1.152664
2024-11-08    0.785010
2024-11-11    0.050254
2024-11-12   -2.561530
2024-11-13   -0.644330
2024-11-14   -0.907912
2024-11-15    0.706809
2024-11-18   -1.403697
2024-11-19    1.370950
2024-11-20   -0.416126
2024-11-21   -1.096887
2024-11-22    1.954052
2024-11-25   -0.362599
2024-11-26   -1.299714
2024-11-27   -1.448512
2024-11-28   -0.160344
2024-11-29    0.240898
2024-12-02    2.376500
2024-12-03    1.356289
2024-12-04    0.591865
2024-12-05    0.537223
2024-12-06   -0.381679
2024-12-09    0.127714
2024-12-10   -0.714283
2024-12-11   -0.873593
2024-12-12    1.166408
2024-12-13    0.076869
2024-12-16    0.409628
2024-12-17    0.509944
2024-12-18    0.000000
2024-12-19   -1.369869
2024-12-20   -0.925920
2024-12-23    2.414327
2024-12-24    0.278836
2024-12-25    0.303332
2024-12-26   -0.075602
2024-12-27    0.327866
2024-12-30   -0.553037
2024-12-31   -1.036402
2025-01-02   -0.868453
2025-01-03    1.004894
2025-01-06    3.137752
2025-01-07    1.113035
2025-01-08   -1.492166
Name: others_dr, dtype: float64
>>> df['others_dlr']      # 對數報酬率
Date
2024-11-06         NaN
2024-11-07    1.146071
2024-11-08    0.781945
2024-11-11    0.050242
2024-11-12   -2.594908
2024-11-13   -0.646415
2024-11-14   -0.912058
2024-11-15    0.704323
2024-11-18   -1.413642
2024-11-19    1.361637
2024-11-20   -0.416995
2024-11-21   -1.102948
2024-11-22    1.935205
2024-11-25   -0.363258
2024-11-26   -1.308234
2024-11-27   -1.459105
2024-11-28   -0.160472
2024-11-29    0.240608
2024-12-02    2.348701
2024-12-03    1.347174
2024-12-04    0.590120
2024-12-05    0.535785
2024-12-06   -0.382410
2024-12-09    0.127632
2024-12-10   -0.716846
2024-12-11   -0.877431
2024-12-12    1.159657
2024-12-13    0.076839
2024-12-16    0.408791
2024-12-17    0.508648
2024-12-18    0.000000
2024-12-19   -1.379338
2024-12-20   -0.930233
2024-12-23    2.385643
2024-12-24    0.278447
2024-12-25    0.302873
2024-12-26   -0.075630
2024-12-27    0.327329
2024-12-30   -0.554572
2024-12-31   -1.041810
2025-01-02   -0.872246
2025-01-03    0.999879
2025-01-06    3.089531
2025-01-07    1.106886
2025-01-08   -1.503411
Name: others_dlr, dtype: float64
>>> df['others_cr']      # 累積報酬率
Date
2024-11-06    0.000000
2024-11-07    1.152664
2024-11-08    1.946723
2024-11-11    1.997956
2024-11-12   -0.614753
2024-11-13   -1.255121
2024-11-14   -2.151638
2024-11-15   -1.460036
2024-11-18   -2.843239
2024-11-19   -1.511269
2024-11-20   -1.921107
2024-11-21   -2.996922
2024-11-22   -1.101431
2024-11-25   -1.460036
2024-11-26   -2.740774
2024-11-27   -4.149586
2024-11-28   -4.303276
2024-11-29   -4.072744
2024-12-02   -1.793033
2024-12-03   -0.461062
2024-12-04    0.128074
2024-12-05    0.665985
2024-12-06    0.281764
2024-12-09    0.409838
2024-12-10   -0.307372
2024-12-11   -1.178280
2024-12-12   -0.025616
2024-12-13    0.051233
2024-12-16    0.461070
2024-12-17    0.973365
2024-12-18    0.973365
2024-12-19   -0.409838
2024-12-20   -1.331963
2024-12-23    1.050206
2024-12-24    1.331970
2024-12-25    1.639343
2024-12-26    1.562502
2024-12-27    1.895490
2024-12-30    1.331970
2024-12-31    0.281764
2025-01-02   -0.589136
2025-01-03    0.409838
2025-01-06    3.560449
2025-01-07    4.713113
2025-01-08    3.150619
Name: others_cr, dtype: float64

在使用 pyfolio 等策略風險評估工具時就需要用到 others_dr 欄位的日報酬率數據 (較長期的風險評估則需要使用調整後的收盤價 Adj Close 較精確), 參考 :



(7). 呼叫 add_all_ta_features() 計算全部 80 個指標 :  

這個函式可說是 ta 的超級函式, 可一次計算出全部 80 個指標, 

其參數格式如下 (與動能類相同) : 

ta.add_all_ta_features(
    df: pd.DataFrame,   # 價量資料 (必要)
    open: str,    # 開盤價欄名 (必要)
    high: str,    # 最高價欄名 (必要)
    low: str,     # 最低價欄名 (必要)
    close: str,   # 收盤價欄名 (必要)
    volume: str,   # 成交量 (必要)
    fillna: bool = False,  # 是否在技術指標計算後自動填充缺失值
    colprefix: str = ""     # 在新增的技術指標攔名前添加前綴以避免欄名衝突
    ) 

可見呼叫此函式時, OHLCV 五個欄位名稱都必須傳入, 它會以預設 window 參數值去計算所有類型之全部 80 個指標數據. 

為了清楚看出此函式新增了那些欄位, 重新下載價量資料讓 df 回復為原始欄位 :

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

ta.add_volume_ta() 會將指標計算結果添加到 DataFrame 後傳回 (也可以用一個變數來接收傳回值, 但沒必要) : 

>>> ta.add_all_ta_features(df, open='Open', high='High', low='Low', close='Close', volume='Volume')    

檢視 DataFrame 欄位 : 

>>> df.columns      
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'volume_adi',
       'volume_obv', 'volume_cmf', 'volume_fi', 'volume_em', 'volume_sma_em',
       'volume_vpt', 'volume_vwap', 'volume_mfi', 'volume_nvi',
       'volatility_bbm', 'volatility_bbh', 'volatility_bbl', 'volatility_bbw',
       'volatility_bbp', 'volatility_bbhi', 'volatility_bbli',
       'volatility_kcc', 'volatility_kch', 'volatility_kcl', 'volatility_kcw',
       'volatility_kcp', 'volatility_kchi', 'volatility_kcli',
       'volatility_dcl', 'volatility_dch', 'volatility_dcm', 'volatility_dcw',
       'volatility_dcp', 'volatility_atr', 'volatility_ui', 'trend_macd',
       'trend_macd_signal', 'trend_macd_diff', 'trend_sma_fast',
       'trend_sma_slow', 'trend_ema_fast', 'trend_ema_slow',
       'trend_vortex_ind_pos', 'trend_vortex_ind_neg', 'trend_vortex_ind_diff',
       'trend_trix', 'trend_mass_index', 'trend_dpo', 'trend_kst',
       'trend_kst_sig', 'trend_kst_diff', 'trend_ichimoku_conv',
       'trend_ichimoku_base', 'trend_ichimoku_a', 'trend_ichimoku_b',
       'trend_stc', 'trend_adx', 'trend_adx_pos', 'trend_adx_neg', 'trend_cci',
       'trend_visual_ichimoku_a', 'trend_visual_ichimoku_b', 'trend_aroon_up',
       'trend_aroon_down', 'trend_aroon_ind', 'trend_psar_up',
       'trend_psar_down', 'trend_psar_up_indicator',
       'trend_psar_down_indicator', 'momentum_rsi', 'momentum_stoch_rsi',
       'momentum_stoch_rsi_k', 'momentum_stoch_rsi_d', 'momentum_tsi',
       'momentum_uo', 'momentum_stoch', 'momentum_stoch_signal', 'momentum_wr',
       'momentum_ao', 'momentum_roc', 'momentum_ppo', 'momentum_ppo_signal',
       'momentum_ppo_hist', 'momentum_pvo', 'momentum_pvo_signal',
       'momentum_pvo_hist', 'momentum_kama', 'others_dr', 'others_dlr',
       'others_cr'],
      dtype='object')

可見新增的欄位就是上面五種類型的全部指標. 

2025年1月15日 星期三

完成阿蘭安金事宜

這周最重要的工作便是要辦理吾妹阿蘭之安金事宜, 我昨日即請假先回鄉下做最後準備, 檢視老師表列之備辦品項是否齊全, 昨日下午先至觀音廟將阿蘭金罐請到墓園南邊門內安放, 這樣今日早上就可以直接去墓園, 不用一大早請觀音廟的師姐開門了. 下午整理完房間就出發前往楠梓載菁菁, 她七點水晶店闆娘要接睫毛, 等她十點下班剛好順路去高鐵載姐姐一起回鄉下. 

昨晚高雄下起小雨, 今晨起來還在下間歇的毛毛雨. 06:20 出發途經榮發舅媽的豬肉店下車取三副牲禮 (3700 元) 後前往墓園, 到土地公廟時發現泥水劉師傅已到, 趕緊先去墓園貼紅紙, 07:00 溫老師抵達後先打開北邊門再打開南邊門讓墓室通風, 調整好牲禮果品位置 (由左而右麵魚雞豬) 倒好茶點燃蠟燭後先進行落馬儀式, 以紙杯盛香爐灰做成香爐, 將金罐從南邊門捧出置於面對墓園之右側台階上, 點六支香, 三支給老師, 由老師先向祖先說明今日儀式目的, 再向阿蘭金罐講述並祈願後將金罐捧入墓室, 劉師傅已用磚頭與混泥土將座位底部墊高, 金罐置入後周圍以細沙填實, 完成後先鎖北邊內門, 再鎖南邊內門, 劉師傅任務完成奉上紅包後先行離去. 接著點香由老師引導祭拜, 拜完奉上紅包老師也先行離開, 接下來就是跟掃墓程序一樣酒過三巡燒金紙, 再次點香向祖先稟告安金儀式完成後鳴炮即可收拾祭品. 

老師離開前給了一張單子列出安金之後續辦理事項 :
  1. 安金後第二天第三天 : 須至墓園奉茶, 上香, 燒金
  2. 安金後第十二天 : 須備三份水果 (祖先, 后土, 天神) 至墓園奉茶, 上香, 燒金
  3. 安金後滿月日 : 須備五牲一副, 發粄, 紅粄各三包, 水果三份, 鮮花一對, 防風燭三對, 鞭炮, 壽金, 香, 茶至墓園祭拜
今明兩天我將從鄉下通勤, 早上六點半出發前往墓園奉茶上香燒金後再去上班. 安金後第 12 天即 1/26 (週日, 春節連假第 2 天, 年二十七) 毋須請假, 安金後滿月日是農曆 1 月 16 日 (國曆 2/13, 元宵節後一日) 是週四要請半天假. 

另有兩個注意事項 : 
  1. 四個月內不可包奠儀, 但若這之前有包過紅包則可. 
  2. 今年掃墓須在農曆 2 月 21 日 (春社日, 春分, 國曆 3/20) 之前掃墓.
每年掃墓都是農曆二月第二個周日, 今年是 3/9 日剛好在春分之前. 


2025年1月13日 星期一

Python 學習筆記 : 策略績效與風險分析套件 pyfolio

Pyfolio 是一個用來評估投資策略表現的 Python 套件, 透過 Jupyter 或 Colab 等 web 介面呈現詳細的分析數據與視覺化圖表, 讓金融投資策略開發者可快速獲得投資組合的風險與績效分析, 例如年化報酬率, 夏普比率, 最大回撤 (MDD) 與波動率等. 除了獨立進行分析, 也可與回測框架 (例如 Zipline) 搭配使用. 

Pyfolio 最早是在 2015 年由位於波士頓的對沖基金公司 Quantopian 所開發, 做為其演算法量化交易平台回測引擎 Zipline 的策略績效分析工具. 於 2017 年開放原始碼後受到金融交易社群與學術圈的喜愛. 不過由於 Quantopian 於 2022 年底停止營運, Pyfolio 的開發與維護也隨之停頓, 但 pyfolio 使用者社群仍非常活躍, 其原始碼與教學文件放在 GitHub :


網路教學文章參考 : 



1. 安裝 pyfolio 套件 : 

Pyfolio 可以直接用 pip 安裝 : 

pip install pyfolio 

這會從 PyPi 網站下載套件, 參考 : 

D:\python\test>pip install pyfolio    
Collecting pyfolio
  Downloading pyfolio-0.9.2.tar.gz (91 kB)
  Preparing metadata (setup.py) ... done
Requirement already satisfied: ipython>=3.2.3 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (8.15.0)
Requirement already satisfied: matplotlib>=1.4.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (3.7.2)
Requirement already satisfied: numpy>=1.11.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (1.24.3)
Requirement already satisfied: pandas>=0.18.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (2.0.3)
Requirement already satisfied: pytz>=2014.10 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (2023.3)
Requirement already satisfied: scipy>=0.14.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (1.11.2)
Requirement already satisfied: scikit-learn>=0.16.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (1.3.0)
Requirement already satisfied: seaborn>=0.7.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pyfolio) (0.12.2)
Collecting empyrical>=0.5.0 (from pyfolio)
  Downloading empyrical-0.5.5.tar.gz (52 kB)
  Preparing metadata (setup.py) ... done
Collecting pandas-datareader>=0.2 (from empyrical>=0.5.0->pyfolio)
  Downloading pandas_datareader-0.10.0-py3-none-any.whl.metadata (2.9 kB)
Requirement already satisfied: backcall in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (0.2.0)
Requirement already satisfied: decorator in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (5.1.1)
Requirement already satisfied: jedi>=0.16 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from ipython>=3.2.3->pyfolio) (0.18.2)
Requirement already satisfied: matplotlib-inline in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (0.1.6)
Requirement already satisfied: pickleshare in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (0.7.5)
Requirement already satisfied: prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (3.0.39)
Requirement already satisfied: pygments>=2.4.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (2.16.1)
Requirement already satisfied: stack-data in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (0.6.2)
Requirement already satisfied: traitlets>=5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (5.9.0)
Requirement already satisfied: exceptiongroup in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=3.2.3->pyfolio) (1.1.3)
Requirement already satisfied: colorama in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from ipython>=3.2.3->pyfolio) (0.4.6)
Requirement already satisfied: contourpy>=1.0.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (1.1.0)
Requirement already satisfied: cycler>=0.10 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (0.11.0)
Requirement already satisfied: fonttools>=4.22.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (4.42.1)
Requirement already satisfied: kiwisolver>=1.0.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (1.4.5)
Requirement already satisfied: packaging>=20.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (23.1)
Requirement already satisfied: pillow>=6.2.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (9.5.0)
Requirement already satisfied: pyparsing<3.1,>=2.3.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (3.0.9)
Requirement already satisfied: python-dateutil>=2.7 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from matplotlib>=1.4.0->pyfolio) (2.8.2)
Requirement already satisfied: tzdata>=2022.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas>=0.18.1->pyfolio) (2023.3)
Requirement already satisfied: joblib>=1.1.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from scikit-learn>=0.16.1->pyfolio) (1.3.2)
Requirement already satisfied: threadpoolctl>=2.0.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from scikit-learn>=0.16.1->pyfolio) (3.2.0)
Requirement already satisfied: parso<0.9.0,>=0.8.0 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from jedi>=0.16->ipython>=3.2.3->pyfolio) (0.8.3)
Requirement already satisfied: lxml in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas-datareader>=0.2->empyrical>=0.5.0->pyfolio) (4.9.3)
Requirement already satisfied: requests>=2.19.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas-datareader>=0.2->empyrical>=0.5.0->pyfolio) (2.31.0)
Requirement already satisfied: wcwidth in c:\users\tony1\appdata\roaming\python\python310\site-packages (from prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30->ipython>=3.2.3->pyfolio) (0.2.6)
Requirement already satisfied: six>=1.5 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from python-dateutil>=2.7->matplotlib>=1.4.0->pyfolio) (1.16.0)
Requirement already satisfied: executing>=1.2.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from stack-data->ipython>=3.2.3->pyfolio) (1.2.0)
Requirement already satisfied: asttokens>=2.1.0 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from stack-data->ipython>=3.2.3->pyfolio) (2.2.1)
Requirement already satisfied: pure-eval in c:\users\tony1\appdata\roaming\python\python310\site-packages (from stack-data->ipython>=3.2.3->pyfolio) (0.2.2)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests>=2.19.0->pandas-datareader>=0.2->empyrical>=0.5.0->pyfolio) (3.2.0)
Requirement already satisfied: idna<4,>=2.5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests>=2.19.0->pandas-datareader>=0.2->empyrical>=0.5.0->pyfolio) (3.4)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from requests>=2.19.0->pandas-datareader>=0.2->empyrical>=0.5.0->pyfolio) (1.26.19)
Requirement already satisfied: certifi>=2017.4.17 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests>=2.19.0->pandas-datareader>=0.2->empyrical>=0.5.0->pyfolio) (2023.7.22)
Downloading pandas_datareader-0.10.0-py3-none-any.whl (109 kB)
Building wheels for collected packages: pyfolio, empyrical
  Building wheel for pyfolio (setup.py) ... done
  Created wheel for pyfolio: filename=pyfolio-0.9.2-py3-none-any.whl size=88689 sha256=cc4a1c12fb59a822d23492165f319d973d85870c01bda5b80915ced28a199a88
  Stored in directory: c:\users\tony1\appdata\local\pip\cache\wheels\71\38\bc\e53700cfd8b0ad6b539d2fbaaf060ed8a299e7622a5b86ef42
  Building wheel for empyrical (setup.py) ... done
  Created wheel for empyrical: filename=empyrical-0.5.5-py3-none-any.whl size=39779 sha256=7780681258103e51def4b2e78230f7d9e84dddf677cb7e840be6b1abf4701561
  Stored in directory: c:\users\tony1\appdata\local\pip\cache\wheels\0e\2e\f2\d6d2d9a1eb8fbbd9949bb5d4c00f753e3b74e5bd7ed10b1d36
Successfully built pyfolio empyrical
Installing collected packages: pandas-datareader, empyrical, pyfolio
Successfully installed empyrical-0.5.5 pandas-datareader-0.10.0 pyfolio-0.9.2

可見 Pyfolio 是在 Pandas, Numpy, Matplotlib + Seaborn 等套件基礎上建構的, 主要依賴 Matplotlib 作為繪圖核心. 匯入 pyfolio 模組檢視版本 (通常取簡名 pf) :

>>> import pyfolio as pf    
C:\Users\tony1\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\pos.py:26: UserWarning: Module "zipline.assets" not found; mutltipliers will not be applied to position notionals.
  warnings.warn(

出現這個 warning 的原因是因為 Pyfolio 通常與回測工具 Zipline 搭配使用, 其原始碼中有一部分預設會匯入其處理資產權重的 assets 模組, 因沒有安裝 Zipline 套件所以找不到此模組. 因 Zipline 並非 Pyfolio 必要之相依套件因此可忽略之, 也可以用 warnings 模組的 filterwarnings() 函式將其濾掉 :

>>> import warnings   
>>> warnings.filterwarnings('ignore')   

再次匯入 pyfolio 就不會有 warning 了 : 

>>> import pyfolio as pf   
>>> pf.__version__   
'0.9.2'


>>> list_members(pf)    
APPROX_BDAYS_PER_MONTH <class 'int'>
FACTOR_PARTITIONS <class 'dict'>
FigureCanvasAgg <class 'type'>
FuncFormatter <class 'type'>
MM_DISPLAY_UNIT <class 'float'>
Markdown <class 'type'>
OrderedDict <class 'type'>
STAT_FUNCS_PCT <class 'list'>
axes_style <class 'function'>
capacity <class 'module'>
create_bayesian_tear_sheet <class 'function'>
create_capacity_tear_sheet <class 'function'>
create_full_tear_sheet <class 'function'>
create_interesting_times_tear_sheet <class 'function'>
create_perf_attrib_tear_sheet <class 'function'>
create_position_tear_sheet <class 'function'>
create_returns_tear_sheet <class 'function'>
create_risk_tear_sheet <class 'function'>
create_round_trip_tear_sheet <class 'function'>
create_simple_tear_sheet <class 'function'>
create_txn_tear_sheet <class 'function'>
customize <class 'function'>
datetime <class 'module'>
deprecate <class 'module'>
display <class 'function'>
division <class '__future__._Feature'>
ep <class 'module'>
figure <class 'module'>
gridspec <class 'module'>
have_bayesian <class 'bool'>
interesting_periods <class 'module'>
matplotlib <class 'module'>
np <class 'module'>
patches <class 'module'>
pd <class 'module'>
perf_attrib <class 'module'>
plot_annual_returns <class 'function'>
plot_capacity_sweep <class 'function'>
plot_cones <class 'function'>
plot_daily_turnover_hist <class 'function'>
plot_daily_volume <class 'function'>
plot_drawdown_periods <class 'function'>
plot_drawdown_underwater <class 'function'>
plot_exposures <class 'function'>
plot_gross_leverage <class 'function'>
plot_holdings <class 'function'>
plot_long_short_holdings <class 'function'>
plot_max_median_position_concentration <class 'function'>
plot_monthly_returns_dist <class 'function'>
plot_monthly_returns_heatmap <class 'function'>
plot_monthly_returns_timeseries <class 'function'>
plot_perf_stats <class 'function'>
plot_prob_profit_trade <class 'function'>
plot_return_quantiles <class 'function'>
plot_returns <class 'function'>
plot_rolling_beta <class 'function'>
plot_rolling_returns <class 'function'>
plot_rolling_sharpe <class 'function'>
plot_rolling_volatility <class 'function'>
plot_round_trip_lifetimes <class 'function'>
plot_sector_allocations <class 'function'>
plot_slippage_sensitivity <class 'function'>
plot_slippage_sweep <class 'function'>
plot_turnover <class 'function'>
plot_txn_time_hist <class 'function'>
plotting <class 'module'>
plotting_context <class 'function'>
plt <class 'module'>
pos <class 'module'>
pytz <class 'module'>
risk <class 'module'>
round_trips <class 'module'>
scipy <class 'module'>
show_and_plot_top_positions <class 'function'>
show_perf_stats <class 'function'>
show_profit_attribution <class 'function'>
show_worst_drawdown_periods <class 'function'>
sns <class 'module'>
sp <class 'module'>
tears <class 'module'>
time <class 'builtin_function_or_method'>
timer <class 'function'>
timeseries <class 'module'>
txn <class 'module'>
utils <class 'module'>
warnings <class 'module'>
wraps <class 'function'>

雖然 Pyfolio 提供玲瑯滿目的函式與模組, 但我們通常只會用到它的快捷函式 create_returns_tear_sheet(), 它會自動產生績效分析與風險評估的全部數據與圖表. 此函式必須傳入一個 Series 物件, 且其索引須為日期時間類型, 值通常為代表報酬率 (return) 的小數, 這可以利用 Series 物件的 pct_change() 方法求得. 


2. 在 web 介面上執行 pyfolio : 

Pyfolio 必須在 web 介面例如 Jupyter Lab, Jupyter Notebook 或 Colab 上執行, 因為它要透過網頁繪製數據分析的圖表, 命令列執行環境無法滿足此要求. 

先在本機用 Jupyter Lab 測試, 先在命令列輸入 jupyter lab 啟動 web 介面 : 

D:\python\test>jupyter lab     




匯入 Pyfolio : import pyfolio as pf 




以下以買入 0050 一股持有不賣策略為例測試 pyfolio 用法, 先用 yfinance 下載股票資料 : 

import yfinance as yf   
df=yf.download('0050.TW')   




檢視前後五筆資料 : 

df.head()    

                 Open       High        Low      Close  Adj Close  Volume
Date                                                                     
2008-01-02  60.009998  60.009998  60.009998  60.009998  42.407536       0
2008-01-03  58.889999  58.889999  58.889999  58.889999  41.616066       0
2008-01-04  59.009998  59.009998  59.009998  59.009998  41.700867       0
2008-01-07  56.389999  56.389999  56.389999  56.389999  39.849377       0
2008-01-08  56.980000  56.980000  56.980000  56.980000  40.266319       0

df.tail()   

                  Open        High  ...   Adj Close    Volume
Date                                ...                      
2025-01-06  198.649994  202.300003  ...  202.149994  24627793
2025-01-07  204.949997  206.000000  ...  204.399994  15943844
2025-01-08  202.649994  203.350006  ...  201.350006  11855717
2025-01-09  200.199997  201.149994  ...  199.050003   8430655
2025-01-10  198.250000  198.250000  ...  198.250000         0

然後利用此股票資料中的調整後收盤價 Adj Close 計算此策略之報酬率, 如果時間較短用 Close 尚可, 但期間長達 15 年應該用調整後價格較準確. 計算報酬率可以呼叫 Series 物件的 pct_change() 方法達成, 此方法是將今日價減昨日價後再除以昨日價得到今日報酬率 :

returns=df['Adj Close'].pct_change()  
print(returns)  



 
可知傳回值為一個以日期時間為索引的 Series 物件, 符合 Pyfolio 的要求, 將此 returns 傳給 pf.create_returns_tear_sheet() 即可得到分析結果 : 

pf.create_returns_tear_sheet(returns)   

但執行卻出現下列錯誤訊息 : 

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[23], line 2
      1 returns.dropna()
----> 2 pf.create_returns_tear_sheet(returns.dropna()) 

File ~\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\plotting.py:52, in customize.<locals>.call_w_context(*args, **kwargs)
     50 if set_context:
     51     with plotting_context(), axes_style():
---> 52         return func(*args, **kwargs)
     53 else:
     54     return func(*args, **kwargs)

File ~\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\tears.py:496, in create_returns_tear_sheet(returns, positions, transactions, live_start_date, cone_std, benchmark_rets, bootstrap, turnover_denom, header_rows, return_fig)
    493 if benchmark_rets is not None:
    494     returns = utils.clip_returns_to_benchmark(returns, benchmark_rets)
--> 496 plotting.show_perf_stats(returns, benchmark_rets,
    497                          positions=positions,
    498                          transactions=transactions,
    499                          turnover_denom=turnover_denom,
    500                          bootstrap=bootstrap,
    501                          live_start_date=live_start_date,
    502                          header_rows=header_rows)
    504 plotting.show_worst_drawdown_periods(returns)
    506 vertical_sections = 11

File ~\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\plotting.py:648, in show_perf_stats(returns, factor_returns, positions, transactions, turnover_denom, live_start_date, bootstrap, header_rows)
    645     perf_stats = pd.DataFrame(perf_stats_all, columns=['Backtest'])
    647 for column in perf_stats.columns:
--> 648     for stat, value in perf_stats[column].iteritems():
    649         if stat in STAT_FUNCS_PCT:
    650             perf_stats.loc[stat, column] = str(np.round(value * 100,
    651                                                         1)) + '%'

File ~\AppData\Roaming\Python\Python310\site-packages\pandas\core\generic.py:5989, in NDFrame.__getattr__(self, name)
   5982 if (
   5983     name not in self._internal_names_set
   5984     and name not in self._metadata
   5985     and name not in self._accessors
   5986     and self._info_axis._can_hold_identifiers_and_holds_name(name)
   5987 ):
   5988     return self[name]
-> 5989 return object.__getattribute__(self, name)

AttributeError: 'Series' object has no attribute 'iteritems'

將錯誤訊息丟給 ChatGPT, 發現原因是與 Pandas 版本不相容, 檢查目前版本是 2.0.3, 而 Pyfolio 因為使用 Pandas v1 版, 所用的 iteritems() 函式已被廢棄導致錯誤, ChatGPT 建議的解決辦法之一是把 Pandas 版本降回 1.2.x 版, 例如 1.2.5 : 

pip install pandas==1.2.5

第二個辦法是到 Pyfolio 的安裝路徑下找出 plotting.py 檔, 將第 648 列的 iteritem() 改為 item() 即可, 利用 .__file__ 屬性即可查到此路徑 :

print(pf.__file__)   

C:\Users\tony1\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\__init__.py 



 
改好後再次執行出現另一個錯誤訊息 : 


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[5], line 1
----> 1 pf.create_returns_tear_sheet(returns)

File ~\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\plotting.py:52, in customize.<locals>.call_w_context(*args, **kwargs)
     50 if set_context:
     51     with plotting_context(), axes_style():
---> 52         return func(*args, **kwargs)
     53 else:
     54     return func(*args, **kwargs)

File ~\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\tears.py:504, in create_returns_tear_sheet(returns, positions, transactions, live_start_date, cone_std, benchmark_rets, bootstrap, turnover_denom, header_rows, return_fig)
    494     returns = utils.clip_returns_to_benchmark(returns, benchmark_rets)
    496 plotting.show_perf_stats(returns, benchmark_rets,
    497                          positions=positions,
    498                          transactions=transactions,
   (...)
    501                          live_start_date=live_start_date,
    502                          header_rows=header_rows)
--> 504 plotting.show_worst_drawdown_periods(returns)
    506 vertical_sections = 11
    508 if live_start_date is not None:

File ~\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\plotting.py:1664, in show_worst_drawdown_periods(returns, top)
   1648 def show_worst_drawdown_periods(returns, top=5):
   1649     """
   1650     Prints information about the worst drawdown periods.
   1651 
   (...)
   1661         Amount of top drawdowns periods to plot (default 5).
   1662     """
-> 1664     drawdown_df = timeseries.gen_drawdown_table(returns, top=top)
   1665     utils.print_table(
   1666         drawdown_df.sort_values('Net drawdown in %', ascending=False),
   1667         name='Worst drawdown periods',
   1668         float_format='{0:.2f}'.format,
   1669     )

File ~\AppData\Local\Programs\Thonny\lib\site-packages\pyfolio\timeseries.py:1008, in gen_drawdown_table(returns, top)
   1003     df_drawdowns.loc[i, 'Duration'] = len(pd.date_range(peak,
   1004                                                         recovery,
   1005                                                         freq='B'))
   1006 df_drawdowns.loc[i, 'Peak date'] = (peak.to_pydatetime()
   1007                                     .strftime('%Y-%m-%d'))
-> 1008 df_drawdowns.loc[i, 'Valley date'] = (valley.to_pydatetime()
   1009                                       .strftime('%Y-%m-%d'))
   1010 if isinstance(recovery, float):
   1011     df_drawdowns.loc[i, 'Recovery date'] = recovery

AttributeError: 'numpy.int64' object has no attribute 'to_pydatetime'

詢問 ChatGPT 回覆錯誤原因同樣是 Pandas 版本不相容所致, 建議修改 Pyfolio 的 timeseries.py 檔, 將其中第 1008~1009 列改為如下寫法 :

df_drawdowns.loc[i, 'Valley date'] = pd.Timestamp(valley).strftime('%Y-%m-%d')




舊版寫法是 :

df_drawdowns.loc[i, 'Valley date'] = (valley.to_pydatetime()
                                      .strftime('%Y-%m-%d'))

改好後重新執行又出現另一個錯誤 : 



原因還是 Pandas 版本不相容, 同樣是 timeseries.py 這檔案, 問題出現在 1014~1015 列的 gen_drawdown_table() 函式寫法, 原本寫法是 :

df_drawdowns.loc[i, 'Net drawdown in %'] = (
    (df_cum.loc[peak] - df_cum.loc[valley]) / df_cum.loc[peak]) * 100




ChatGPT 建議改為如下 : 

try:
    df_drawdowns.loc[i, 'Net drawdown in %'] = (
        (df_cum.loc[peak] - df_cum.loc[valley]) / df_cum.loc[peak]) * 100
except KeyError:
    # 如果索引找不到,設置為 NaN 或其他處理方式
    df_drawdowns.loc[i, 'Net drawdown in %'] = float('nan')

改好後就可以順利執行, 結果會產生下面的 13 張分析圖表 :

第一張圖表包含年化報酬率, 累積報酬率, 最大回撤率 (MDD, Maximum Draw Down) 等統計數值之摘要報告 :




MDD 表示資產淨值從最高峰到最低谷的最大跌幅, 是評估投資組合在特定期間內抗風險能力的重要指標, 它反映了投資者在最糟糕情況下可能面臨的損失比例 (最大曝險比率), 是考量曝險能力的重要數據. MDD 的計算通常用資產最大值與最小值之差除以最大值而得, 也可以用累積報酬率的峰值與谷值來算.  

第二張圖表顯示歷次回撤的區間與波峰波谷時間 : 




第三張圖表 Cumulative returns 是累積報酬率折線圖 :




第四張圖表 Cumulative returns volatility mathed to benchmark 是累積報酬率與基準報酬率 (例如大盤) 的比較, 可看出此策略之投資組合是否跑贏基準報酬率 : 



 
第五張圖表 Cumulative returns on logorithmic scale 則是以對數刻度來顯示累積報酬率與基準報酬率之比較 : 




第六張圖表 Returns 是每日報酬率折線圖 : 




第七張圖表 Rolling volatility (6-month) 顯示六個月為窗口 (window) 的移動波動率的變化, 以標準差為單位來衡量投資組合報酬率之波動程度 :




第八張圖表 Rolling Sharpe ratio (6-month) 顯示 6 個月為窗口 (window) 的夏普比率變化, 用來衡量報酬率相對於風險的效率. 夏普比的定義是單位風險所能獲得的報酬率, 基本上低於 0.5 的策略不要考慮, 大於 1 就算是很不錯的策略了. 




第九張圖表 Top 5 Drawdown periods 顯示最大的前五個回撤區間 : 





第十張圖表 Underwater plot 顯示投資組合的回撤深度, 頻率和恢復時間, 讓投資者可觀察策略在不同行情下的表現, 了解該策略是否符合自身的風險承受能力 : 




第十張圖表 Monthly returns 依照年月順序 (年份為垂直軸由上而下, 月份為水平軸由左至右) 以不同顏色在熱力圖中顯示每個月的報酬率表現, 正報酬以綠色系, 顏色越深報酬越大; 負報酬以紅色系表示, 越紅越負, 可藉顏色與深淺不同來觀察每年的季節性或趨勢性表現, 找出表現特別好或特別差的月份 : 




第十一張圖表 Annual returns 由上而下依照年度以水平長條圖顯示各年度之報酬率 : 




第十二張圖表 Distribution of monthly returns 以直方圖呈現投資組合的每月報酬率分佈狀況, 可讓投資者了解報酬率的集中程度, 對稱性, 以及是否存在異常值. 直方圖的橫軸是報酬率的分布範圍, 它將將整個報酬率範圍分為若干個區間 (bins), 例如每 2%. 而縱軸則表示每個報酬率區間發生的次數, 高度越高表示該區間內的報酬率出現得越頻繁 : 




第十三張圖表 Return quantiles 呈現投資組合報酬率在不同時間範圍內的分佈情況, 它用類似箱型圖的形式來顯示報酬率在各個時間段的分佈範圍, 橫軸表示日, 周, 月等不同時間範圍; 縱軸表示報酬率, 箱型圖最中間的線代表報酬率之中位數, 箱子的範圍 (上下四分位距) 表示 25% 到 75% 分位數的報酬率範圍 :




投資者可從箱型圖的範圍變化評估策略的穩定性和表現特徵, 若箱型圖的範圍隨著時間窗口增加而變窄, 表示投資組合在更長的時間範圍時表現更加穩定; 如果範圍隨著時間增加而變寬則表示隨著時間窗口拉長, 報酬率可能更加不穩定或波動變大. 

我將上面修改好的 plotting.py 與 timeseries.py 這兩個檔案放在 GitHub, 如果 Pandas 版本是 v2 以上的話, 只要將此兩檔案下載後複製到 Pyfolio 安裝目錄下覆蓋舊檔案即可 :


查詢 Pyfolio 安裝路徑的方法 :

import pyfolio as pf
print(pf.__file__)