2024年8月17日 星期六

Python 學習筆記 : 用 FinMind 套件取得股票資料 (一) 盤後資訊

過去四個月打完爬蟲之戰之後, 最近把注意力轉向 Pandas 與其在量化投資上的應用, 當然也是要藉此把過去買來卻束之高閣的書讀完啦. 這幾天在讀下面兩本書時, 發現除了之前使用的 yfinance 外, 還有 FinMind 與 FinLab 這兩個很不錯的公開 API 資料源, 兩者皆提供有條件的免費使用額度 : 

最強 AI 投資分析 (旗標, 施威銘, 2024)
# Python 股票 x ETF 量化交易 (博碩, 劉承彥, 2022)

本篇先來測試 FinMind 的 API, 此資料庫以台股為主, 包含個股盤後, 三大法人與融資融券等資料, 加入會員可獲得每小時 600 次免費請求額度 (不夠用的話再付費加入贊助方案), 參考 :





先來申請會員帳號 :


1. 註冊 FinMind 帳號 :   

連線 FinMind API 網址 : 


按右上角的 "登入/註冊" 超連結 :




按下方的 "註冊"  超連結 :




填寫 User ID, E-mail (只接受 Gmail, Yahoo 與 icloud 信箱) 與密碼按 "註冊" 鈕即可 :




FinMind 會寄一封認證信到信箱, 開啟後按 "VERIFY EMAIL" 鈕 :




認證成功會導引至登入頁面 :


登入成功後會顯示使用者資訊頁面 :




可見免費會員有每小時 600 次的 API 呼叫額度, 將 API Token 金鑰複製到記事本中保存備用. 也可以複製到工作目錄下的一個隱藏檔 .env 中並取一個名稱例如 FinMind_TOKEN, 這樣做可以避免範例程式分享到 GitHub 時不小心洩漏 :




使用時可用 dotenv 或 python-decouple 等模組取出特定名稱之金鑰, 例如 dotenv 使用時要搭配 os 模組, 鑰先匯入 os 模組與 dotenv.load_dotenv 函式 :

import os   
from dotenv import load_dotenv   

先用 load_dotenv() 載入環境變數後再用 os.environ.get() 取出指定名稱之金鑰 :

load_dotenv()    
token=os.environ.get('FinMind_TOKEN')   

參考 :



2. 安裝 FinMind 套件 :   

使用 pip install FinMind 指令安裝 :

D:\python\test>pip install FinMind    
Collecting FinMind
  Downloading FinMind-1.6.9-py3-none-any.whl.metadata (7.4 kB)
Requirement already satisfied: pandas>=1.1.5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (2.0.3)
Requirement already satisfied: numpy>=1.19.5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (1.24.3)
Requirement already satisfied: requests>=2.23.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (2.31.0)
Requirement already satisfied: pydantic>=1.6.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (2.5.3)
Collecting ta~=0.5.25 (from FinMind)
  Downloading ta-0.5.25.tar.gz (20 kB)
  Preparing metadata (setup.py) ... done
Collecting pytest>=6.2.2 (from FinMind)
  Downloading pytest-8.3.2-py3-none-any.whl.metadata (7.5 kB)
Requirement already satisfied: lxml>=4.6.3 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (4.9.3)
Collecting pyecharts>=1.9.0 (from FinMind)
  Downloading pyecharts-2.0.6-py3-none-any.whl.metadata (1.3 kB)
Requirement already satisfied: ipython>=7.16.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (8.15.0)
Collecting loguru>=0.5.3 (from FinMind)
  Downloading loguru-0.7.2-py3-none-any.whl.metadata (23 kB)
