2026年九月3日 · 資料工程

不存在的分鐘:OHLCV 歷史資料中的缺口、停牌與零成交量 K 棒

不存在的分鐘:OHLCV 歷史資料中的缺口、停牌與零成交量 K 棒

一整年的 1-minute K 棒應該有 525,600 筆。我們抓取 2025 年 BTCUSDT 永續合約資料,拿到 525,557 筆——少了 43 分鐘,完整率為 99.992%。同一交易所、同一次抓取、同一段程式流程的一種中型市值山寨幣永續合約,則少了 1,247 筆:完整率 99.76%。同一年份的美股分鐘資料,大約比加密貨幣資料少 427,000 筆;那根本不是資料缺口,只是市場會休市。

這些數字裡有 3 個沒什麼特別。值得千言萬語的是 43。

525,600非閏年的 1-minute K 棒數
0.008%2025 年 BTCUSDT 的缺漏比例
61%的缺漏分鐘落在波動度前 10% 的時段

4 種看起來都像資料框缺了一塊的情況

在談那 43 分鐘之前,先釐清分類,因為多數處理缺口的程式碼一開始就搞錯了。當本機 parquet 裡少了一列,你根本不知道是以下哪種情況造成的,而每種情況的正確處理方式都不同。

類型實際發生的事資料呈現方式正確處理方式
沒有成交市場有開盤,但那分鐘沒有人跨價差成交有些端點會提供零成交量 K 棒(OHLC 全相同,trades=0),其他端點則完全不提供該列保留。這是真實資訊:沒有人想交易。
交易所當機撮合引擎離線,可能是預定維護,也可能不是缺少資料列,有時會連續缺一段標記為資料過期。不要在這段期間交易。
商品暫停交易觸及 LULD 價格帶、等待新聞,或收到下市通知缺少資料列,之後出現重新開盤的競價成交標記為資料過期,並將重新開盤視為不連續點。
我們自己的問題分頁請求遭限流、重試時漏掉一頁,或迴圈裡有時區邊界錯誤缺少資料列,和上述情況無從分辨偵測後重新抓取。這是唯一能修好的類型。

交易所對表格裡的第一種情況處理不同,這正是容易出問題的地方。Coinbase 的 K 線端點會完全略過沒有成交的時段,所以流動性低的交易對歷史資料裡滿是實際缺口。Binance 的 klines 通常會提供合成 K 棒,成交量為 0,open=high=low=close 都沿用前一筆成交價。同一個事實,卻有兩種不同呈現方式;而載入器若重新索引成完整的分鐘網格,就會在不告知你的情況下把其中一種轉成另一種。動手寫補缺程式之前,先確認交易所的資料格式。

山寨幣缺少的 1,247 分鐘幾乎全屬於第一類:週日 UTC 04:00 的委託簿很薄,沒有人交易。雖然麻煩,但容易理解,而且大致無害,因為策略本來就不會在那時交易。也正因如此,我不再盯著它看,轉而回頭追查那 43 分鐘。

那 43 分鐘並非零星散落

如果缺漏是均勻分布的,一年裡的 43 分鐘就會是 43 個孤立的單點,大約每 8.5 天出現 1 個,每個都只是四捨五入等級的誤差。但實際情況並非如此。它們分成 6 段:1 段連續 19 分鐘,1 段 11 分鐘,2 段各 4 分鐘,還有幾段各 2 分鐘。這是 6 次事件,不是 43 次意外。

而且這些事件會隨你在意的條件而變化。交易所會在負載過高時當機,而價格波動時負載也會升高。我把全年的每個小時依實現波動度分組,再查看缺漏分鐘落在哪裡:61% 都在波動度最高的 10% 時段。任意 1 分鐘缺漏的無條件機率是 0.008%。若限定在波動度最高的 10% 時段,機率約為 0.05%——高出 6 倍,而且缺漏還會成群出現。

所以資料品質儀表板上的完整率指標,衡量的不是重點。99.992% 聽起來像是你可以不用再操心的資料集;但它實際描述的是:策略不做事的時段資料完整,策略全力運作的時段卻有缺口。遇到波動擴大就進場的動能系統,碰上缺口的機率遠高於那個總體數字暗示的程度,而且缺口可能正好出現在持倉期間。

向前填補 3 行之後會發生什麼事

這正是讓我動筆寫這篇文章的問題。以那段連續 19 分鐘的缺口為例。標準整理流程是:重新索引成完整的分鐘網格、用最後收盤價向前填補 OHLC,再把成交量設為 0。現在序列連續了,指標也能照常計算,完全看不到 NaN。

