- 登入
- 註冊
上一篇文章,我們介紹該如何進行元富證券「下單」跟「行情」API 的認證。
那在本篇,我們要來說明如何使用元富 API 抓取歷史的成交資料,以及劃出 K 線圖。
首先請到元富 API 專區的網頁,並且在「下載專區」的「技術指標」欄位中,下載「Python」 的技術指標套件。
請注意,因為目前技術指標的套件,只支援 32 bit 的 Python,所以我們需要先額外設定環境。
套件下載並解壓縮後,將檔案放到我們的工作目錄底下,並用 VS Code 開啟,記得在左上角先打開 Terminal,因為稍後要輸入指令。
上面步驟完成後,接著先切換到目錄底下: cd .\Python_tech_analysis\
再來需要輸入以下指令:
指令輸入完成後,這時我們左邊的目錄應該會跟下圖所示一樣:
接著找到「venv」的資料夾,會看到同一資料夾底下有「example.py」跟一個「.whl」檔案,接著請依照下列指示安裝套件:
套件安裝完成後,這樣我們就完成環境的建置。
完成環境建置後,接著我們要使用官方的範例,來測試資料的抓取。
第一步開啟「example.py」檔案,這一部分也可以參考元富的官網說明。
接著在「main function」內,修改自己的帳號密碼(黃色塗鴉處)。
並且將「股票代號 2330」後面的日期,改成昨天儲存之後,執行此程式。如果這時候你在「Terminal」看到如下圖的畫面,這樣就表示執行成功。
接下來的步驟,我們會使用以下所撰寫的程式,透過元富 API 撈取台積電的股價資料,並繪製成 K 線圖。
如果這部分看不懂的話,也可以直接使用文章最後的完整程式碼,只要複製貼上,並且儲存後執行即可。
首先我們需要額外安裝一些 Python 套件:
註:「numpy」是 python 做資料處理時,很常用且重要的套件;「mplfinance」則是方便我們繪圖用的套件。
套件安裝完成後,接下來我們在同一個資料夾裡,新增一個新的檔案,至於檔案名稱則隨意,這邊是命名為「historyDataAndPlot.py」並貼入下列程式碼:
from tech_analysis_api_v2.api import TechAnalysis
from tech_analysis_api_v2.model import *
import threading
import pandas as pd
from datetime import datetime, timedelta
import mplfinance as mpf
def OnDigitalSSOEvent(aIsOK, aMsg):
print(f'OnDigitalSSOEvent: {aIsOK} {aMsg}')
def OnTAConnStuEvent(aIsOK):
print(f'OnTAConnStuEvent: {aIsOK}')
if aIsOK:
event.set()
def OnUpdate(ta_Type: eTA_Type, aResultPre, aResultLast):
if aResultPre != None:
if ta_Type == eTA_Type.SMA:
print(f'前K {str(aResultPre)}')
if ta_Type == eTA_Type.EMA:
print(f'前K {str(aResultPre)}')
if ta_Type == eTA_Type.WMA:
print(f'前K {str(aResultPre)}')
if ta_Type == eTA_Type.SAR:
print(f'前K {str(aResultPre)}')
if ta_Type == eTA_Type.RSI:
print(f'前K {str(aResultPre)}')
if ta_Type == eTA_Type.MACD:
print(f'前K {str(aResultPre)}')
if ta_Type == eTA_Type.KD:
print(f'前K {str(aResultPre)}')
if ta_Type == eTA_Type.CDP:
print(f'前K {str(aResultPre)}')
if aResultLast != None:
if ta_Type == eTA_Type.SMA:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, SMA:{aResultLast.Value}')
if ta_Type == eTA_Type.EMA:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, EMA:{aResultLast.Value}')
if ta_Type == eTA_Type.WMA:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, EMA:{aResultLast.Value}')
if ta_Type == eTA_Type.SAR:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, SAR:{aResultLast.SAR}, EPh:{aResultLast.EPh}, EPl:{aResultLast.EPl}, AF:{aResultLast.AF}, RaiseFall:{aResultLast.RaiseFall}')
if ta_Type == eTA_Type.RSI:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, RSI:{aResultLast.RSI}, UpDn:{aResultLast.UpDn}, UpAvg:{aResultLast.UpAvg}, DnAvg:{aResultLast.DnAvg}')
if ta_Type == eTA_Type.MACD:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, DIF:{aResultLast.DIF}, OSC:{aResultLast.OSC}')
if ta_Type == eTA_Type.KD:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, K:{aResultLast.K}, D:{aResultLast.D}')
if ta_Type == eTA_Type.CDP:
print(f'最新 Time:{aResultLast.KBar.TimeSn_Dply}, CDP:{aResultLast.CDP}, AH:{aResultLast.AH}, NH:{aResultLast.NH}, AL:{aResultLast.AL}, NL:{aResultLast.NL}')
def OnRcvDone(ta_Type: eTA_Type, aResult):
if ta_Type == eTA_Type.SMA:
for x in aResult:
print(f'回補 {x}')
if ta_Type == eTA_Type.EMA:
for x in aResult:
print(f'回補 {x}')
if ta_Type == eTA_Type.WMA:
for x in aResult:
print(f'回補 {x}')
if ta_Type == eTA_Type.SAR:
for x in aResult:
print(f'回補 {x}')
if ta_Type == eTA_Type.RSI:
for x in aResult:
print(f'回補 {x}')
if ta_Type == eTA_Type.MACD:
for x in aResult:
print(f'回補 {x}')
if ta_Type == eTA_Type.KD:
for x in aResult:
print(f'回補 {x}')
if ta_Type == eTA_Type.CDP:
for x in aResult:
print(f'回補 {x}')
def option():
ProdID = input("商品代號: ")
SNK = input("分K(1/3/5): ")
STA_Type = input("指標(SMA/EMA/WMA/SAR/RSI/MACD/KD/CDP): ")
DateBegin = input("日期(ex: 20230619): ")
NK = eNK_Kind.K_1m
if SNK == '1':
NK = eNK_Kind.K_1m
elif SNK == '3':
NK = eNK_Kind.K_3m
elif SNK == '5':
NK = eNK_Kind.K_5m
TA_Type = eTA_Type.SMA
if STA_Type == 'SMA':
TA_Type = eTA_Type.SMA
elif STA_Type == 'EMA':
TA_Type = eTA_Type.EMA
elif STA_Type == 'WMA':
TA_Type = eTA_Type.WMA
elif STA_Type == 'SAR':
TA_Type = eTA_Type.SAR
elif STA_Type == 'RSI':
TA_Type = eTA_Type.RSI
elif STA_Type == 'MACD':
TA_Type = eTA_Type.MACD
elif STA_Type == 'KD':
TA_Type = eTA_Type.KD
elif STA_Type == 'CDP':
TA_Type = eTA_Type.CDP
return TechAnalysis.get_k_setting(ProdID, TA_Type, NK, DateBegin)
event = threading.Event()
def fetch_historical_data(prod_id='2330', date='20231115'):
"""
將帳號密碼取代為你的帳號密碼,
填入商品代號與日期,抓取指定日期的歷史成交資料。
"""
ta = TechAnalysis(OnDigitalSSOEvent, OnTAConnStuEvent, OnUpdate, OnRcvDone)
ta.Login('{{你的身分證字號}}', '{{你的密碼}}') # Replace with your credentials
event.wait()
lsBS, sErrMsg = ta.GetHisBS_Stock(prod_id, date)
if sErrMsg:
print(f"Error: {sErrMsg}")
return None
# Converting data to DataFrame
data = [{
'ProdID': x.Prod,
'Match_Time': x.Match_Time,
'Match_Price': x.Match_Price,
'Match_Quantity': x.Match_Quantity,
'Is_TryMatch': x.Is_TryMatch,
'BS': x.BS
} for x in lsBS]
df = pd.DataFrame(data)
# 應用轉換
df['Formatted_Time'] = df['Match_Time'].apply(convert_time)
return df
def convert_time(time_val):
# 先轉成 String比較好處理 因為資料是回復一串數字
time_str = str(time_val)
time_str = time_str[:6]
if '.' in time_str:
time_str = time_str.replace('.', '')
time_str = '0' + time_str
# 解析時間 小時、分鐘、秒
hours = int(time_str[:2])
minutes = int(time_str[2:4])
seconds = int(time_str[4:6])
# 返回datetime物件
return datetime(2023, 11, 15, hours, minutes, seconds)
def aggregate_to_ohlc(df, time_frame='5T'):
"""
將給定的DataFrame轉換為指定時間幀的OHLC數據,並使用最後一個有效時間點的數據填充空白時間段。
參數:
df -- 原始的DataFrame。
time_frame -- 要聚合的時間幀,默認為'5T'(五分鐘)。
返回:
轉換後的DataFrame,包含OHLC和總交易量。
"""
df['Formatted_Time'] = pd.to_datetime(df['Formatted_Time'])
df.set_index('Formatted_Time', inplace=True)
# 定義聚合成OHLC的規則
ohlc_dict = {
'Match_Price': 'ohlc',
'Match_Quantity': 'sum'
}
# 聚合資料
df_ohlc = df.resample(time_frame).apply(ohlc_dict)
df_ohlc.columns = df_ohlc.columns.droplevel(0) # 移除多级列名
# 13:25 並不會有交易,所以使用前一筆的資料填補
df_ohlc.fillna(method='pad', inplace=True)
return df_ohlc
def main():
df = fetch_historical_data()
if df is not None:
df_ohlc = aggregate_to_ohlc(df)
print("---------印出OHLC資料---------")
print(df_ohlc)
if df_ohlc is not None:
# 確保你的 DataFrame 索引是日期時間型別(datetime)
df_ohlc.index = pd.to_datetime(df_ohlc.index)
# 交易量欄位重新命名為 volume (mplfinance 預設欄位名稱)
df_ohlc.rename(columns={'Match_Quantity': 'volume'}, inplace=True)
mpf.plot(df_ohlc, type='candle', style='charles',
title='2330', volume=True)
main()
其實前面大部分的程式碼,都跟之前的範例檔案相同,但在這裡我們重寫抓取歷史資料的部分(fetch_historical_data),並且將其資料整理(aggregate_to_ohlc)成可以畫成 K 線圖的格式(須要有OHLC資料),然後在輸出成圖表(mpf.plot)。
所以在「terminal」那邊輸入以下執行程式時,你應該會看到如下圖的輸出:
python historyDataAndPlot.py
這樣就代表我們成功抓取資料,並且畫出 K 線圖囉,那麼本章就介紹到這邊!