群益API 報價停止更新怎麼辦?單一商品停格的偵測與自救設計
連線正常,其他商品的報價都在跳,只有某一檔的成交明細和五檔不動了。你寫了看門狗,它卻從來沒有觸發過。
這種群益API 報價停止更新有一個特點:停下來的只有一條流,而其他還活著的流,把它遮住了。
先說清楚本文的定位:官方文件沒有說明這個情況。 以下是我們的實測,以及從中整理出來的通用設計原則,不是官方規格。

目錄
群益API 報價停止時,連線不一定斷了:單一商品會單獨停格
我們在盤中實測時觀察到:某一檔國內報價商品,用 RequestTicks 訂閱之後,成交明細與五檔停止更新,同時其他商品的報價仍然正常進來——也就是說,連線本身還在。
為什麼會停,我們不知道,也不打算猜。這裡只陳述觀察到的現象。
官方文件沒有與停格對應的錯誤碼或事件,所以要發現它,只能靠自己比對「多久沒收到資料了」。而這正是陷阱所在。
為什麼看門狗從來沒觸發?一個時間戳只能代表一條流
假設你的看門狗只記一個全域的「最後收到任何報價的時間」。某一檔停格了,但其他商品還在跳——那個時間會被其他商品一直刷新,看門狗永遠不會觸發。
停格是局部的,偵測卻是全域的。 這就是看門狗從來沒動過的原因。
從這裡可以整理出一條通用原則:多條資料流共用同一個「最後更新時間」,任何一條活著,就會遮住其他條的停格。 同一個問題,我們自己的實作在不同的層級各踩過一次,前後一共三次。
報價其實有好幾條流:
- 每一檔商品是一條。
- 同一檔的快照層與深度層(〈群益API 報價訂閱有哪兩層〉講的兩層)又各是一條。
- 深度層裡,成交明細與五檔來自兩個不同的事件,也是兩條。
所以有兩條規則:
規則一:每新增一個「最後更新時間」,先問它代表哪一條流。 答案如果是「好幾條」,那就是未來會遮住停格的地方,要分開記。例如成交明細與五檔,就要各記一個時間。
規則二:偵測的粒度不能比自救的粒度粗。 如果你的自救動作是「重新訂閱某一檔的深度層」,偵測就必須做到「某一檔的深度層」,不能拿全域或跨層的時間去判斷。
從來沒收過資料的商品,看門狗會漏掉嗎?
會,這是看門狗的一個盲區。
我們實測過一個案例:某檔商品送出 RequestTicks 之後,深度層從來沒有任何事件進來——五檔沒有掛單、成交明細沒有資料;但同一個畫面上,它的最佳買賣價有在更新(快照層 RequestStocks 正常)。
而看門狗一直沒有動作。原因是它的判斷方式是「最後更新時間距離現在太久」——從來沒更新過的商品,最後更新時間是初始值,被當成「還沒開始」直接略過了。
通用做法是:
- 「訂閱後從來沒收過資料」也要算停格,不能略過。
- 但要給一段寬限期,從訂閱的那一刻起算,避免把「第一筆資料還在路上」的商品誤判成停格。
寬限期多長,要看你自己的環境,本文不給數字。
哪些安靜是正常的?沒有訂閱與冷門商品
有兩種「本來就不會有資料」的情況,看門狗要排除。
第一,沒有任何訂閱時。 最後更新時間本來就不會前進。看門狗要先確認「現在有沒有在訂閱」,再比對時間;否則會在沒人看行情的時候誤觸重連。這一條是從邏輯推出來的,不是從事故學到的。
第二,冷門商品、還沒開盤的商品。 它們本來就可能很久沒有成交明細。看門狗分不出「停格」和「本來就沒有成交」,所以自救一定要有退避與次數上限:每次重試的間隔拉長,到了上限就放棄並記錄下來。沒有上限,這類商品會讓自救變成無止盡的重送。
退避怎麼拉長、上限設幾次,本文不給數字。
群益API 報價停止之後怎麼自救?先局部、再重連,但效果沒有保證
以下是我們的做法,不是保證有效的解法。
局部自救:只對停格的那一檔,重新送一次 RequestTicks。這是對齊規則二的做法——偵測到哪一檔,就只處理哪一檔。這個做法能不能救回來,我們沒有完整驗證。
反例:〈群益API 連線狀態碼怎麼解讀〉提過的那一次——握手還沒完成就重複呼叫 EnterMonitorLONG 的那一場,我們觀察那一檔商品時,對同一頁重送了多次 RequestTicks,都沒有任何成交明細或五檔回來;而同一天其他幾次啟動、走同樣的流程都正常。我們不確定所有停格都是同一個原因。
所以自救要有升級路徑:局部重訂閱沒用,就升級成重新連線(EnterMonitorLONG),連線後等 3003,再把訂閱整批送回去。這個方向是從「其他重新啟動的場次都正常」推出來的,升級做法本身還沒有實機驗證,所以我們不說「重新連線就會恢復」。
另外,重新訂閱時要注意當天成交明細的回補,見〈群益API Tick 回補是什麼〉。
畫面上怎麼讓人看出報價已經舊了?
停格的時候,畫面上還顯示著最後一筆價格,看起來跟正常的一模一樣。
通用做法是:顯示報價時一併顯示「這筆資料是多久以前的」,或在超過一段時間沒更新時改變顯示方式。讓看的人知道,這個數字可能已經不是現在的價格。
keep-alive 可以解決停格嗎?
不行,這是兩件事。
〈群益API 連線狀態碼怎麼解讀〉講的「每十五秒呼叫 RequestServerTime」,是官方針對收盤後連線被防火牆切斷給的建議。本文的停格發生在盤中、連線沒斷的時候,keep-alive 解決不了。
兩者都要做,但 keep-alive 是前提,不是停格的解法。
群益API 報價停止的看門狗:8 條設計原則+程式碼
- 每一條流各記一個「最後收到資料的時間」:逐商品、逐層、逐事件。
- 偵測的粒度對齊自救的粒度:要重訂閱某一檔的深度層,就偵測某一檔的深度層。
- 「訂閱後從來沒收過」也算停格,但從訂閱起算給一段寬限期。
- 看門狗先確認有在訂閱,沒有訂閱時不判斷。
- 自救要有退避與次數上限,到上限就放棄並記錄。
- 自救要有升級路徑:局部重訂閱沒用,就重新連線,等
3003後整批重訂。 - 畫面上標示報價有多舊,不要讓停格的價格看起來跟即時的一樣。
- keep-alive 照做,但它不是停格的解法。
深度層的兩個事件是 OnNotifyTicksLONG(成交明細)與 OnNotifyBest5LONG(最佳五檔)。注意兩者的索引參數名字不一樣:前者叫 nIndex,後者叫 nStockidx,這是官方原樣;五檔事件的參數是價與量逐檔交錯排列。
// 一條流一個時間:成交明細與五檔是兩個事件,各記各的
Dictionary<string, DateTime> _lastTickAt = new Dictionary<string, DateTime>();
Dictionary<string, DateTime> _lastBest5At = new Dictionary<string, DateTime>();
Dictionary<string, DateTime> _subscribedAt = new Dictionary<string, DateTime>();
// OnNotifyTicksLONG 收到時(先用索引查出商品代碼)
void MarkTick(string stockNo) { _lastTickAt[stockNo] = DateTime.Now; }
// OnNotifyBest5LONG 收到時
void MarkBest5(string stockNo) { _lastBest5At[stockNo] = DateTime.Now; }
// 單一條流是否停格:從未收過就從訂閱時間起算寬限期
bool IsStale(Dictionary<string, DateTime> last, string stockNo, DateTime subscribedAt,
TimeSpan staleAfter, TimeSpan grace)
{
DateTime t;
if (!last.TryGetValue(stockNo, out t))
return DateTime.Now - subscribedAt > grace; // 從未收過:過了寬限期也算停格
return DateTime.Now - t > staleAfter;
}
// 看門狗:偵測逐商品、逐事件;自救的粒度是「重送這一檔的 RequestTicks」,
// 所以任一條流停格,就把這一檔交給事件之外的自救流程
List<string> FindStale(TimeSpan staleAfter, TimeSpan grace)
{
var stale = new List<string>();
if (_subscribedAt.Count == 0) return stale; // 沒有訂閱時,安靜是正常的
foreach (var kv in _subscribedAt)
if (IsStale(_lastTickAt, kv.Key, kv.Value, staleAfter, grace) ||
IsStale(_lastBest5At, kv.Key, kv.Value, staleAfter, grace))
stale.Add(kv.Key);
return stale;
}這段程式碼同時示範了兩條規則:分開記(規則一),以及偵測不比自救粗(規則二)——自救是以「一檔」為單位重送,所以任一條流停格,就把整檔交出去。
還有三件事要補充:
staleAfter、grace由你自己決定。 成交明細可能因為沒有成交而本來就安靜,所以兩條流的停格門檻可以分開設。- 這幾個字典由事件執行緒寫入、由看門狗讀取,實際程式要做同步。 例如看門狗判斷前先複製一份時間表,再用複本判斷。片段為了簡短沒有寫。事件在元件自己的執行緒上觸發,細節見〈群益API 事件回呼跑在哪個執行緒〉。
- 退避與次數上限的程式碼沒有放,原則見上文。
常見問題
連線沒斷,為什麼群益API 報價停止更新了?
我們實測過的情況是:單一商品的成交明細與五檔停止更新,其他商品照常,成因我們不知道。官方文件沒有與停格對應的錯誤碼或事件,所以只能靠自己比對每一條資料流多久沒更新來發現。
看門狗該記幾個「最後收到資料的時間」?
一條流一個。逐商品、逐層(快照層與深度層分開)、逐事件(成交明細與五檔分開)。多條流共用一個時間,任何一條活著就會遮住其他條的停格。
偵測到停格後重新訂閱,就會恢復嗎?
我們沒有完整驗證。我們的做法是先對那一檔重送 RequestTicks,沒用再升級成重新連線、等 3003 後整批重訂,但升級做法本身也還沒有實機驗證。自救一定要有退避與次數上限。
已經照做 keep-alive,為什麼還是會停格?
因為兩者處理的是不同的問題。keep-alive 是官方針對收盤後連線被防火牆切斷的建議;停格發生在盤中、連線沒斷的時候,要靠看門狗另外偵測。
風險揭露
期貨與選擇權屬高槓桿商品,價格波動可能造成超過原始保證金的損失,交易人須自負交易責任。本文為程式開發技術教學,說明報價資料流停止更新的偵測設計,不構成投資建議,也不保證任何交易結果。程式化交易並不降低市場風險,反而可能因程式錯誤造成非預期的行為——報價停止更新而程式未察覺時,程式可能依據已經過時的價格做判斷。
投資人教育資源
| 機構 | 資源 |
|---|---|
| 臺灣期貨交易所(TAIFEX) | 期貨及選擇權數位學習網 |
| 證券暨期貨市場發展基金會 | 證券期貨市場教育推廣 |
| 金融智慧網 | 金管會金融知識平台 |
| 中華民國期貨業商業同業公會 | 期貨業法規與宣導 |
| 證券投資人及期貨交易人保護中心 | 投資人保護與申訴 |
延伸閱讀
- 〈群益API 連線狀態碼怎麼解讀〉——就緒判定、keep-alive,以及本文反例的那一次握手期間重複連線。
- 〈群益API 報價訂閱有哪兩層〉——快照層與深度層,看門狗要分開記的就是這兩層。
- 〈群益API 事件回呼跑在哪個執行緒〉——事件執行緒寫入、看門狗讀取,兩邊的同步要注意什麼。
- 〈群益API 訂閱額度有多少〉——兩層的訂閱額度。
參考資料
SKQuoteLib_RequestTicks的相關通知事件、OnNotifyTicksLONG/OnNotifyBest5LONG的宣告與參數,依群益官方元件說明文件「國內報價」章節。官方文件沒有關於報價停格的說明。- 單一商品停格、看門狗盲區、局部重訂閱與升級重連的做法、反例:實作與實測經驗,非官方文件記載。
免責聲明
本文章僅作為群益API實作經驗分享,不構成投資建議,且策略及程式皆應自行撰寫。期貨及衍生性金融商品交易屬高風險投資,請謹慎評估自身風險承擔能力。