這 19 根 K 棒的 high == low == close。每一根的真實波幅都是 0。在這段期間計算 ATR(14),若當機前讀值約為 240 USDT,等交易所恢復時就會一路降到約 34——整個平均值幾乎只由期間內剩下的 5 根真實 K 棒決定。接著把它輸入常見的波動度縮放部位規模計算器,例如 size = risk_budget / ATR。部位規模會增加 7 倍。

下一根真實 K 棒是重新開盤的成交價,而且絕不是平靜的一根。以我們的例子來說,開盤價比當機前最後收盤價偏離了 1.8%。回測卻輕鬆地以 7 倍部位進場,迎向 1.8% 的價格缺口;這筆成交不可能發生,當時也沒有人掛出那個價格。這筆合成交易對權益曲線的影響,超過 1 個月的真實損益,而且方向還錯——它完全是為了讓資料框整齊而寫的一行資料清理程式碼造成的。

直接刪除資料列、不做填補也不是解法;只是同一個錯誤換了副面孔。刪掉資料列後,以整數索引計算的回看區間會開始說謊:「20-bar EMA」現在跨越了當機前後總共 39 分鐘;邊界兩側的 K 棒報酬會把整個 1.8% 跳空當成 1 分鐘的變動;每根 K 棒的波動度估計則會把它讀成 60-sigma 事件。沒有任何警示,索引仍然單調遞增。

重採樣會讓問題悄悄消失

多數研究不是用 1-minute K 棒,而是用聚合後的資料;聚合會把問題洗掉。重採樣成 5-minute K 棒後,19 分鐘缺口會變成 4 根 K 棒,其中第一根和最後一根是不完整的。Pandas 可以用只剩下的 2 分鐘資料算出看起來完全合理的 OHLC,標記方式卻和用 5 分鐘資料建出的 K 棒一模一樣。輸出結果裡沒有任何資訊能區分兩者。

我知道最簡單的修法:每次重採樣都保留 bars_in_window 欄位,絕不丟掉。每列多一個整數,後續所有判斷 K 棒是否可信的問題就都有答案。我們也會保留 seconds_since_last_real_print,以便執行層直接採取動作。

我們目前的處理原則

目前 agents 套用的順序如下:

  1. 絕不默默重新索引。載入器會產生缺口清單,列出開始時間、結束時間、長度,以及判定屬於 4 種類型中的哪一種。如果缺漏少於 3 根 K 棒,而且前後 K 棒的成交量都很低,就視為沒有成交的分鐘。交易活躍時段的缺漏只要超過這個長度,就先當成交易所當機,直到證明另有原因。
  2. 解讀之前先重新抓取。我們早期的缺口有一半是分頁錯誤造成的。改用不同端點或不同供應商再抓一次,可以找出第四類問題,在需要判斷之前先縮小範圍。
  3. 設定資料過期門檻,不補缺。策略會收到 data_age 輸入,並遵守嚴格規則:最後一筆真實成交超過 N 根 K 棒時,不開新倉;未平倉部位則只在重新開盤時透過市價單平倉,並以明確折減的跳空風險估算價格。無法實際成交的回測,比沒做成的交易更糟。
  4. 指標看到 NaN,而不是虛構價格。向前填補的價格永遠不會進入特徵層。ATR 若無法計算,就視為未定義;未定義就代表空手。明確報錯,勝過悄悄放大 7 倍。
  5. 回報缺口條件下的績效。我們交付的每份回測都會呈現排除當機前後交易後的損益,並與整體數字並列。如果績效是靠那些交易撐起來的,那只是資料造成的假象。

今天就能做個簡單檢查:把缺漏分鐘依連續區段分組,再查看回測中有多少比例的交易在區段邊界前後 30 分鐘內開倉或平倉。低於 1% 的話,缺口可能沒造成什麼影響;若達到 5% 或更高,權益曲線就有一部分是在講交易所停擺的故事。

我現在最信任的判斷線索,是缺漏的形狀,而不是數量。閒置時段裡散落著幾千個零星缺口的資料集,通常沒問題。只有幾段緊密群聚缺口的資料集,則是在告訴你某些東西會在高負載下故障;而會在高負載下故障的地方,正是你的策略運作之處。

OHLCV 缺口資料工程回測重採樣加密貨幣期貨
← 所有文章