Python 接群益API 收不到事件的檢查流程示意圖,說明 COM 執行緒模式的差異

Python 接群益API 收不到事件?先檢查這 4 行的順序

登入回 0,成功。掛事件,沒有例外。訂閱函式,回傳正常。

然後——什麼都沒發生

這個問題最難的地方,是它沒有給你任何線索:沒有錯誤碼、沒有例外、沒有一個可以拿去搜尋的字串。你手上只有「它不動」這件事。

Python 接群益API 收不到事件的檢查流程示意圖,說明 COM 執行緒模式的差異

群益API 收不到事件,但每個函式都回傳成功?

群益API 收不到事件的時候,懷疑的順序通常是這樣的:

你會先懷疑但是
是不是沒訂閱成功?訂閱函式回傳正常
是不是商品代碼寫錯?換一個確定存在的代碼,一樣沒反應
是不是盤後沒資料?盤中再試,還是沒有
是不是權限問題?登入回 0,事件也掛上去了

四個方向都排除之後,多數人會回頭再讀一次自己的訂閱邏輯——而那段程式碼是對的

問題不在你寫的任何一行,在一個你沒有寫、也看不見的地方:整個行程的 COM 執行緒模式。

為什麼 C# 的人不會遇到這個問題?

這是群益API 收不到事件會卡很久的原因之一:你搜尋不到這個問題

網路上關於這套元件的範例以 C# 為主。而 C# 的視窗程式(WinForms、WPF)自帶訊息迴圈——它的 UI 執行緒本來就是一個正常運作的 STA,這件事由框架處理掉了,寫程式的人不需要知道它存在。

所以同一套元件,在 C# 下不會遇到這個狀況,自然也不會有人寫文章討論它。

如果你用 Python 卡在這裡,那不是你做錯了什麼,是你用的語言沒有幫你處理這一層。 這個認知本身就值得先講——它可以讓你停止懷疑自己的訂閱邏輯,把注意力移到對的地方。

STA 與 MTA:官方怎麼說

要理解群益API 收不到事件的檢查點,先看 COM 的執行緒模型。COM 把執行緒分成兩種 apartment 模式,微軟官方文件的對照如下:

模式CoInitializeEx 旗標官方建議使用時機
STA(單一執行緒 Apartment)COINIT_APARTMENTTHREADED執行緒有訊息迴圈(例如 UI 執行緒),或所用的 COM 物件需要訊息幫浦
MTA(多執行緒 Apartment)COINIT_MULTITHREADED執行緒做背景工作、沒有訊息迴圈,且 COM 物件是執行緒安全或 agile 的

官方另有一段關於 STA 的重要提醒:STA 執行緒必須抽送訊息;如果 STA 執行緒在沒有抽送訊息的情況下阻塞等待,送進該 apartment 的 COM 呼叫就永遠不會被派送。

讀到這裡,很自然會想:那原因是不是就是沒有抽送訊息?

不是。 comtypes 提供的 PumpEvents 正是一個訊息抽送迴圈——也就是說,下一節要講的實測環境符合官方這段的要求,事件卻依然沒有進來。

官方這段說明並不足以解釋我們觀察到的現象。 把它寫出來,一方面是因為它是理解 STA/MTA 的必要背景,另一方面是要先把「是不是沒抽送訊息」這條路堵起來——不要往那個方向查,那裡沒有答案。

實測:在這個組合下,群益API 收不到事件

先把條件講清楚,因為這是單一環境的一次觀察,不是反覆驗證過的通則:

項目版本
SKCOM2.13.58 x64
Python3.12
comtypes1.4.16

在這個組合下,群益API 收不到事件的具體樣貌是:

  • comtypes 預設走 STA,並提供 PumpEvents 作為訊息抽送迴圈。
  • 在這個預設組合下,事件完全收不到
  • 而且可以確認掛載是成功的——advise 沒有失敗,登入也回 0
  • 改成 MTA 之後,事件就正常進來了。

至於為什麼 STA 搭配訊息抽送迴圈會收不到,我們沒有查明。 上一節已經說過,官方那段解釋不了它。這裡只陳述觀察到的現象與有效的處理方式,不推測機制——因為推測出來的機制聽起來會很合理,而合理跟正確是兩件事。

也因為只有一次單一環境的觀察,這篇不會告訴你「Python 接群益API 一定要用 MTA」。準確的說法是:在上面那個版本組合下必須設成 MTA,其他組合我們沒有驗證過。

解法:這四行的順序決定它生不生效

import sys

# 必須在 import comtypes 之前設定,否則不生效,而且不會報錯
sys.coinit_flags = 0x0   # 0x0 = COINIT_MULTITHREADED

import comtypes.client

解決群益API 收不到事件的關鍵就在這四行,而重點不在語法,在順序

sys.coinit_flags 必須在 import comtypes 之前設定。comtypes 在匯入的當下就依這個值完成初始化——設晚了,它不會生效。

而最麻煩的是:設錯順序不會有任何錯誤訊息。 程式照跑、匯入成功、後面每一個函式也都回傳正常,然後你又回到文章開頭那個狀態——什麼都沒發生。

這是一個無聲失敗,套在另一個無聲失敗上面。 如果你已經知道要設 MTA、也寫了那一行,群益API 收不到事件的狀況卻還在,先檢查那行在不在 import comtypes 前面

改成 MTA 之後,等待事件不要再用 PumpEvents

換成 MTA 就不需要訊息抽送迴圈了。等待事件的方式改成輪詢 sink 上的旗標,讓事件在背景被派送進來:

import time

# MTA 下不需要 PumpEvents,事件會在背景被派送進來
class QuoteSink:
    def __init__(self):
        self.received = False

    # 官方簽名:OnConnect(int nCode, int nSocketCode)
    def OnConnect(self, nCode, nSocketCode):
        self.received = True   # 回呼只做最輕量的標記

sink = QuoteSink()
connection = comtypes.client.GetEvents(quote_lib, sink)

# 用輪詢等待,不要在這裡阻塞住
timeout = time.time() + 10
while not sink.received and time.time() < timeout:
    time.sleep(0.1)

注意回呼裡只做了一件事:把旗標設起來。這不是為了讓範例簡單,而是下一節的紀律。

另外,登入之前必須先註冊 OnReplyMessage,否則 SKCenterLib_Login 會回 2017——這一條 C# 與 Python 一致,差別只在輸出參數的映射方式(Python 的 comtypes 以回傳值對應,寫 return -1)。這部分在〈群益API 是什麼〉講過,本篇不重述。

MTA 不取代回呼裡的那四條紀律

最後一件事,也是最容易被誤會的:設成 MTA 解決的是群益API 收不到事件這個問題,不解決「回呼裡能做什麼」。

群益API 事件回呼跑在哪個執行緒〉那四條紀律照樣適用——不要在回呼裡更新畫面、不要呼叫耗時查詢、不要阻塞等待、不要重入元件。上面範例的回呼只設一個旗標,就是這個原因。

而且在 MTA 下還要小心一件事:依 MTA 的定義,該 apartment 裡的物件可以被那個 apartment 的任何執行緒直接呼叫,也就是回呼可能跑在任意一條執行緒上。跨執行緒的資料保護要做得比 STA 下更嚴謹。

(這一點是從官方對 MTA 的定義推導出來的,不是我們量測的結果。)

常見問題

群益API 收不到事件,但登入和訂閱都成功,是哪裡寫錯了?

不一定是你寫錯。在 Python 端有一個常被忽略的檢查點:行程的 COM 執行緒模式。實測在 SKCOM 2.13.58 x64 + Python 3.12 + comtypes 1.4.16 的組合下,預設的 STA 收不到事件,改成 MTA 之後就正常。

加上 PumpEvents 訊息迴圈就能收到事件嗎?

在上述那個組合下實測是收不到的。要注意 PumpEvents 本身就是訊息抽送迴圈——也就是說,那個環境符合微軟文件對 STA 的要求,事件仍然沒有進來。原因我們沒有查明。

為什麼網路上查不到這個問題?

關於這套元件的範例以 C# 為主,而 C# 的視窗程式自帶訊息迴圈,UI 執行緒本來就是正常運作的 STA,不會遇到這個狀況。

sys.coinit_flags 我寫了,為什麼還是沒用?

檢查它在不在 import comtypes 前面。comtypes 在匯入的當下就依這個值初始化,設晚了不會生效,而且不會有任何錯誤訊息。

改成 MTA 之後,回呼裡就可以放心做事了嗎?

不行。MTA 解決的是事件進不進得來,回呼裡的四條紀律沒有改變;而且 MTA 下回呼可能跑在任意執行緒上,跨執行緒的資料保護要更小心。

風險揭露

期貨與選擇權屬高槓桿商品,價格波動可能造成超過原始保證金的損失,交易人須自負交易責任。本文為程式開發技術教學,說明 Python 端 COM 執行緒模式的設定與檢查方式,不構成投資建議,也不保證任何交易結果。程式化交易並不降低市場風險,反而可能因程式錯誤造成非預期的委託行為——本文描述的無聲失敗正是這類風險的來源之一。

投資人教育資源

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

延伸閱讀

參考資料

  • Processes, Threads, and Apartments – Microsoft Learn(STA/MTA 的定義、CoInitializeEx 旗標、STA 需抽送訊息的說明)
  • 本篇核心現象(該組合下 STA 收不到事件、MTA 正常)為 2026-07-18 單一環境實測,非官方文件記載,亦未經多環境驗證;其機制未查明
  • 「MTA 下回呼可能跑在任意執行緒」為官方 MTA 定義的推導,非實測結果。

免責聲明

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

延伸閱讀|相關文章

發佈留言

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