市場資料時間戳:SIP 與交易所時鐘
市場資料有四種時間戳。比較同筆成交依不同時鐘排序後的差異,了解各時鐘代表的意義,並找出不同情境下應採用的時間。
市場資料時間戳,是單筆成交紀錄從執行交易的撮合引擎傳送到顯示畫面時,沿途附上的時間。美國股票交易在公開紀錄中會帶有三種時間戳,每一種都回答不同問題。若先依其中一種時間排序同一天的成交紀錄,再改用另一種時間排序,你會得到兩份真正不同的成交序列。
單筆成交紀錄經過的四種時間
一筆成交紀錄在傳送到你手上前,會反覆被加上時間戳。依序如下:
- 撮合引擎時間。 交易場所的撮合引擎撮合兩筆委託的瞬間。交易場所外部無法直接讀取這個數值。它是交易實際發生時間的基準,之後的每個時間戳都只是對它的近似。
- 參與者時間,也稱交易場所或交易所時間。 交易場所透過自己的資料流發布成交紀錄時寫入的時間戳,位於
participant_timestamp欄位。在所有你實際能讀取的時間中,這個時間最接近撮合引擎時間。 - SIP 時間。 成交紀錄抵達證券資訊處理器時,由該處理器寫入的時間戳。SIP 是整合所有美國股票交易場所的單一官方資料流。這是
sip_timestamp欄位,官方成交序列也依此時間排列。該資料流與交易場所自有資料流的差異,詳見SIP 與交易所直接資料流。 - 擷取時間。 你的網路卡在封包抵達時寫入的時間戳。供應商的紀錄中不會出現這個時間,因為它描述的是你的傳輸路徑,而不是市場。封包擷取與重播作業完全依賴這個時間。
場外成交紀錄還會帶有第五種時間戳,即 trf_timestamp,標示交易報告機制接收報告的時間。
為何不同交易場所的市場資料時間戳不一致
交易場所時間戳與整合時間戳之間的差距,就是成交紀錄在傳輸途中及處理器佇列中所花的時間。這不是單一固定數值。每個交易場所與處理器的距離不同,使用的硬體不同,面對的佇列也不同。下方面板統計了 2026年6月10日固定半小時內,所有曾產生 AAPL 成交紀錄的交易場所,其時間差。
每個數據背後的精確 SQL 語法
WITH venues AS
(
SELECT
toUInt32(id) AS exchange_id,
any(coalesce(nullIf(acronym, ''), name)) AS venue_name
FROM global_markets.stocks_exchanges
WHERE asset_class = 'stocks'
GROUP BY exchange_id
)
SELECT
if(v.venue_name = '', concat('Venue ', toString(t.exchange)), v.venue_name) AS venue,
count() AS print_count,
round(quantileDeterministic(0.5)(
toFloat64(toUnixTimestamp64Nano(t.sip_timestamp)
- toUnixTimestamp64Nano(t.participant_timestamp)) / 1000,
toUInt64(t.sequence_number)), 1) AS median_lag_us,
round(quantileDeterministic(0.99)(
toFloat64(toUnixTimestamp64Nano(t.sip_timestamp)
- toUnixTimestamp64Nano(t.participant_timestamp)) / 1000,
toUInt64(t.sequence_number)), 1) AS p99_lag_us
FROM global_markets.stocks_trades AS t
LEFT JOIN venues AS v ON v.exchange_id = toUInt32(t.exchange)
WHERE t.ticker = 'AAPL'
AND t.sip_timestamp >= '2026-06-10 14:30:00'
AND t.sip_timestamp < '2026-06-10 15:00:00'
AND ifNull(toUnixTimestamp64Nano(t.trf_timestamp), 0) = 0
GROUP BY venue
HAVING count() >= 200
ORDER BY median_lag_us DESC
LIMIT 15在這些交易場所中,交易場所時間戳與整合時間戳之間的最大中位數差距為 346.2 微秒,出現在 NYSE Arca, Inc.。同一時段內,差距最小的交易場所為 13.7 微秒。最值得關注的是 p99 欄位:同一個最慢交易場所的數值達到 420.8 微秒,呈現中位數所掩蓋的尾端延遲。
應使用哪一種時間戳
幾乎所有情況都可遵循以下四項規則。
- 微觀結構研究與事件研究使用參與者時間。 凡是衡量交易場所發生了什麼,以及事件發生順序的研究,都應使用交易場所時間。訂單簿重建,也就是MBO 與 MBP 訂單簿資料的主題,使用其他時間都無法可靠完成。
- 凡是必須與官方成交序列核對的內容,使用 SIP 時間。 包括監管申報、最佳執行檢視、官方開盤與收盤,以及交易對手會拿來與整合紀錄核對的任何數值。
- 只有在衡量自身傳輸路徑時,才使用擷取時間。 它能告訴你資料花了多久抵達自己的機器,但無法告訴你交易何時發生,而且兩台機器的擷取時間永遠不會一致。
- 同一份資料集內絕對不要混用時間。 若用一種時間連結報價、再用另一種時間連結成交,所得數字看似合理,卻會在最重要的時刻出現錯誤。
同一批成交紀錄,依兩種方式排序
排序會讓抽象概念變得具體。下方面板取出該半小時內最繁忙的十毫秒,保留交易場所成交紀錄中依交易場所順序排列的前十二筆,再以整合成交序列重新排列相同的十二筆。表格依每筆成交在兩種排名之間移動的幅度排序。
每個數據背後的精確 SQL 語法
WITH
burst AS
(
SELECT intDiv(toUnixTimestamp64Nano(participant_timestamp), 10000000) AS slice_10ms
FROM global_markets.stocks_trades
WHERE ticker = 'AAPL'
AND sip_timestamp >= '2026-06-10 14:30:00'
AND sip_timestamp < '2026-06-10 15:00:00'
AND ifNull(toUnixTimestamp64Nano(trf_timestamp), 0) = 0
GROUP BY slice_10ms
ORDER BY count() DESC, slice_10ms ASC
LIMIT 1
),
sample AS
(
SELECT
participant_timestamp,
sip_timestamp,
toUInt64(sequence_number) AS seq,
toUnixTimestamp64Nano(sip_timestamp)
- toUnixTimestamp64Nano(participant_timestamp) AS lag_ns
FROM global_markets.stocks_trades
WHERE ticker = 'AAPL'
AND sip_timestamp >= '2026-06-10 14:30:00'
AND sip_timestamp < '2026-06-10 15:00:00'
AND ifNull(toUnixTimestamp64Nano(trf_timestamp), 0) = 0
AND intDiv(toUnixTimestamp64Nano(participant_timestamp), 10000000)
IN (SELECT slice_10ms FROM burst)
ORDER BY participant_timestamp ASC, seq ASC
LIMIT 12
),
ranked AS
(
SELECT
participant_timestamp,
sip_timestamp,
lag_ns,
row_number() OVER (ORDER BY participant_timestamp ASC, seq ASC) AS participant_rank,
row_number() OVER (ORDER BY sip_timestamp ASC, seq ASC) AS sip_rank
FROM sample
)
SELECT
concat('P', leftPad(toString(participant_rank), 2, '0')) AS print_label,
concat(formatDateTime(toTimeZone(participant_timestamp, 'America/New_York'), '%H:%i:%S'), '.',
leftPad(toString(intDiv(toUnixTimestamp64Nano(participant_timestamp) % 1000000000, 1000)), 6, '0')) AS venue_clock_et,
concat(formatDateTime(toTimeZone(sip_timestamp, 'America/New_York'), '%H:%i:%S'), '.',
leftPad(toString(intDiv(toUnixTimestamp64Nano(sip_timestamp) % 1000000000, 1000)), 6, '0')) AS tape_clock_et,
participant_rank,
sip_rank,
abs(toInt32(sip_rank) - toInt32(participant_rank)) AS places_moved,
round(lag_ns / 1000, 1) AS sip_lag_delta_us
FROM ranked
ORDER BY places_moved DESC, participant_rank ASC單筆成交的兩種排名之間,最大差距為 6。該筆成交在 10:44:17.160712 離開交易場所,305.7 微秒後於 10:44:17.161018 抵達成交序列,排名也從交易場所時間的第 6 位移至成交序列的第 12 位。兩種排序都沒有錯,因為它們回答的是不同問題。若以成交序列時間進行交易順序研究,這段成交密集區會呈現出任何交易場所都未曾產生的順序;若以交易場所時間進行最佳執行檢視,結果則會與官方紀錄不一致。
場外成交紀錄遠晚於交易事件抵達
在交易所外執行的交易,例如由批發商或暗池執行的交易,會報告給交易報告機制,而不是在公開訂單簿上撮合。報告會帶有執行時間,但之後才抵達成交序列。這段間隔屬於報告延遲,而非傳輸時間,規模也大得多。
每個數據背後的精確 SQL 語法
WITH off_exchange AS
(
SELECT
(toUnixTimestamp64Nano(sip_timestamp)
- toUnixTimestamp64Nano(participant_timestamp)) / 1000000.0 AS delay_ms
FROM global_markets.stocks_trades
WHERE ticker = 'AAPL'
AND sip_timestamp >= '2026-06-10 14:30:00'
AND sip_timestamp < '2026-06-10 15:00:00'
AND ifNull(toUnixTimestamp64Nano(trf_timestamp), 0) > 0
)
SELECT
multiIf(delay_ms < 1, 'under 1 ms',
delay_ms < 10, '1 to 10 ms',
delay_ms < 100, '10 to 100 ms',
delay_ms < 1000, '100 ms to 1 s',
delay_ms < 10000, '1 s to 10 s',
'over 10 s') AS reporting_delay,
count() AS print_count,
round(100 * count() / (SELECT count() FROM off_exchange), 2) AS share_pct
FROM off_exchange
GROUP BY reporting_delay
ORDER BY min(delay_ms) ASC在該時段的場外 AAPL 成交紀錄中,有 24.8% 筆落入 under 1 ms 區間。尾端延伸至 1 s to 10 s 區間,其中包含 74 筆。一筆晚到十秒的成交紀錄,仍會帶有實際執行時的交易場所時間,但在成交序列中會落後十秒。若依成交序列時間排序,它看起來就像出現在錯誤的分鐘。以正常順序以外方式申報的成交紀錄,會帶有相應的銷售條件代碼;交易條件代碼的用途之一,就是標示這類情況。
為何兩個人會用同一批成交紀錄建立出不同的 K 線
幾乎所有「資料有誤」的問題,最後都會回到這裡。K 線是成交紀錄的分組,而一筆成交歸入哪一組,取決於你使用哪一個時間戳進行分組。下方面板統計了在四種常見 K 線週期下,從交易場所時間切換至成交序列時間後,成交紀錄發生分組變化的數量。
每個數據背後的精確 SQL 語法
WITH
prints AS
(
SELECT
toUnixTimestamp64Nano(participant_timestamp) AS venue_ns,
toUnixTimestamp64Nano(sip_timestamp) AS tape_ns
FROM global_markets.stocks_trades
WHERE ticker = 'AAPL'
AND sip_timestamp >= '2026-06-10 14:30:00'
AND sip_timestamp < '2026-06-10 15:00:00'
),
grids AS
(
SELECT arrayJoin([1, 10, 60, 300]) AS bar_seconds
)
SELECT
multiIf(bar_seconds = 1, '1 second',
bar_seconds = 10, '10 seconds',
bar_seconds = 60, '1 minute',
'5 minutes') AS bar_length,
countIf(intDiv(venue_ns, toInt64(bar_seconds) * 1000000000)
!= intDiv(tape_ns, toInt64(bar_seconds) * 1000000000)) AS moved_print_count,
round(100 * countIf(intDiv(venue_ns, toInt64(bar_seconds) * 1000000000)
!= intDiv(tape_ns, toInt64(bar_seconds) * 1000000000)) / count(), 3) AS moved_pct
FROM prints
CROSS JOIN grids
GROUP BY bar_seconds
ORDER BY bar_seconds ASC在 1 second 的時間網格下,該時段中有 8.966% 的成交紀錄會因兩種時間不同而落入不同 K 線,共 8944 筆。將 K 線週期延長至 5 minutes 後,這個數值降至 0.024%。這個現象完全由機械性規則決定:只要兩種時間的差距跨越某個分界點,成交紀錄就會改變所屬分組;而較短的 K 線週期包含更多分界點。因此,兩家供應商都可能正確,卻仍會公布同一分鐘的不同成交量。其建立方式詳見OHLCV K 線的建立方式。
奈秒欄位不代表奈秒精確度
兩種時間戳都以具備奈秒解析度的整數呈現。解析度代表欄位能表達的細緻程度。精確度則代表該數值與真實時間的接近程度,兩者由完全不同的因素決定。
業界的時鐘同步受監管容許範圍,而非物理極限所規範。FINRA 的時鐘同步規則要求會員公司的業務時鐘與 NIST 參考時間相差不得超過 50 毫秒。交易所與處理器使用 Precision Time Protocol(PTP,標準化為 IEEE 1588)運作得更為精密。該協定透過承載資料的同一網路傳送參考時鐘,讓機器維持在次微秒級同步。
這會帶來兩項結論。在同一組織的時間戳之間,以微秒解析度判斷順序具有意義。在不同組織之間,兩個時間戳相差數百奈秒,可能仍落在誤差範圍內;若將其視為真實順序,就是把雜訊當成訊號。
常見問題
SIP 時間戳與參與者時間戳有何不同?
參與者時間戳由交易場所發布自有交易資料流時寫入。SIP 時間戳則由整合成交序列處理器在交易抵達官方整合資料流時寫入。兩者的差距就是傳輸與排隊時間;交易所成交紀錄通常以微秒計算,而透過交易報告機制申報的成交紀錄,延遲往往以毫秒甚至更長時間計算。
回測應使用哪一種市場資料時間戳?
凡是模擬市場參與者在某個交易場所能看到或執行什麼,都應使用參與者時間戳。凡是必須與官方整合紀錄核對的內容,則應使用 SIP 時間戳。無論選擇哪一種,都必須套用至研究中的所有資料表,包括報價資料。
為何我的一分鐘 K 線與資料供應商的不一致?
最常見的原因是時間戳不一致。一筆交易場所時間戳剛好落在分鐘分界前的成交紀錄,可能在成交序列中於分界後才出現。依兩種規則分組,同一筆交易就會落入不同 K 線。較晚申報的場外成交紀錄會進一步放大差異。
奈秒時間戳是否精確到奈秒?
不是。欄位具備奈秒解析度,但精確度取決於寫入機器的時鐘同步程度。使用 PTP 的交易所與處理器系統可維持次微秒級同步,而券商業務時鐘則受 50 毫秒的監管容許範圍規範。若比較精度高於同步程度較低的時鐘容許範圍,結果便沒有意義。
上述每個面板都附有產生結果的 SQL,因此你可以清楚確認每個數值所使用的時間戳。若要將自己的成交紀錄區間改用另一種時間重新排序,觀察成交序列如何變化,可以在 Strasmore terminal 以自然語言提出問題。