Requirement already satisfied: setuptools>=49.2.1 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from FinMind) (65.5.0)
Requirement already satisfied: aiohttp>=3.7.4.post0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (3.9.1)
Requirement already satisfied: flask>=2.0.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (2.3.3)
Requirement already satisfied: importlib-metadata>=4.8.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from FinMind) (6.8.0)
Requirement already satisfied: attrs>=17.3.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from aiohttp>=3.7.4.post0->FinMind) (23.1.0)
Requirement already satisfied: multidict<7.0,>=4.5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from aiohttp>=3.7.4.post0->FinMind) (6.0.4)
Requirement already satisfied: yarl<2.0,>=1.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from aiohttp>=3.7.4.post0->FinMind) (1.9.4)
Requirement already satisfied: frozenlist>=1.1.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from aiohttp>=3.7.4.post0->FinMind) (1.4.1)
Requirement already satisfied: aiosignal>=1.1.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from aiohttp>=3.7.4.post0->FinMind) (1.3.1)
Requirement already satisfied: async-timeout<5.0,>=4.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from aiohttp>=3.7.4.post0->FinMind) (4.0.3)
Requirement already satisfied: Werkzeug>=2.3.7 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from flask>=2.0.1->FinMind) (2.3.7)
Requirement already satisfied: Jinja2>=3.1.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from flask>=2.0.1->FinMind) (3.1.2)
Requirement already satisfied: itsdangerous>=2.1.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from flask>=2.0.1->FinMind) (2.1.2)
Requirement already satisfied: click>=8.1.3 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from flask>=2.0.1->FinMind) (8.1.7)
Requirement already satisfied: blinker>=1.6.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from flask>=2.0.1->FinMind) (1.6.2)
Requirement already satisfied: zipp>=0.5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from importlib-metadata>=4.8.1->FinMind) (3.17.0)
Requirement already satisfied: backcall in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (0.2.0)
Requirement already satisfied: decorator in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (5.1.1)
Requirement already satisfied: jedi>=0.16 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from ipython>=7.16.1->FinMind) (0.18.2)
Requirement already satisfied: matplotlib-inline in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (0.1.6)
Requirement already satisfied: pickleshare in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (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>=7.16.1->FinMind) (3.0.39)
Requirement already satisfied: pygments>=2.4.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (2.16.1)
Requirement already satisfied: stack-data in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (0.6.2)
Requirement already satisfied: traitlets>=5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (5.9.0)
Requirement already satisfied: exceptiongroup in c:\users\tony1\appdata\roaming\python\python310\site-packages (from ipython>=7.16.1->FinMind) (1.1.3)
Requirement already satisfied: colorama in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from ipython>=7.16.1->FinMind) (0.4.6)
Collecting win32-setctime>=1.0.0 (from loguru>=0.5.3->FinMind)
  Downloading win32_setctime-1.1.0-py3-none-any.whl.metadata (2.3 kB)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas>=1.1.5->FinMind) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas>=1.1.5->FinMind) (2023.3)
Requirement already satisfied: tzdata>=2022.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pandas>=1.1.5->FinMind) (2023.3)
Requirement already satisfied: annotated-types>=0.4.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pydantic>=1.6.1->FinMind) (0.5.0)
Requirement already satisfied: pydantic-core==2.14.6 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pydantic>=1.6.1->FinMind) (2.14.6)
Requirement already satisfied: typing-extensions>=4.6.1 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pydantic>=1.6.1->FinMind) (4.9.0)
Collecting prettytable (from pyecharts>=1.9.0->FinMind)
  Downloading prettytable-3.11.0-py3-none-any.whl.metadata (30 kB)
Collecting simplejson (from pyecharts>=1.9.0->FinMind)
  Downloading simplejson-3.19.3-cp310-cp310-win_amd64.whl.metadata (3.2 kB)
Collecting iniconfig (from pytest>=6.2.2->FinMind)
  Downloading iniconfig-2.0.0-py3-none-any.whl.metadata (2.6 kB)
Requirement already satisfied: packaging in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pytest>=6.2.2->FinMind) (23.1)
Collecting pluggy<2,>=1.5 (from pytest>=6.2.2->FinMind)
  Downloading pluggy-1.5.0-py3-none-any.whl.metadata (4.8 kB)
Requirement already satisfied: tomli>=1 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from pytest>=6.2.2->FinMind) (2.0.1)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests>=2.23.0->FinMind) (3.2.0)
Requirement already satisfied: idna<4,>=2.5 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests>=2.23.0->FinMind) (3.4)
Requirement already satisfied: urllib3<3,>=1.21.1 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from requests>=2.23.0->FinMind) (1.26.19)
Requirement already satisfied: certifi>=2017.4.17 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from requests>=2.23.0->FinMind) (2023.7.22)
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>=7.16.1->FinMind) (0.8.3)
Requirement already satisfied: MarkupSafe>=2.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from Jinja2>=3.1.2->flask>=2.0.1->FinMind) (2.1.3)
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>=7.16.1->FinMind) (0.2.6)
Requirement already satisfied: six>=1.5 in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from python-dateutil>=2.8.2->pandas>=1.1.5->FinMind) (1.16.0)
Requirement already satisfied: executing>=1.2.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from stack-data->ipython>=7.16.1->FinMind) (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>=7.16.1->FinMind) (2.2.1)
Requirement already satisfied: pure-eval in c:\users\tony1\appdata\roaming\python\python310\site-packages (from stack-data->ipython>=7.16.1->FinMind) (0.2.2)
Downloading FinMind-1.6.9-py3-none-any.whl (60 kB)
Downloading loguru-0.7.2-py3-none-any.whl (62 kB)
Downloading pyecharts-2.0.6-py3-none-any.whl (149 kB)
Downloading pytest-8.3.2-py3-none-any.whl (341 kB)
Downloading pluggy-1.5.0-py3-none-any.whl (20 kB)
Downloading win32_setctime-1.1.0-py3-none-any.whl (3.6 kB)
Downloading iniconfig-2.0.0-py3-none-any.whl (5.9 kB)
Downloading prettytable-3.11.0-py3-none-any.whl (28 kB)
Downloading simplejson-3.19.3-cp310-cp310-win_amd64.whl (75 kB)
Building wheels for collected packages: ta
  Building wheel for ta (setup.py) ... done
  Created wheel for ta: filename=ta-0.5.25-py3-none-any.whl size=24873 sha256=69a2fe129c272c3311e7e8d4901588f3879c96f19fae839ff215e5fb37a4b56f
  Stored in directory: c:\users\tony1\appdata\local\pip\cache\wheels\d4\bd\c4\0dda911b5461ee856352cd8c9e16472229bb51c95e74b74c08
Successfully built ta
Installing collected packages: win32-setctime, simplejson, prettytable, pluggy, iniconfig, pytest, pyecharts, loguru, ta, FinMind
Successfully installed FinMind-1.6.9 iniconfig-2.0.0 loguru-0.7.2 pluggy-1.5.0 prettytable-3.11.0 pyecharts-2.0.6 pytest-8.3.2 simplejson-3.19.3 ta-0.5.25 win32-setctime-1.1.0

檢視版本 :

>>> import FinMind   
>>> FinMind.__version__   
'1.6.9'   

因為呼叫 FinMind API 會傳回 Pandas 的 DataFrame, 而在 Colab 中顯示 DataFrame 較美觀, 所以我也在 Colab 安裝 FinMind, 只是 pip 指令前須加驚嘆號 ! : 

!pip install FinMind 




用 dir() 檢視 FinMind 套件 :

>>> dir(FinMind)    
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_version', 'crawler', 'data', 'indicators', 'schema', 'strategies', 'utility']

用自訂模組 Members 的 list_members() 檢視 FinMind 套件 : 

>>> list_members(FinMind)   
crawler <class 'module'>
data <class 'module'>
indicators <class 'module'>
schema <class 'module'>
strategies <class 'module'>
utility <class 'module'>

