群益API Python 入門:從安裝到收到第一筆報價的 7 個步驟

群益API Python 入門:從安裝到收到第一筆報價的 7 個步驟

看完觀念,知道群益API 是一組 COM 元件、資料是推播過來的,接下來卻不知道 Python 的第一行要寫什麼。網路上的範例東拼西湊,有的用 PumpEvents、有的設 MTA、有的什麼都沒做,跑起來事件沒進來,也不報錯。

群益API Python 入門有固定的先後順序。每一步都有一個「做對了會看到的訊號」,看不到訊號,就不要往下走。

本文是一張步驟地圖:每一步寫做什麼、做對了會看到什麼、卡住了去看哪一篇。COM 元件與事件推播的觀念,見〈群益API 是什麼〉。

群益API Python 入門的 7 個步驟示意圖:從確認位元數到收到第一筆報價

群益API Python 入門前要準備什麼?

準備項目用途
Windows 電腦群益API 是 Windows 的 COM 元件,無法跨平台
Python 64 位元位元數要和元件一致
群益提供的元件包(含 SKCOM.dll)所有功能都在這個元件裡
帳號、密碼、有效憑證登入用;官方說明是「請在登入前,安裝使用者帳號ID有效憑證」
帳戶側的前置條件已完成行情連線需要;未完成時,商品資料會拿不到

最後一項屬於帳戶設定範疇,不是程式能處理的。如果有遇到這類的問題可以先跟您的營業員反應。

步驟 1–3:確認位元數、註冊元件、安裝套件

步驟 1:確認 Python 是 64 位元。

python -c "import struct,sys; print(sys.version, struct.calcsize('P')*8, 'bit')"

做對了會看到:輸出裡有 64 bit。64 位元的 Python 要搭配 64 位元(x64)的元件;位元數不一致時,元件無法使用。要注意,載入型別的 GetModule 不會因此失敗,問題會出在後面建立元件物件的那一步(依微軟文件推論),細節見〈群益API Python 環境檢查〉。

步驟 2:註冊元件。

把元件包放在固定的位置,之後不要搬動;進入對應位元的資料夾,以系統管理員身分執行註冊。

做對了會看到:執行過程沒有出現錯誤訊息。是否真的註冊成功,要到步驟 4 建立 COM 物件時沒有報錯才確認得到。卡住時:

步驟 3:安裝 Python 套件。

python -m pip install comtypes pywin32
  • comtypes:載入元件的型別、建立 COM 物件、掛上事件。
  • pywin32:本文用它的 pythoncom.PumpWaitingMessages() 抽送訊息(步驟 4)。

步驟 4:群益API 登入前,為什麼要先掛 OnReplyMessage?

這一步要照下面的順序做,其中第 2、4 點的先後有官方依據:

  1. 用 comtypes.client.GetModule 載入元件的型別,再建立 SKCenterLib、SKReplyLib、SKQuoteLib 三個物件。
  2. 登入之前,先掛上 SKReplyLib 的 OnReplyMessage,並回傳 -1。
  3. 掛上 SKQuoteLib 的事件(OnConnection、OnNotifyQuoteLONG)。
  4. 呼叫 SKCenterLib_Login,回傳 0 才往下走。
  5. 等待事件的期間,一定要抽送訊息。

第 2 點的官方說明是「需在登入前先註冊此通知事件—OnReplyMessage,方可正確登入,不需先做回報連線。」沒有掛、或沒有回 -1,登入會回 2017:

代碼常數官方說明
2017SK_WARNING_REGISTER_REPLYLIB_ONREPLYMESSAGE_FIRST請(註冊)接收公告再登入 / 請參考單元3-2

這裡有一個跨語言的差異:官方宣告裡,OnReplyMessage 的第三個參數 sConfirmCode 是輸出參數。C# 透過輸出參數交回這個值;Python 的 comtypes 則把輸出參數變成回傳值,所以 Python 的方法只收前兩個參數,用 return -1 交回。

comtypes 的使用注意: GetEvents 回傳的連線物件與事件接收物件,都要存在變數裡;物件被回收後,就收不到事件。

抽送訊息有兩條路:

