群益API 委託回報怎麼對帳?序號配對、失敗回報與重複成交
成交回報進來了,程式卻對不回它是哪一筆委託。或者刪單失敗了,本地的掛單簿卻把那筆委託刪掉,畫面上看不到,之後也刪不到。
〈群益API 委託回報怎麼解析〉講的是怎麼讀懂一筆回報;這篇接著講讀懂之後,怎麼用它跟自己的掛單簿對帳。
群益API 對帳的難處在於:一筆委託有好幾個編號,而哪一個有值,要看市場別與回報型態。

目錄
群益API 對帳要對什麼?一筆委託有好幾個編號
一筆委託從送出到成交,會碰到這幾個編號:
| 編號 | 從哪裡來 | 官方說明 |
|---|---|---|
| 送出時的委託序號 | 同步:下單函式的 bstrMessage;非同步:OnAsyncOrder 的 bstrMessage | 同步寫「13碼的委託序號」;非同步寫「委託序號」 |
KeyNo(回報第 0 欄) | 回報 | 原始13碼委託序號 |
SeqNo(回報第 47 欄) | 回報 | 13碼序號(成交單含IOC/FOK產生取消單) |
ExecutionNo(回報第 38 欄) | 成交回報 | 成交序號 |
送出時拿到委託序號的方式,在〈群益API 下單回傳值是 0 就成功了嗎〉講過。
群益API 對帳的核心,是把「送出時拿到的委託序號」,跟回報裡的 KeyNo 或 SeqNo 對起來。 問題是,這兩個欄位哪一個有值,會隨市場別與回報型態改變。
群益API 對帳該用 KeyNo 還是 SeqNo?
先看官方對 KeyNo 的完整說明:
原始13碼委託序號 國內證券市場:成交單會提供此欄位 國內期選、海外市場:成交單無此欄 可使用新增的SeqNo 比對 成交單包含IOC/FOK產生的取消單
也就是說:
- 國內證券:成交單會提供
KeyNo。 - 國內期選、海外市場:成交單沒有
KeyNo,要用SeqNo比對。 - 這裡的「成交單」包含 IOC/FOK 產生的取消單——
SeqNo的官方說明「成交單含IOC/FOK產生取消單」也是同一個意思。
所以國內期選的成交,以及 IOC/FOK 沒成交而產生的取消,要用 SeqNo 配對。 我們實單校正過:國內期權的成交與取消回報,SeqNo 等於送出時 OnAsyncOrder 回的委託序號。
防禦性做法:兩把鍵都試
官方只說明了成交單(含 IOC/FOK 產生的取消單)裡哪一個欄位沒有值,其他回報型態的欄位擺法沒有逐一說明。
所以比較穩的做法是:配對時兩把鍵都試——先用 KeyNo 找,找不到再用 SeqNo 找。建檔與查找要用同一套鍵,不然建檔時用了 A、查找時只看 B,就會對不到。
刪單失敗了,為什麼委託卻從畫面上消失?失敗回報不能動掛單簿
回報的第 3 欄 OrderErr,官方的值是「Y:失敗 T:逾時 N:正常」。
原則是:OrderErr 不是 N 的回報,不要改變本地掛單簿的任何狀態。 只把它記錄下來、顯示出來。
為什麼要這麼嚴格?官方在 BuySell 欄位有這樣一條註記(這條註記列在證券那一段):
*註:如為刪單失敗單(EX:預約單已被取消,又進行刪單之情況),則[0]將顯示0,非B或S
也就是說,「刪單失敗」這種回報是存在的。如果程式一看到刪單相關的回報就把委託從本地簿移除,刪單失敗時,那筆其實還活著的委託就從畫面上消失了,之後也刪不到。 我們自己的實作,就是把「失敗回報一律不動掛單簿」當成第一道閘門。
T(逾時)也不動。逾時代表結果不確定,狀態要等後續回報或查詢確認,不要自己猜成功或失敗。(官方只給了「T:逾時」這個值,沒說明逾時之後會怎樣;這一條是從「結果不確定」推出來的。)
各種回報怎麼套到掛單簿?
依〈群益API 委託回報怎麼解析〉講的官方語意(Qty 在改量時是減量數、在取消時是原委託剩量),可以推出這樣的套用規則:
Type | 對掛單簿做什麼 |
|---|---|
N 委託 | 建一筆,數量=Qty(委託量) |
D 成交 | 在途量扣掉 Qty(成交量);扣到 0 以下就移除 |
U 改量 | 在途量扣掉 Qty(減量數,不是改後的總量) |
C 取消 | 整筆移除(Qty 是原委託剩量) |
P 改價 | 只更新價格,不動數量 |
S 動態退單 | 整筆移除 |
B(改價改量)官方沒有進一步說明這種回報裡數量欄位是什麼意思,本文不列規則。收到 B 時,這筆委託的狀態要靠官方的委託查詢核對。
有兩個地方要特別注意:
U的Qty是減量數,不要當成「改完之後的總量」直接覆蓋。- 同一筆委託可能收到多筆移除類的回報。 官方動態退單的例子是「會收到委託回報、取消回報與動態退單回報」——
C和S都會來。所以移除要能重複執行而不出錯:已經不在就略過。
同一筆成交怎麼去重?
同一筆成交會被重複收到,官方寫到的情況只有一種:〈群益API 下單初始化有哪 7 步〉講過,回報連線之後會先回補,回補裡的舊成交也是 Type = D。重新連線之後會不會再回補一次,官方沒有寫。
有兩個做法可以選:
做法一:用 ExecutionNo 當去重鍵。 官方的欄位就叫「成交序號」,而另一個欄位 OkSeq 的說明是「成交序號(請以ExecutionNo為主)」。累加成交之前,先查這個成交序號收過沒有。
但 ExecutionNo 是否在所有市場、所有情況下都唯一,官方沒有寫,我們也沒有驗證。 這是從欄位定義推出來的做法。
做法二:重新連線時把成交累計清空,等回補把成交重送過來再重建。 這跟〈群益API Tick 回補是什麼〉的「清空、由回補重建」是同一個思路。它的前提是回補一定會把成交完整送來,而官方沒有寫重連時會不會回補。
兩種做法都不能保證不重複,都要搭配查詢核對。回報是推過來的,中間可能漏、可能重複、可能還在回補;本地掛單簿與成交累計,定期要跟官方的查詢結果對一次,見〈群益API 未平倉查詢怎麼選〉。
數量解析不出來怎麼辦?
回報裡的 Qty 是字串,要自己解析。解析失敗時,不要當成 0 繼續。
當成 0 的意思是「什麼都沒發生」,但實際上可能真的成交了。解析失敗時要往安全的方向處理——具體怎麼做由你的程式決定,重點是不要假裝沒事。
群益API 對帳的 7 條原則+程式碼
- 送出時記下委託序號:同步看
bstrMessage(官方寫 13 碼),非同步看OnAsyncOrder(官方寫「委託序號」)。 - 配對時兩把鍵都試:先
KeyNo,找不到再SeqNo;國內期選的成交與 IOC/FOK 產生的取消,官方寫明要用SeqNo。 OrderErr不是N的回報不動掛單簿,只記錄並顯示。- 依
Type套用:成交與改量是扣量(改量的Qty是減量數),取消與動態退單整筆移除,改價只改價格。 - 移除要能重複執行:同一筆委託可能先後收到取消與動態退單回報。
- 成交去重用
ExecutionNo當候選鍵,或重連時清空再由回補重建;兩者都要跟查詢核對。 - 數量解析失敗往安全方向處理,不要當成 0。
Dictionary<string, WorkingOrder> _book = new Dictionary<string, WorkingOrder>(); // 以委託序號為鍵
HashSet<string> _seenExecution = new HashSet<string>(); // 收過的成交序號
void Apply(string[] v) // 呼叫前先照 G1 判斷第 0 欄是否為 980
{
string F(int i) => i < v.Length ? v[i] : "";
if (F(3) != "N") { LogError(F(3), F(44)); return; } // 失敗或逾時:不動掛單簿(逾時時 ErrorMsg 可能是空的,OrderErr 一起記)
string type = F(2);
if (type == "N") // 委託回報:建檔(已經有了就不覆蓋,避免回補重送時把已扣的量重置)
{
string newKey = F(47) != "" ? F(47) : F(0);
if (!_book.ContainsKey(newKey)) _book[newKey] = NewOrder(v);
return;
}
// 兩把鍵都試:KeyNo 優先,找不到再用 SeqNo(國內期選成交單沒有 KeyNo)
string key = _book.ContainsKey(F(0)) ? F(0) : F(47);
if (!_book.TryGetValue(key, out var order)) { LogUnmatched(v); return; } // 對不到:留紀錄,交給查詢核對
if (type == "D")
{
string exec = F(38);
if (exec != "" && !_seenExecution.Add(exec)) return; // 成交序號收過了:重複(候選做法;空值不列入)
if (!int.TryParse(F(20), out int q)) { SafeStop(key); return; } // 解析失敗:往安全方向
order.Remaining -= q;
if (order.Remaining <= 0) _book.Remove(key);
}
else if (type == "U")
{
if (!int.TryParse(F(20), out int dq)) { SafeStop(key); return; } // 減量數解析失敗:同樣往安全方向
order.Remaining -= dq;
}
else if (type == "C" || type == "S") _book.Remove(key); // 已經移除過也不出錯
else if (type == "P") order.Price = F(11);
}片段裡有四個細節值得留意:
- 成交序號是空的,就不列入去重——否則之後所有空值的成交,都會被當成重複丟掉。
- 對不到的回報要留紀錄,交給查詢核對,不要靜靜丟掉。
- 委託回報已經存在就不覆蓋——回補重送委託回報時,才不會把已經扣掉的量重置回來。
SafeStop的內容由你的程式決定;B(改價改量)片段沒有處理。
事件在元件自己的執行緒上觸發,掛單簿實際使用時要做同步,見〈群益API 事件回呼跑在哪個執行緒〉。
常見問題
國內期選的成交回報,要用哪一個序號對回委託?
用 SeqNo(第 47 欄)。官方寫明國內期選、海外市場的成交單沒有 KeyNo,可使用 SeqNo 比對;這裡的成交單也包含 IOC/FOK 產生的取消單。
刪單失敗的回報要怎麼處理?
不要動本地掛單簿。OrderErr 不是 N 的回報,只記錄並顯示。如果看到刪單相關回報就移除委託,刪單失敗時那筆仍然活著的委託會從畫面上消失。
改量回報的數量,是改完之後的總量嗎?
不是。依官方欄位表,改量回報(Type = U)的 Qty 是減量數,要從在途量扣掉,不能直接覆蓋。
重新連線之後,成交量又加了一次,怎麼辦?
群益API 對帳要有去重的方法:用 ExecutionNo(成交序號)當候選去重鍵,或重連時清空、由回補重建。兩者都不能保證,要搭配查詢核對。
風險揭露
期貨與選擇權屬高槓桿商品,價格波動可能造成超過原始保證金的損失,交易人須自負交易責任。本文為程式開發技術教學,說明委託回報與本地掛單簿的對帳方式,不構成投資建議,也不保證任何交易結果。程式化交易並不降低市場風險,對帳錯誤——例如把失敗的刪單當成成功、或重複計算成交——都可能讓程式對自己的委託與部位產生錯誤認知並繼續下單。
投資人教育資源
| 機構 | 資源 |
|---|---|
| 臺灣期貨交易所(TAIFEX) | 期貨及選擇權數位學習網 |
| 證券暨期貨市場發展基金會 | 證券期貨市場教育推廣 |
| 金融智慧網 | 金管會金融知識平台 |
| 中華民國期貨業商業同業公會 | 期貨業法規與宣導 |
| 證券投資人及期貨交易人保護中心 | 投資人保護與申訴 |
延伸閱讀
- 〈群益API 委託回報怎麼解析〉——欄位表、
Type與Qty的語意。 - 〈群益API 下單回傳值是 0 就成功了嗎〉——送出時拿到的委託序號與
OnAsyncOrder。 - 〈群益API 下單初始化有哪 7 步〉——回報通道的回補與
OnComplete。 - 〈群益API Tick 回補是什麼〉——同一類去重問題,在報價那一側的處理。
參考資料
KeyNo、SeqNo、ExecutionNo、OkSeq、OrderErr、ErrorMsg、Type、Qty的說明與BuySell的刪單失敗註記,依群益官方元件說明文件 V2.13.59 主手冊的OnNewData欄位表;動態退單例與回報回補依官方「回報」章節;OnAsyncOrder與同步bstrMessage依官方「下單-國內期選」章節。SeqNo等於OnAsyncOrder回的委託序號、失敗回報不動掛單簿:實作與實單經驗,非官方文件記載。- 兩把鍵都試、套用規則、成交去重的兩種做法:通用設計原則。
免責聲明
本文章僅作為群益API實作經驗分享,不構成投資建議,且策略及程式皆應自行撰寫。期貨及衍生性金融商品交易屬高風險投資,請謹慎評估自身風險承擔能力。