可見此套件由 6 個模組構成, 呼叫 FinMind API 用到的是 data 模組下的 DataLoader 類別 :

>>> dir(FinMind.data)  
['DataLoader', 'DataSubscriber', 'FinMindApi', 'FutureAndOption', 'Stock', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'data_loader', 'data_subscriber', 'finmind_api']   
>>> list_members(FinMind.data)        
DataLoader <class 'type'>
DataSubscriber <class 'type'>
FinMindApi <class 'type'>
FutureAndOption <class 'enum.EnumMeta'>
Stock <class 'enum.EnumMeta'>
data_loader <class 'module'>
data_subscriber <class 'module'>
finmind_api <class 'module'>


3. 建立 DataLoader 物件呼叫 API :   

呼叫 FinMind API 需建立 DataLoader 物件, 先從 FinMind.data 模組匯入 DataLoader 類別 :

from FinMind.data import DataLoader   

然後呼叫其建構子建立 DataLoader 物件 : 

data_loader=DataLoader()     

例如 :

>>> from FinMind.data import DataLoader    
>>> data_loader=DataLoader()      
>>> type(data_loader)    
<class 'FinMind.data.data_loader.DataLoader'>     

用自訂模組 Members 的 list_members() 檢視 DataLoader 物件 : 

>>> list_members(data_loader)   
api_version <class 'str'>
feature <class 'FinMind.data.data_loader.Feature'>
get_data <class 'method'>
get_datalist <class 'method'>
get_taiwan_futures_snapshot <class 'method'>
get_taiwan_options_snapshot <class 'method'>
get_taiwan_stock_tick_snapshot <class 'method'>
login <class 'method'>
login_by_token <class 'method'>
taiwan_daily_short_sale_balances <class 'method'>
taiwan_futopt_daily_info <class 'method'>
taiwan_futopt_tick_info <class 'method'>
taiwan_futopt_tick_realtime <class 'method'>
taiwan_futures_daily <class 'method'>
taiwan_futures_dealer_trading_volume_daily <class 'method'>
taiwan_futures_institutional_investors <class 'method'>
taiwan_futures_snapshot <class 'method'>
taiwan_futures_tick <class 'method'>
taiwan_option_daily <class 'method'>
taiwan_option_dealer_trading_volume_daily <class 'method'>
taiwan_option_institutional_investors <class 'method'>
taiwan_option_tick <class 'method'>
taiwan_options_snapshot <class 'method'>
taiwan_securities_trader_info <class 'method'>
taiwan_stock_10year <class 'method'>
taiwan_stock_balance_sheet <class 'method'>
taiwan_stock_bar <class 'method'>
taiwan_stock_book_and_trade <class 'method'>
taiwan_stock_capital_reduction_reference_price <class 'method'>
taiwan_stock_cash_flows_statement <class 'method'>
taiwan_stock_convertible_bond_daily <class 'method'>
taiwan_stock_convertible_bond_daily_overview <class 'method'>
taiwan_stock_convertible_bond_info <class 'method'>
taiwan_stock_convertible_bond_institutional_investors <class 'method'>
taiwan_stock_daily <class 'method'>
taiwan_stock_daily_adj <class 'method'>
taiwan_stock_day_trading <class 'method'>
taiwan_stock_delisting <class 'method'>
taiwan_stock_dividend <class 'method'>
taiwan_stock_dividend_result <class 'method'>
taiwan_stock_financial_statement <class 'method'>
taiwan_stock_government_bank_buy_sell <class 'method'>
taiwan_stock_holding_shares_per <class 'method'>
taiwan_stock_info <class 'method'>
taiwan_stock_info_with_warrant <class 'method'>
taiwan_stock_institutional_investors <class 'method'>
taiwan_stock_institutional_investors_total <class 'method'>
taiwan_stock_margin_purchase_short_sale <class 'method'>
taiwan_stock_margin_purchase_short_sale_total <class 'method'>
taiwan_stock_margin_short_sale_suspension <class 'method'>
taiwan_stock_market_value <class 'method'>
taiwan_stock_month_revenue <class 'method'>
taiwan_stock_news <class 'method'>
taiwan_stock_per_pbr <class 'method'>
taiwan_stock_securities_lending <class 'method'>
taiwan_stock_shareholding <class 'method'>
taiwan_stock_tick <class 'method'>
taiwan_stock_tick_snapshot <class 'method'>
taiwan_stock_total_return_index <class 'method'>
taiwan_total_exchange_margin_maintenance <class 'method'>
translation <class 'method'>
tse <class 'method'>
us_stock_info <class 'method'>
us_stock_price <class 'method'>

這些 DataLoader 物件的方法就是所謂的 API, 使用者可以透過呼叫這些方法從 FinMind 資料庫下載台股資訊. 目前 FinMind 總共提供 55 種台股資料集可供查詢, 含括基本面, 技術面, 籌碼面, 新聞面, 與即時資料等, 每種資料集的 API 用法參考 :


常用的 API 摘要如下表 : 


 DataLoader 物件常用方法 說明
 login_by_token() 傳入金鑰登入 FinMind 帳戶, 參數 : api_token
 taiwan_stock_daily() 台股收盤資料, 參數 : stock_id, start_date, end_date
 taiwan_stock_daily_adj() 還原權息股價, 參數 : stock_id, start_date, end_date
 taiwan_stock_financial_statement() 個股損益表, 參數 : stock_id, start_date
 taiwan_stock_cash_flows_statement() 個股現金流量表, 參數 : stock_id, start_date
 taiwan_stock_balance_sheet() 個股資產負債表, 參數 : stock_id, start_date
 taiwan_stock_institutional_invenstors() 法人進出資訊, 參數 : stock_id, start_date, end_date
 taiwan_stock_shareholding() 外資持股表, 參數 : stock_id, start_date, end_date
 taiwan_stock_month_revenue() 個股月營收表, 參數 : stock_id, start_date
 taiwan_stock_info() 台股總覽, 參數 : 無


其中最常用的是查詢每日收盤資料的 API : taiwan_stock_daily() 與 taiwan_stock_daily_adj(), 差別是後者的收盤價是還原權息的. 不過 taiwan_stock_daily_adj() 這個 API 並未開放給免費帳戶使用, 必須付費加入贊助帳戶才能呼叫. 

但是, 呼叫這些 API 前必須先呼叫 login_by_token() 方法並傳入金鑰 (參數 api_token) 登入帳戶才行. 我們先利用 dotenv 模組從 .env 檔取出金鑰 :

>>> import os   
>>> from dotenv import load_dotenv   
>>> load_dotenv()   
True   
>>> token=os.environ.get('FinMind_TOKEN')    
>>> print(token)   
'金鑰在此'    

然後呼叫 DataLoader 物件的 login_by_token() 並傳入關鍵字參數 api_token=金鑰進行認證 :

>>> data_loader.login_by_token(api_token=token)   

參考官方文件 :


如果沒有出現錯誤表示登入成功 (傳回 None), 這樣就可以呼叫其他 API 了, 例如呼叫 taiwan_stock_daily() 可取得盤後資料, 此方法須傳入三個參數 :
  • stock_id : 股票代號 (字串), 例如 '0050'
  • start_date : 起始日期, 可以是日期字串例如 '2024-08-17' 或 datatime 物件
  • end_date : 結束日期, 可以是日期字串例如 '2024-08-17' 或 datatime 物件
例如取得近一個月的日收盤資料, 起訖日期可以直接傳入日期字串 '2024-07-18' 與 '2024-08-17', 也可以傳入 datetime 物件, 下面用 datime 模組的 date 與 timedelta 類別來處理起訖日期 : 