不要只靠 comtypes 的 PumpEvents。 我們在 SKCOM 2.13.59 x64、Python 3.12、comtypes 1.4.16 的環境實測:STA 下只用 PumpEvents,收不到連線事件;改用 PumpWaitingMessages,或改成 MTA,都收得到。為什麼 PumpEvents 不行,我們沒有查明。

做對了會看到:登入回 0。登入失敗時,第一件事是看元件自己的 log,官方錯誤碼 1001 的說明就是「登入失敗,請由LOG查看失敗原因。」其他登入錯誤碼的判讀,見〈群益API 登入錯誤怎麼判讀〉。

步驟 5:群益API 什麼時候才能訂閱報價?

  1. 呼叫 SKQuoteLib_EnterMonitorLONG(),建立行情連線。
  2. 等 OnConnection 收到 3003,才訂閱。官方說明是「*請先SKQuoteLib_EnterMonitorLONG,須等OnConnection收到SK_SUBJECT_CONNECTION_STOCKS_READY後,方可進行訂閱商品報價。」
  3. 呼叫 SKQuoteLib_RequestStocks 訂閱,例如台指期近月的商品代碼 TX00。
  4. 收到 OnNotifyQuoteLONG(sMarketNo, nIndex) 之後,用 SKQuoteLib_GetStockByIndexLONG 取出報價。官方說明是「透過以上兩個參數,可以再使用SKQuoteLib_GetStockByIndexLONG 取出報價的內容。」
  5. 價格是整數,要除以 10 的 sDecimal 次方。

幾點補充:

  • RequestStocks 在 Python 的回傳: 官方宣告第一個參數 psPageNo 是 [in,out],comtypes 會把它一起放進回傳值,所以要拆開來看回傳碼。頁號照官方範例傳 1。
  • 商品代碼: 訂閱用的是 TX00 這類商品代碼,不是交易所的合約代碼。
  • 要在交易時段內測試,才看得到報價變動。

做對了會看到:OnConnection 會收到 3001(連線)與 3003(報價商品載入完成);等到 3003 才訂閱,訂閱回 0,接著報價事件進來。卡住時:

步驟 6:怎麼確認自己用的元件版本?

第一次跑通時,呼叫一次 SKCenterLib_GetSKAPIVersionAndBit。官方說明是「取得目前註冊SKAPI 版本及位元。」,回傳值是「目前註冊SKAPI版本及COM位元。(EX: 2.13.30_x64)」。官方沒有說明這個函式要在登入前還是登入後呼叫,本文範例放在登入之後,只是示意。我們實測在登入前呼叫也能取得結果,見〈群益API Python 環境檢查〉。

如果你用的是舊的元件包,登入可能直接回 9996:

代碼常數官方說明
9996SK_ERROR_UPDATE_API_REQUIRED此版本已無法登入,請更新版本。

這不是帳密或憑證的問題,要換新版元件。哪些版本、什麼時候會開始回這個錯誤碼,官方沒有公告。升級的做法見〈群益API 版本升級要注意什麼〉。

群益API Python 最小範例:只用 comtypes 與 pywin32 的完整程式

步驟 4 到步驟 6 合在一起,就是下面這段程式。所有群益API 函式的名稱與參數順序,都對照過官方元件說明文件:

# 群益API Python 最小範例:登入 → 行情連線 → 等 3003 → 訂閱 → 印第一筆報價
# 環境:Windows、Python 64 位元、已註冊 64 位元元件、pip install comtypes pywin32
import time
import pythoncom
import comtypes.client

DLL_PATH = r"C:\SKCOM\x64\SKCOM.dll"   # 改成你註冊的元件路徑
USER_ID = "YOUR_LOGIN_ID"
PASSWORD = "YOUR_PASSWORD"

comtypes.client.GetModule(DLL_PATH)
import comtypes.gen.SKCOMLib as sk

center = comtypes.client.CreateObject(sk.SKCenterLib, interface=sk.ISKCenterLib)
reply = comtypes.client.CreateObject(sk.SKReplyLib, interface=sk.ISKReplyLib)
quote = comtypes.client.CreateObject(sk.SKQuoteLib, interface=sk.ISKQuoteLib)

state = {"ready": False, "got_quote": False}


class ReplyEvents:
    def OnReplyMessage(self, bstrUserID, bstrMessage):
        # 官方:登入前必須註冊此事件,sConfirmCode 回傳 VARIANT_TRUE(-1)
        return -1


