群益API行情串接(一)


像股票這樣的金融商品百百種,加上市場的變化性與多樣的市場規則,使得這類的分析總是相當困難。但相關數據取得與蒐集的管道也相當稀少,甚至難以整合與上手。國內幾家券商提供之API服務便顯得相當地重要。

前言

在金融分析這塊,先不論許多金融商品的歷史資料本身就很少,在網路上能查到的資料多數是未結構化資料,或格式相當複雜,致使難以進行整理。事實上,如果需要詳細資料的話,比方說比日資料時間更短的Tick Data,除了向交易所購買資料之外,使用API服務便是另一種更方便的選擇。

然而,使用API服務,你得有一個心理準備,由於各家券商提供之功能與API伺服器環境等因素不同,接收資料可能會有不穩定或格式不合個人常使用之格式,所以針對這些情況,個人得自己動態做調整。此外,一般API也會需要隨著時間進行更新,可能隨時都會發布新版本,並停用某些舊版本。有些券商通知更換新版本或淘汰舊版本的方式也不同,有很多時候,可能不會即時知道,得要偶爾去確認。並且,有些時候,新版本並不一定穩定,所以若當前使用穩定,或不急於使用新增功能,可以觀察或測試新版本的情況後,再正式換版。

一般而言,在各券商官方網站上會有許多豐富的資訊,若需使用API,需先向券商進行申請,之後在去進行下載元件、設定與註冊、直接使用官方範例程式或自行編寫程式等等。

而若是使用API下單,除了會需要帳號密碼登入之外,也會需要進行註冊憑證,一般憑證都會有使用期限,可以延長或重新申請。這兩者搭配才能正常使用下單功能。使用報價等未實際進行交易的功能一般則不需要憑證。

本篇主要提及群益金鼎證券提供之API服務,其提供的官方說明文件是目前我使用過最詳細(且複雜)的,有著許許多多的功能函式,但相對而言卻也造成在使用上難以快速上手。但其有提供當天Tick回補功能,因此如果不小心在取得即時資訊時遺漏或出現問題時,能夠輕鬆透過此功能進行補足。當然,也能夠用來一次性在盤後進行資料請求,將當天Tick資料進行儲存。

本篇主要介紹先備知識,並且完成元件註冊與帳號連線。下一篇將會繼續介紹獲取當日Tick回補的功能實現,我們將會使用到Class,並註冊與管理事件。


程式環境

程式語言: Python 3.6.8

作業系統: Windows 10 64位元


元件註冊

群益API的元件,在去官方網站下載後,其會附一個非常詳盡的官方說明文件,基本上照著做就沒問題,上面也有一些疑難排解問題的說明。

其元件是ActiveX元件,是一個基於COM標準的檔案。也因此,通常安裝與使用環境是在Windows系統上,而這個元件,官方目前則推薦使用Windows 10版本。

所以這個元件也會需要進行註冊(登入到電腦登入檔)才能夠正式使用。

在Windows作業系統上,COM元件註冊的方式除了手動進行在登錄檔修改之外,一般是使用內建的regsvr32.exe進行註冊以及解除註冊。

相關指令在之前元大API即時行情串接系列中有提到,因此這邊就不詳細討論,若不想了解詳細指令,可以跳過這個步驟,只需要按照官方的安裝步驟開啟bat批次檔,基本上就能直接開始使用。

其提供之元件有支援64位元版本,得考慮作業系統位元版本等需求進行安裝,基本上x86與x64版本功能都會一樣,但由於相容性問題,若使用x86版本的元件,python也會需要安裝32位元版本。

且由於目前我使用的元件為2.13.16 x86版本,所以本篇會以這個版本為主。而其登入機制正好在隨後發佈的2.13.17版本有進行小幅度修正,舊版本也可能隨時會停止服務,所以會在另外一篇文章進行更換版本至新版。

這邊要另外一提的是,如果真的有需求,需要安裝兩個版本以上,也是沒問題,也是按照安裝步驟逐一進行元件註冊。不過到時候,得依照開發使用的程式語言與相關工具、模組進行微調,避免因檔案同名與執行預設值問題導致讀不到指定版本。

安裝必要套件

在Python裡面,可以讀取並呼叫使用COM元件有幾個相關套件,像是pywin32以及comtypes等等。我們可以將以上兩個套件進行交互使用。所以首先我們得先安裝以上兩個套件。安裝指令如下:

pip install comtypes pywin32

安裝過程如果順利,大致會如以下圖片所示:

這邊附上The Python Package Index (PyPI)上這兩個套件的相關資訊,PyPI是一個Python程式語言的軟體存儲庫,主要是第三方軟體套件會在上面發佈,pip管理器預設便是從這個來源進行下載安裝:


Python 串接群益API - Event Loop

這邊要先提到,我們並不打算在這邊使用Python建立一個GUI介面與程式,一般GUI程式會有一個Main Loop的方法可以使用,除了動態刷新視窗元件與維持視窗顯示之外,也會輔助監聽事件和處理程式的Message Queue。因此少了這個方法,這會讓我們在接收從事件上回傳的訊息時出現一些問題。

Message Loop是一種Event Loop,而Event Loop是一個設計模式(design pattern),用來處理一個程式中監聽、調用事件與訊息。根據不同作業系統有不一樣的實作,像是Windows作業系統會有Microsoft Message Queuing (MSMQ)。簡單來說,就是透過Queue的資料結構進行訊息的儲存,以一個循環(Loop)的方式,可以進行非同步、inter-process(像是執行緒, thread,或是進程, Process)之間的溝通。

關於Message Queue的相關文件參考連結如下:

總而言之,沒有GUI程式並呼叫使用Main Loop的時候,即便能正常接收訊息,如果沒有做任何取出訊息的動作,訊息也只會卡在Message Queue裡面。因此我們想要使用pywin32套件裡pythoncom模組包含的PumpWaitingMessages()將獲取到的訊息,從Message Queue裡面取出並利用。

其實還有另一種叫做PumpMessages()的方法,其會循環進行等待並獲取訊息的動作。但我們選擇使用PumpWaitingMessages(),是因為這兩個函式的終止條件不一樣,而pywin32的官方說明文件缺乏,網路上也較難取得相關的運作機制,比方說PumpMessages()會因為甚麼情況停止或結束難以得知,使用PumpMessages()會持續等待在未知的情況底下,而無法正常結束這個函式,因此為避免非預期的bug出現,就先不使用了。

Python 串接群益API - 調用COM元件

想要調用COM元件,其實pywin32以及comtypes都能做到,但就像是剛剛所說,pywin32的說明文件太過於少,萬一出現問題,會很難解決。這就跟在使用WordPress開發網頁時,所選用的Template模組,除了星數等級之外,其熱門度就很重要,越多人使用,理論上出現問題時,去網路上能搜尋到的討論與資源就越多,問題能夠被解決的機會就更大。因此,本篇才使用comtypes進行調用COM元件的角色。


Python 串接群益API

首先我們先把會用到的模組import進來,其中pythoncom用來從Message Queue中取出訊息、comtypes用來管理與使用COM元件、使用內建的os模組,用來管理檔案與路徑。最後,我們會使用到內建的time模組,只是為了獲取時間戳,與做一些迴圈中的小延遲:

import pythoncom
import comtypes.client

import os

import time

然後我們先設定一些變數,為了方便,而且通常運行這個程式,都是獨立運行,所以我們使用全域變數(再提一下,由於目前我所使用的是2.13.16 32位元版本,所以python我也使用x86版本)。

<api_path>變數用來儲存欲使用元件位置,這邊在字串前面加個'r'字元,用以代表raw string,這樣如果使用'\'作為路徑,就不會被當作特殊字元(雖然這邊是使用'/')、<account>及<password>理所當然會是帳號與密碼、<data_path>代表抓到的資料要儲存在哪個根目錄、<market_number_dict>則是參考官方文件說明,用來表示不同市場別的編號,使用字典型別以方便呼叫使用,最後<refresh_interval>只是為了做適當的固定延遲:

api_path = r'C:/SKCOM/2.13.16/SKCOM.dll'

account = 'your account'
password = 'your password'

data_path = './Data'

market_number_dict = {
0: '上市',
1: '上櫃',
2: '期貨',
3: '選擇權',
4: '興櫃'
}

refresh_interval = 6e-1