>>> from datetime import date, timedelta     
>>> stock_id='0050'     
>>> end_date=date.today()      
>>> end_date    
datetime.date(2024, 8, 17)   
>>> start_date=end_date - timedelta(days=30)      # 前 30 日之 datetime 物件
>>> start_date      
datetime.date(2024, 7, 18)  

將此三個參數傳入 taiwan_stock_daily() 會傳回指定區間之每日盤後資料的 DataFrame :

>>> data=data_loader.taiwan_stock_daily( 
    stock_id=stock_id, 
    start_date=start_date, 
    end_date=end_date)  
2024-08-17 23:48:34.329 | INFO     | FinMind.data.finmind_api:get_data:125 - download TaiwanStockPrice, data_id: 0050
>>> type(data)   
<class 'pandas.core.frame.DataFrame'>
>>> data  
          date stock_id  Trading_Volume  ...   close  spread  Trading_turnover
0   2024-07-18     0050        19796818  ...  190.60   -3.50             39463
1   2024-07-19     0050        18436231  ...  186.25   -4.35             43183
2   2024-07-22     0050        23474914  ...  180.70   -5.55             48514
3   2024-07-23     0050        13180830  ...  186.30    5.60             18100
4   2024-07-26     0050        29164975  ...  179.00   -7.30             61517
5   2024-07-29     0050        10574343  ...  180.60    1.60             16228
6   2024-07-30     0050        14007149  ...  180.60    0.00             22564
7   2024-07-31     0050         9530148  ...  180.85    0.25             12645
8   2024-08-01     0050        14525195  ...  184.00    3.15             14649
9   2024-08-02     0050        32816025  ...  174.70   -9.30             68966
10  2024-08-05     0050        61002329  ...  158.75  -15.95            111467
11  2024-08-06     0050        65342352  ...  167.50    8.75             54758
12  2024-08-07     0050        32751044  ...  174.15    6.65             29003
13  2024-08-08     0050        26852782  ...  170.55   -3.60             29484
14  2024-08-09     0050        24121860  ...  175.85    5.30             21088
15  2024-08-12     0050        18188036  ...  178.05    2.20             18552
16  2024-08-13     0050        11257895  ...  178.50    0.45             12862
17  2024-08-14     0050        19282599  ...  180.75    2.25             16415
18  2024-08-15     0050        12003834  ...  179.35   -1.40             16847
19  2024-08-16     0050        13980972  ...  183.40    4.05             17837

[20 rows x 10 columns]

檢視 DataFrame 的 columns 屬性 (欄位) 可知它有 10 個欄位 : 

>>> data.columns   
Index(['date', 'stock_id', 'Trading_Volume', 'Trading_money', 'open', 'max', 'min', 'close', 'spread', 'Trading_turnover'], dtype='object')

這比 yfinance 傳回的欄位還多, 但要進行技術分析其實只要 "OHLCV (開高低收量)" 這 5 個欄位即可. 其次, 最高價與最低價用的是 max 與 min, 而非常見的 high 與 low. 另外, 欄位名稱大部分是小寫開頭, 所以如果要用 backtest 做回測的話須調整為大寫字母開頭 (像 yfinance 那樣). 

這 10 個欄位說明如下表 : 


 欄位 說明
 date 日期 (字串)
 stock_id 股票代號 (整數)
 Trading_Volumn 成交股數 (整數)
 Trading_money 成交金額 (字串)
 open 開盤價 (浮點數)
 max 最高價 (浮點數)
 min 最低價 (浮點數)
 close 收盤價 (浮點數)
 spread 漲跌價 (浮點數)
 Trading_turnover 成交筆數 (整數)


用 info() 方法查詢各欄位資料型態 :

>>> data.info()    
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 10 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   date              20 non-null     object 
 1   stock_id          20 non-null     object 
 2   Trading_Volume    20 non-null     int64  
 3   Trading_money     20 non-null     int64  
 4   open              20 non-null     float64
 5   max               20 non-null     float64
 6   min               20 non-null     float64
 7   close             20 non-null     float64
 8   spread            20 non-null     float64
 9   Trading_turnover  20 non-null     int64  