class QuoteEvents:
    def OnConnection(self, nKind, nCode):
        print("OnConnection", nKind, nCode)
        if nKind == 3003:          # SK_SUBJECT_CONNECTION_STOCKS_READY
            state["ready"] = True

    def OnNotifyQuoteLONG(self, sMarketNo, nIndex):
        # comtypes 會把 [in,out] 參數和回傳碼一起回傳:(結構, 回傳碼)
        stock, code = quote.SKQuoteLib_GetStockByIndexLONG(sMarketNo, nIndex, sk.SKSTOCKLONG())
        if code == 0:
            price = stock.nClose / (10 ** stock.sDecimal)
            print(stock.bstrStockNo, stock.bstrStockName, price)
            state["got_quote"] = True


# 事件連線物件一定要存在變數裡,被回收就收不到事件
reply_sink, quote_sink = ReplyEvents(), QuoteEvents()
reply_conn = comtypes.client.GetEvents(reply, reply_sink)
quote_conn = comtypes.client.GetEvents(quote, quote_sink)


def pump_until(cond, timeout):
    end = time.time() + timeout
    while not cond() and time.time() < end:
        pythoncom.PumpWaitingMessages()   # 不要只用 comtypes.client.PumpEvents
        time.sleep(0.05)
    return cond()


code = center.SKCenterLib_Login(USER_ID, PASSWORD)
print("Login", code, center.SKCenterLib_GetReturnCodeMessage(code))
if code != 0:
    raise SystemExit("登入失敗,先看元件的 log")

print("版本", center.SKCenterLib_GetSKAPIVersionAndBit(USER_ID))

code = quote.SKQuoteLib_EnterMonitorLONG()
print("EnterMonitorLONG", code)
if not pump_until(lambda: state["ready"], 30):
    raise SystemExit("沒有等到 3003,不能訂閱")

page_no, code = quote.SKQuoteLib_RequestStocks(1, "TX00")   # comtypes 回傳(頁號, 回傳碼)
print("RequestStocks", code, center.SKCenterLib_GetReturnCodeMessage(code))
if code != 0:
    raise SystemExit("訂閱失敗")

if not pump_until(lambda: state["got_quote"], 30):
    print("30 秒內沒有報價變動;請在交易時段內測試")

幾點說明:

  • 帳號密碼:片段用佔位字串。實際使用時,不要把帳密寫死在程式裡,改從環境變數讀取。
  • SKCenterLib_GetReturnCodeMessage:把回傳碼轉成官方的說明文字,除錯時很實用。
  • 回傳形態:GetStockByIndexLONG 的第三個參數是 [in,out],comtypes 會把它和回傳碼一起放進回傳值,順序是(結構, 回傳碼),所以直接拆成兩個變數。RequestStocks 也一樣,回傳的是(頁號, 回傳碼)。
  • 事件方法的參數名照官方宣告寫(nKind, nCode、sMarketNo, nIndex),對照官方文件比較容易。這裡的 OnConnection 是國內報價 SKQuoteLib 的事件。
  • 本文只收報價,所以片段沒有建立下單物件。
  • 0.05 秒與 30 秒都是示意值,依你的程式調整。
  • 回呼裡直接 print、直接取報價:在回呼內用 GetStockByIndexLONG 取報價是官方說明的用法;直接 print 則只是入門示意。正式的程式要遵守〈群益API 事件回呼跑在哪個執行緒〉的四條紀律,回呼裡只做最輕量的收集。

收到第一筆報價之後,下一步讀什麼?

你接下來想做去看
準備下單〈群益API 下單初始化有哪 7 步〉
弄清楚下單回傳 0 代表什麼〈群益API 下單回傳值是 0 就成功了嗎〉
解析委託回報〈群益API 委託回報怎麼解析〉
事件回呼裡能做什麼〈群益API 事件回呼跑在哪個執行緒〉
弄清楚各個物件管什麼、初始化的完整順序〈群益API 的六個 COM 物件各管什麼〉
斷線之後怎麼重連〈群益API 斷線怎麼重連〉
弄清楚商品代碼怎麼填〈群益API 商品代碼怎麼填〉
弄清楚回傳值為什麼是 list、參數要傳幾個〈群益API Python 回傳值為什麼是 list〉
把行情、下單、回報接成一支程式〈群益API Python 程式架構〉