在功能上,我們定義一個進行API初始化的函式,並回傳可用物件,這邊主要有管理登入(還有環境設定)、國內報價兩個主要功能。還有其他功能,像是海外期貨與回報等等功能,本篇並不會用到。而這些功能是以Active Template Library (ATL)作為實現,ATL是一組template-based C++ Classes,由微軟(Microsoft)所開發,主要用於簡化編寫COM元件:

def capital_api_init(api_path_):
# link api module.
comtypes.client.GetModule(api_path_)
import comtypes.gen.SKCOMLib as sk

# Login.
skC = comtypes.client.CreateObject(sk.SKCenterLib, interface=sk.ISKCenterLib)

# Domestic Quote.
skQ = comtypes.client.CreateObject(sk.SKQuoteLib, interface=sk.ISKQuoteLib)

return skC, skQ

其中,我們使用comtypes.client.CreateObject()函式建立物件。

這邊要注意,在這個進行初始化的函式裡面,import SKCOMLib這個步驟,得要在comtypes.client.GetModule()之後,這是由於comtypes.client.GetModule()函式會對指定的COM檔案,參考裡面所涵蓋的type library,進一步轉為一組Python module,基本上會放置於path\to\Lib\site-packages\comtypes\gen目錄底下。整體結構如下圖所示:

另外,裡面還有一個子目錄__pycache__,裡面會存放Python在第一次執行主程式後,把相關依賴的函式庫或模組轉為bytecode進行儲存,以.pyc為副檔名的檔案存在。之所以會這樣做,也是為了讓Python這類的Interpreted Language可以在第二次與之後的執行更快速(簡單來說,就是不用再轉換一次bytecode的動作)。

因此,在這個模組產生之前(尚未執行過程式的時候),如果以IDE像是Pycharm進行編寫,可能會發現import SKCOMLib這段會提示找不到模組。這個不用擔心,只要執行過基本上就會產生了。

附上comtypes官方說明文件:

關於python在bytecode上的轉換與相關說明,可以查看以下官方連結:


這邊再提醒一下,如果註冊的元件與Python位元版本相互不相容,也可能是會出現類別未登錄的錯誤。比如安裝了x86版本的元件,但使用x64版本的python,執行後會出現以下訊息:

當然,類別未登錄的這樣的錯誤訊息,也可能是因為真的未註冊成功,或是COM元件位置指定錯誤等等。


我們可以先嘗試執行看看:

if __name__ == '__main__':
skC, skQ = capital_api_init(api_path)

雖然我們尚未進行帳號登入,但如果上面執行沒有任何錯誤的話,就能確定元件已正常註冊,並且能夠正常讀取並使用。

所以下一步,我們要來進行帳號登入:

def capital_Login(acc_, pass_):
m_n_code = skC.SKCenterLib_Login(acc_, pass_)
print("Login [{}]".format(skC.SKCenterLib_GetReturnCodeMessage(m_n_code)))

這邊透過skC物件,我們可以呼叫skC.SKCenterLib_Login()函式進行登入,其會回傳一個狀態碼,代表不同的登入狀態。而這個登入碼對應的登入狀態,可以透過skC.SKCenterLib_GetReturnCodeMessage()函式進行查看。

立馬來嘗試登入:

if __name__ == '__main__':
skC, skQ = capital_api_init(api_path)
capital_Login(account, password)

登入成功會顯示以下訊息:




結論

至此,我們介紹了API服務、註冊元件、透過Python使用COM元件的方法,最後我們完成了帳號登入。有部分宣告好的變數在本篇上未用到,但還是先提及了,主要是為了呈現,在編寫程式之前,如果可以,我們應該可以先規劃好整體大概流程與細節,像是會需要定義與使用到哪些變數等等。

此外,本次我們並沒有像在使用元大API時那樣,特別設定主機位置,這是因為群益API已經有一個預設值與預設行為,如果沒特別需求,其實不用特別另外設定。

而由於篇幅關係,我把報價的功能實現放在下一篇的內容中。

並且在更之後,也將介紹進行更換版本的動作,在這個步驟中,如果先前以有運行舊版本,可能會遇到一些問題,我們針對這些問題會進行排解。 

沒有留言:

張貼留言