dtypes: float64(5), int64(3), object(2)
memory usage: 1.7+ KB


這時去查看 FinMind 的使用者資訊, 會顯示已使用 API 呼叫 1 次 :




免費帳戶如果呼叫 taiwan_stock_daily_adj() 會 400 錯誤訊息 :

>>> data=data_loader.taiwan_stock_daily_adj( 
    stock_id='0050', 
    start_date='2024-07-18', 
    end_date='2024-08-17') 
2024-08-18 15:05:03.943 | INFO     | FinMind.data.finmind_api:get_data:125 - download TaiwanStockPriceAdj, data_id: 0050
2024-08-18 15:05:04.089 | ERROR    | FinMind.data.finmind_api:request_get:43 - {'dataset': <Dataset.TaiwanStockPriceAdj: 'TaiwanStockPriceAdj'>, 'data_id': '0050', 'stock_id': '', 'start_date': '2024-07-18', 'end_date': '2024-08-17', 'user_id': '', 'password':
... (略) ...
Exception: {"msg":"Your level is register. Please update your user level. Detail information:https://finmindtrade.com/analysis/#/Sponsor/sponsor","status":400}

此 API 未開放給免費帳戶使用, 需付費成為贊助帳戶才能呼叫. 


4. 繪製 K 線圖 :   

接下來我們可以用 mplfinance 套件來繪製上面從 FinMind 取得的盤後資料之 K 線圖, 但在繪圖之前必須對傳回的 DataFrame 物件做一些調整以符合 mplfinance 套件之要求. 

首先 mplfinance 要求做為 X 軸的 date 欄位之資料型態必須是 Timestamp 型別, 而且 date 欄位必須設為列索引, 如上所示 FinMind 傳回的 DataFrame 之 date 欄位型態為字串 (Object), 因此必須利用 Pandas 的 to_datetime() 函式將 date 欄位由日期字串 'YYYY-mm-dd' 轉成一個 Timestamp 物件 : 

>>> data['date']=pd.to_datetime(data['date'])  
>>> type(data['date'][0])  
<class 'pandas._libs.tslibs.timestamps.Timestamp'>

然後呼叫 DataFrame 物件的 set_index() 方法將其設為列索引 (index) :

>>> data=data.set_index(data['date'])    

其次是 mplfinance 要求 OHLCV 欄位名稱必須為首字母大寫 Open, High, Low, Close, 與 Volumn (即與 yfinance 的欄位名稱一致) 才能繪製 K 線圖, 這可以用 DataFrame 物件的 rename() 方法並傳入前後欄位名稱組成的字典給 columns 參數來修改 : 

>>> columns={'open': 'Open', 
         'max': 'High', 
         'min': 'Low', 
         'close': 'Close', 
         'Trading_Volume': 'Volume'}     # 舊: 新欄位名稱對映字典
>>> data=data.rename(columns=columns)   
>>> data 
                 date stock_id  Trading_Volume  Trading_money    Open    High     Low   Close  spread  Trading_turnover
date                                                                                                                   
2024-07-18 2024-07-18     0050        19796818     3754498885  188.60  190.90  188.20  190.60   -3.50             39463
2024-07-19 2024-07-19     0050        18436231     3456363627  188.75  189.00  186.20  186.25   -4.35             43183
2024-07-22 2024-07-22     0050        23474914     4268806758  185.40  185.40  180.05  180.70   -5.55             48514
2024-07-23 2024-07-23     0050        13180830     2437876621  183.70  186.30  183.70  186.30    5.60             18100
2024-07-26 2024-07-26     0050        29164975     5210021472  178.05  179.45  177.10  179.00   -7.30             61517
2024-07-29 2024-07-29     0050        10574343     1915739406  181.80  182.00  179.95  180.60    1.60             16228
2024-07-30 2024-07-30     0050        14007149     2507019649  179.25  181.30  177.50  180.60    0.00             22564
2024-07-31 2024-07-31     0050         9530148     1715165757  178.85  181.15  178.60  180.85    0.25             12645
2024-08-01 2024-08-01     0050        14525195     2671260519  185.00  185.10  182.70  184.00    3.15             14649
2024-08-02 2024-08-02     0050        32816025     5798178503  178.15  178.90  174.70  174.70   -9.30             68966
2024-08-05 2024-08-05     0050        61002329     9893076101  166.65  166.65  158.40  158.75  -15.95            111467
2024-08-06 2024-08-06     0050        65342352    10883521675  167.30  170.40  160.80  167.50    8.75             54758
2024-08-07 2024-08-07     0050        32751044     5650132811  169.85  174.75  169.00  174.15    6.65             29003
2024-08-08 2024-08-08     0050        26852782     4585081468  171.05  172.40  169.00  170.55   -3.60             29484
2024-08-09 2024-08-09     0050        24121860     4242663929  175.00  176.90  174.50  175.85    5.30             21088
2024-08-12 2024-08-12     0050        18188036     3246767115  177.25  179.80  177.25  178.05    2.20             18552
2024-08-13 2024-08-13     0050        11257895     2010945566  179.30  179.50  178.00  178.50    0.45             12862
2024-08-14 2024-08-14     0050        19282599     3487274230  180.35  181.85  179.95  180.75    2.25             16415
2024-08-15 2024-08-15     0050        12003834     2160883722  180.60  181.00  179.05  179.35   -1.40             16847
2024-08-16 2024-08-16     0050        13980972     2561360342  183.00  183.65  182.35  183.40    4.05             17837
 
接下來就可以用 mplfinance 來繪製 K 線圖了, 首先要匯入 mplfinance 套件 :

import mplfinance as mpf   

然後呼叫其 plot() 函式並傳入具有 "開高低收量 (OHLCV)" 欄位的 DataFrame 即可 :

mpf.plot(data [, type='candle', title, style=style, volume=True, mav=None, xrotation=0, savefig])   

參數說明如下 :
  • type : K 線類型, 台日股市的蠟燭圖 (陰陽線) 要設為 'candle'.
  • style : 用來指定自訂樣式物件.
  • volume : 用來設定是否要在 K 線圖下方同時顯示成交量 (預設為 False). 
  • nav : 用來設定要顯示幾日均線, 可傳入整數 (例如 3) 或整數串列 (例如 [3, 5, 7])
  • xrotation : 日期 (X 軸 date 欄位) 旋轉角度, 預設 0
  • savefig : 儲存之 png 檔名 (含路徑)
由於 mplfinance 預設樣式為美式 K 線, 所以需要用自訂樣式來調整為台股樣式, 首先要將 K 棒的漲跌顏色設成漲紅跌綠 :

>>> color=mpf.make_marketcolors(up='red', down='green', inherit=True)

然後為了讓標題可顯示中文, 設定字型物件為微軟正黑體 :

>>> font={'font.family': 'Microsoft JhengHei'}   

然後將 color 與 font 傳入 make_mpf_style() 函式來建立自訂樣式物件 :

>>> style=mpf.make_mpf_style(base_mpf_style='default', marketcolors=color, rc=font)  

這樣就可以呼叫 plot() 來繪製 K 線圖了 : 

>>> mpf.plot(data, type='candle', title='台灣五十(0050)', style=style)   

這樣就會畫出 K 線圖了 :




傳入 mav=[3, 5, 7] 可在 K 線圖上繪製 3, 5 7 日均線; 傳入 volume=True 會在底下添加成交量值條圖, 例如 :

>>> mpf.plot(data, type='candle', title='台灣五十(0050)', style=style, mav=[3, 5, 7], volume=True)  




關於 mplfinance 用法參考 :


沒有留言 :