下單之前要知道一件事:官方文件說明,下單函式回傳 0 只表示委託成功送至交易所,交易結果要由回報確認。

群益API Python 入門的 5 個誤解

  1. 誤解: 照網路範例呼叫 comtypes.client.PumpEvents 就能收到事件。其實: 我們實測在 STA 下只靠它收不到,要改用 pythoncom.PumpWaitingMessages() 或改成 MTA。
  2. 誤解: 登入回 0 之後就能立刻訂閱。其實: 要先呼叫 EnterMonitorLONG,等 OnConnection 收到 3003 才可以。
  3. 誤解: 報價事件會直接把價格送進來。其實: 事件只給市場別和索引,要自己用 GetStockByIndexLONG 取,價格還要除以 10 的 sDecimal 次方。
  4. 誤解: 登入失敗一定是密碼錯。其實: 2017(登入前沒掛公告事件)、9996(元件版本已無法登入)都跟密碼無關。
  5. 誤解: 範例程式裡的封裝類別是群益元件的功能。其實: 群益元件提供的是 SKCenterLib_*、SKQuoteLib_* 這類函式,封裝是範例作者自己寫的。

常見問題

群益API 可以用 Python 寫嗎?需要裝什麼?

可以。在 Windows 上用 64 位元的 Python,安裝 comtypes 與 pywin32 兩個套件,再註冊 64 位元的元件,就能用 comtypes 呼叫群益API。

群益API Python 登入回 2017 是什麼意思?

登入之前沒有掛上 SKReplyLib 的 OnReplyMessage,或沒有回傳 -1。官方說明這個事件需在登入前先註冊,方可正確登入。

登入和訂閱都回 0,為什麼一筆報價都沒有?

先檢查等待事件的方式:在我們的實測裡,STA 下只用 PumpEvents 收不到事件,要改用 pythoncom.PumpWaitingMessages() 或改成 MTA。再確認是不是等到 3003 才訂閱,以及現在是不是交易時段。

網路上的範例用了一個封裝類別,那是群益API 的函式嗎?

不是。群益元件提供的是 SKCenterLib_*、SKQuoteLib_* 這類函式;範例裡的封裝類別是範例作者自己寫的。本文的範例只用 comtypes 與 pywin32,直接呼叫群益元件的函式。

風險揭露

期貨與選擇權屬高槓桿商品,價格波動可能造成超過原始保證金的損失,交易人須自負交易責任。本文為程式開發技術教學,說明用 Python 連線群益API 並收到報價的步驟,不構成投資建議,也不保證任何交易結果。程式化交易並不降低市場風險,程式錯誤可能造成非預期的委託行為,正式使用前請充分測試。

投資人教育資源

機構資源
臺灣期貨交易所(TAIFEX)期貨及選擇權數位學習網
證券暨期貨市場發展基金會證券期貨市場教育推廣
金融智慧網金管會金融知識平台
中華民國期貨業商業同業公會期貨業法規與宣導
證券投資人及期貨交易人保護中心投資人保護與申訴

延伸閱讀

參考資料

  • 函式宣告、事件說明、錯誤碼 1001/2017/9996、SKSTOCKLONG 欄位,以及登入前註冊 OnReplyMessage、等 3003 才訂閱、用 GetStockByIndexLONG 取報價的說明,依群益官方元件說明文件。
  • 下單函式回傳 0 的意義,依群益官方元件說明文件 2.13.58 的文件調整說明。
  • TX00 為近月商品代碼,依群益官方元件說明文件(期、選智慧單委託物件的備註、錯誤代碼表)。
  • comtypes – PyPI
  • pywin32 – PyPI
  • STA 下 PumpEvents 收不到事件、PumpWaitingMessages 與 MTA 收得到:我們的實測(SKCOM 2.13.59 x64、Python 3.12、comtypes 1.4.16),非官方文件記載;機制未查明。

免責聲明

本文章僅作為群益API實作經驗分享,不構成投資建議,且策略及程式皆應自行撰寫。期貨及衍生性金融商品交易屬高風險投資,請謹慎評估自身風險承擔能力。

延伸閱讀|相關文章

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *