台股停券預告 BFI84U 端點 — 暫停融券清單、ROC 日期 Parse 坑與 MI_MARGN 註記欄位
除息、ETF 分配收益、股東會等公司事件發生前,TWSE 規定相關證券須在停止過戶前**暫停融券賣出(停券)**一段時間,期間融資買進仍可進行。這份預告清單在 TWSE OpenAPI 有一個獨立端點:BFI84U,TWSE 官方報表名稱即「得為融資融券有價證券停券預告表」。
對工程師而言,這個端點有三個容易踩到的坑——ROC 民國日期解析、Reason 原因值容錯、以及與 MI_MARGN 餘額端點的職責區分。
① BFI84U 是什麼
GET https://openapi.twse.com.tw/v1/exchangeReport/BFI84U
回傳:近期將「暫停融券賣出(停券)」的證券清單,含停券起訖日(停券起日為最後回補日)與原因。對除息/分配收益/股東會事件,期間融資買進不停、僅停融券賣出。
這份清單每日更新、筆數隨事件預告滾動;近期一次實測約數十筆。
此端點位於 TWSE OpenAPI exchangeReport 路徑,性質是事件預告,回答的問題是:「哪些股票,在哪段期間,因為什麼原因,暫停融券賣出(停券)?」它的定位與每日融資融券餘額端點(MI_MARGN)不同:BFI84U 提供提前數日至數週的事件預告,而 MI_MARGN 的「註記」欄只反映次一營業日的融資融券狀態。要提前判斷停券窗,兩者需互補使用,這個時間粒度差異是本文的核心坑之一。
② 欄位結構(JSON 回應)
實測為五個欄位:
| Key 名稱 | 中文含義 | 範例值 | 說明 |
|---|---|---|---|
Code | 證券代號 | 00939 / 1219 | ETF 與普通股混存 |
Name | 證券名稱 | (依代號) | 字串 |
StartDate | 停券起日(最後回補日) | 1150529 | ROC 民國日期,7 碼 |
EndDate | 停券迄日 | 1150601 | ROC 格式 · 但可能為空字串 ""(迄日待公告,常見於減資/長期停券) |
Reason | 原因 | 除息 / 分配收益 | 五種原因(見下) |
欄位結構示意(單列,僅示範結構,非任何買賣建議):
{"Code":"00939","Name":"...","StartDate":"1150529","EndDate":"1150601","Reason":"分配收益"}
③ Reason 的五種原因值
近期一次實測中,Reason 的分布以「除息」為大宗,其餘依序為「分配收益」(ETF 收益分配)、「減資」、「現金增資」、「其他」:
| Reason 值 | 來源 |
|---|---|
| 除息 | 普通股年度或半年度除息(佔多數) |
| 分配收益 | ETF 收益分配 |
| 減資 | 減資 |
| 現金增資 | 現金增資 |
| 其他 | 非上述四類的暫停情境 |
「除息」與「分配收益」兩類混在同一份回應內,按 Reason 欄位可區分,但需自行判斷。
重要:這些值是當期清單的實際分布,不是固定不變的欄位定義。「其他」這個值存在,代表 TWSE 保留了一個概括性的歸類。寫自動化邏輯時,不應寫死成「只接受這五個原因字串」——遇到未知值應走容錯邏輯(略過或記錄),而非中斷或靜默丟棄。
④ 三個工程坑
坑 1 · ROC 民國日期(StartDate / EndDate)
StartDate 與 EndDate 是 7 碼 ROC 民國格式,如 1150529,代表民國 115 年 05 月 29 日(即西元 2026-05-29)。
直接當西元年解析會得到「西元 1150529」這個無意義的數值。TWSE OpenAPI 體系中多個端點使用 ROC 日期,BFI84U 是其中之一。
轉換邏輯:前 3 碼為民國年,後 4 碼為 MMDD,民國年 + 1911 = 西元年:
// StartDate: "1150529" → 2026-05-29 西元
// EndDate 可能為空字串(迄日待公告)→ 回傳 null,非解析失敗
function parseRocDate(roc7) {
if (!roc7 || roc7.length !== 7) return null; // 防呆:空字串或格式異常
const rocYear = parseInt(roc7.slice(0, 3)); // 115
const mm = roc7.slice(3, 5); // "05"
const dd = roc7.slice(5, 7); // "29"
const adYear = rocYear + 1911; // 2026
return `${adYear}-${mm}-${dd}`; // "2026-05-29"
}
EndDate 可能為空字串:實測清單中會有少數幾筆的 EndDate 是 ""(多為減資或長期停券),代表「停券中、結束日待定」。上方函式因首行防呆判斷(空字串會被判為否),parseRocDate("") 回傳 null。真正的風險是:若省略這道防呆、直接對空字串做 parseInt 後的日期運算,才會得到 NaN / Invalid Date,導致這幾檔的停券判斷被靜默漏掉。務必在後續比較時把 end === null 當成「迄日待公告、仍在停券期內」處理。
TWSE OpenAPI 整體的日期格式分布(7 碼 ROC / 斜線 ROC / 西元 8 碼等五種並存)詳見 TWSE OpenAPI 日期格式陷阱。
坑 2 · MI_MARGN 的「註記」欄與時間粒度差異(最易漏的坑)
MI_MARGN 是 TWSE 每日融資融券餘額端點,回傳的是「某檔今日融資餘額是多少張 / 融券餘額是多少張」。
關鍵在於:MI_MARGN 自身即帶「註記」欄,會標示停券/停資狀態。TWSE 在報表底部明列符號圖例:
| 符號 | 含義 |
|---|---|
O | 停止融資 |
X | 停止融券 |
! | 停止買賣 |
@ | 融資分配 |
% | 融券分配 |
實測比對下來,BFI84U 停券清單內的代號,在同一天 MI_MARGN 的 註記 欄都帶有對應符號(除息/分配收益事件對應 X 停止融券)。因此「MI_MARGN 對停券完全沒有跡象、必須靠 BFI84U 才知道」並不成立。
真正的坑是時間粒度差異與欄位語意成本,而非「沒有標記」:
MI_MARGN的註記欄只反映次一營業日的融資融券狀態,且該符號需查圖例(X/O/!)才看得懂;漏讀圖例就會把標記當雜訊。MI_MARGN的餘額數字本身不因停券歸零——它仍是最後一筆餘額快照。若自動化只看餘額數字、不解析註記欄,就會把停券期間的數據當成正常餘額照常推播。- 若需提前數日至數週得知停券窗(含完整起訖日區間),
MI_MARGN的「次一營業日」粒度不夠,仍應比對 BFI84U 的事件預告。
因此正確做法是兩者互補:解析 MI_MARGN 時務必讀 註記 欄判斷當日停券/停資狀態,並在需要提前判斷或完整起訖日時比對 BFI84U。
// 判斷某個證券是否在當日停券(暫停融券)期間(偽代碼)
async function isMarginSuspended(code, today) {
const suspensions = await fetch(BFI84U_URL).then(r => r.json());
return suspensions.some(row => {
const start = parseRocDate(row.StartDate);
const end = parseRocDate(row.EndDate);
// end === null 代表迄日待公告(仍在暫停期內)
return row.Code === code && today >= start && (end === null || today <= end);
});
}
// 取得融資餘額前先過 suspension gate
if (await isMarginSuspended(targetCode, today)) {
// 跳過此週期的 MI_MARGN 解讀或標記為 suspended
}
這兩個端點互補:BFI84U 是停券事件預告清單(提前數日至數週,含完整停券起訖日區間),MI_MARGN 是餘額快照 + 次一營業日的 註記 狀態(含 X 停券/O 停資標記)。只讀其中一個,要嘛漏掉提前量(只用 MI_MARGN),要嘛漏掉每日餘額(只用 BFI84U)。
坑 3 · ETF 與普通股混存於同一份回應
BFI84U 的清單同時含普通股(Reason 多為 除息)與 ETF(Reason 多為 分配收益)。ETF 收益分配前的停券和普通股除息前的停券,在法規邏輯上來源不同,但在 BFI84U 中是同一層的 JSON 陣列。
按 Reason 分類可以區分兩類,但需注意:同一檔 ETF 若在不同月份多次分配,會在清單內出現多筆不同日期的資料列。自動化邏輯若只檢查「是否存在於清單」而不看日期範圍,在多筆情況下需正確對齊 StartDate/EndDate 才能判斷「現在是否仍在暫停期內」。
⑤ 穩定取值建議
BFI84U 每日更新,適合在盤前自動化流程中作為前置檢查關卡。整合要點:
- ROC 日期轉換:轉換前先確認前 3 碼為民國年,
+ 1911轉西元;空字串EndDate經防呆判斷回傳null,後續以end === null視為「迄日待公告、仍在停券期內」 - Reason 容錯:用集合查表而非窮舉所有值,未知值記錄下來、不中斷
- MI_MARGN 註記欄解析:依賴融資融券餘額的邏輯,務必解析
MI_MARGN的註記欄(X停券/O停資/!停止買賣)判斷當日狀態;需提前數日預警或完整停券起訖日時再比對 BFI84U - 日期範圍匹配:同一代號可能有多筆,匹配時需同時比對
StartDate≤ 今日 ≤EndDate
想把這類台股 API 監控整合進自動化推播流程,floviq 提供的盤前自動化工具模板(portaly.cc/floviq)涵蓋 TWSE 開放 API 資料整合架構,可作為起點參考。
⑥ 已知邊界條件
- 假日 / 非交易日:BFI84U 回傳的是預告清單,即使非交易日仍可取得,資料為最後更新狀態。確認「今日是否交易日」需搭配交易日曆另行查詢。
- 筆數每日滾動:清單隨事件預告動態增減,不是固定筆數。距除息日較遠時筆數較少,密集除息季筆數較多。
Reason: 其他存在:這個概括性的值代表非前四類原因的暫停情境,自動化邏輯需能處理此值而不報錯。
⑦ See also
- TWSE OpenAPI 日期格式陷阱 — BFI84U ROC 7 碼日期屬五種格式中的「民國 7 碼」類別,完整日期格式分布在此
- TWSE BWIBBU_d 端點直取台股本益比與殖利率 — 同樣屬於
exchangeReport路徑的不同維度端點 - 台股新上市股票前 5 個交易日無漲跌幅限制 — 另一類「靜默誤判」案例:漲跌幅自動化邏輯套到新股的靜默錯誤
- TWSE OpenAPI Swagger:https://openapi.twse.com.tw/
- floviq 台股自動化工具:portaly.cc/floviq
修正紀錄
先前版本有以下更正:
- 開場/①/②:停的是融券賣出(停券)、融資買進不停 → 非「暫停融資融券買賣」。
- 坑 2:
MI_MARGN本身即帶註記欄標示停券/停資;兩端點差別在時間粒度 → 非「MI_MARGN無標記」。 - 坑 1:
parseRocDate("")回傳null→ 非NaN(僅省略防呆才會得NaN)。
※ 本文描述 TWSE BFI84U 端點欄位機制、工程坑與停券(暫停融券)預告查詢實況 · 不構成投資建議,投資請自行評估風險。
資料來源:TWSE BFI84U · TWSE MI_MARGN 註記欄圖例
如果這篇對你有用: