ความแตกต่างของ Market data timestamps ในตลาดหุ้น
เรียนรู้ความแตกต่างของนาฬิกาประทับเวลาสี่ประเภทในข้อมูลการซื้อขาย และผลกระทบต่อการเรียงลำดับข้อมูลในเทปบันทึกการซื้อขาย รวมถึงวิธีการเลือกใช้ให้เหมาะสมกับวัตถุประสงค์ของคุณ
Market data timestamps คือเวลาที่ประทับลงบนข้อมูลการซื้อขาย (trade print) ในขณะที่ข้อมูลเดินทางจาก matching engine ที่ทำการจับคู่คำสั่งซื้อขายไปยังหน้าจอแสดงผล ในตลาดหุ้นสหรัฐฯ ข้อมูลการซื้อขายหนึ่งรายการจะมี timestamp สามชุดในบันทึกสาธารณะ ซึ่งแต่ละชุดมีวัตถุประสงค์ที่แตกต่างกัน หากคุณเรียงลำดับข้อมูลการซื้อขายในวันเดียวกันโดยใช้ clock คนละตัว คุณจะได้เทปบันทึกการซื้อขายที่แตกต่างกันอย่างสิ้นเชิง
นาฬิกาทั้งสี่ประเภทที่ข้อมูลการซื้อขายต้องผ่าน
ข้อมูลการซื้อขายจะถูกประทับเวลาซ้ำหลายครั้งระหว่างทางมาถึงคุณ โดยมีลำดับดังนี้:
- Matching engine time: ช่วงเวลาที่ matching engine ของตลาดจับคู่คำสั่งซื้อขายสองรายการเข้าด้วยกัน ไม่มีใครภายนอกตลาดที่สามารถอ่านค่านี้ได้โดยตรง นี่คือความจริงพื้นฐานว่าการซื้อขายเกิดขึ้นเมื่อใด และ clock ทุกตัวหลังจากนี้เป็นเพียงค่าประมาณการเท่านั้น
- Participant time หรือที่เรียกว่า venue หรือ exchange time: คือเวลาที่ตลาดประทับไว้เมื่อเผยแพร่ข้อมูลการซื้อขายบน feed ของตนเอง ซึ่งอยู่ในฟิลด์
participant_timestampในบรรดาข้อมูลที่คุณสามารถอ่านได้จริง ค่านี้ถือว่าใกล้เคียงกับ matching engine มากที่สุด - SIP time: คือเวลาที่ securities information processor ประทับไว้เมื่อข้อมูลการซื้อขายไปถึง consolidated tape ซึ่งเป็น feed ทางการเพียงแห่งเดียวที่รวมข้อมูลจากทุกตลาดหุ้นในสหรัฐฯ ค่านี้อยู่ในฟิลด์
sip_timestampและลำดับของเทปทางการจะยึดตามเวลานี้ ความแตกต่างระหว่าง feed นี้กับ feed ของตลาดโดยตรงได้อธิบายไว้ใน SIP versus direct exchange feeds - Capture time: คือเวลาที่การ์ดเครือข่ายของคุณประทับไว้เมื่อได้รับแพ็กเก็ตข้อมูล ค่านี้จะไม่ปรากฏในบันทึกของผู้ให้บริการข้อมูล เนื่องจากเป็นเวลาที่อธิบายเส้นทางของคุณเองไม่ใช่ตลาด งานด้าน Packet capture and replay จะใช้ clock นี้ทั้งหมด
ข้อมูลการซื้อขายที่เกิดขึ้นนอกตลาด (off exchange) จะมี timestamp ชุดที่ห้าคือ trf_timestamp ซึ่งระบุเวลาที่ trade reporting facility ได้รับรายงานการซื้อขายนั้น
เหตุใด timestamp ของข้อมูลตลาดจึงไม่ตรงกันในแต่ละตลาด
ช่องว่างระหว่างเวลาของตลาดกับเวลาของ consolidated tape คือระยะเวลาที่ข้อมูลใช้ในการเดินทางและอยู่ในคิวของตัวประมวลผล ซึ่งไม่ใช่ตัวเลขเดียวที่ตายตัว แต่ละตลาดมีระยะห่างจากตัวประมวลผลไม่เท่ากัน ใช้ฮาร์ดแวร์ต่างกัน และอยู่หลังคิวที่ต่างกัน ตารางด้านล่างวัดช่องว่างดังกล่าวสำหรับทุกตลาดที่มีการซื้อขาย AAPL ในช่วงเวลา 30 นาทีของวันที่ 10 มิถุนายน 2026
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ในบรรดาตลาดเหล่านั้น ช่องว่างมัธยฐานที่กว้างที่สุดระหว่างเวลาของตลาดและเวลาของ consolidated tape คือ 346.2 ไมโครวินาที ที่ NYSE Arca, Inc. ส่วนตลาดที่มีช่องว่างแคบที่สุดในช่วงเวลาเดียวกันคือ 13.7 ไมโครวินาที คอลัมน์ p99 คือจุดที่ควรให้ความสนใจ: สำหรับตลาดที่ช้าที่สุด ค่านี้พุ่งไปถึง 420.8 ไมโครวินาที ซึ่งเป็นค่าหาง (tail) ที่ค่ามัธยฐานซ่อนไว้
คุณควรใช้ timestamp ใด
กฎสี่ข้อนี้ครอบคลุมเกือบทุกกรณี:
- Participant time สำหรับงานด้าน microstructure และ event studies: สิ่งใดก็ตามที่วัดเหตุการณ์ที่เกิดขึ้นในตลาดและลำดับเหตุการณ์ ต้องใช้ clock ของตลาดนั้น การสร้าง order book ขึ้นใหม่ ซึ่งเป็นหัวข้อของ MBO versus MBP order book data จะไม่สามารถใช้ clock อื่นได้
- SIP time สำหรับสิ่งที่ต้องกระทบยอดกับเทปทางการ: ไม่ว่าจะเป็นการรายงานต่อหน่วยงานกำกับดูแล การตรวจสอบ best execution การเปิดและปิดตลาดอย่างเป็นทางการ และตัวเลขใดก็ตามที่คู่สัญญาจะใช้ตรวจสอบกับบันทึกรวม
- Capture time สำหรับการวัดเส้นทางของคุณเองเท่านั้น: มันบอกคุณว่าข้อมูลใช้เวลานานเท่าใดกว่าจะมาถึงเครื่องของคุณ แต่มันไม่ได้บอกอะไรเกี่ยวกับเวลาที่การซื้อขายเกิดขึ้นจริง และเครื่องสองเครื่องไม่มีทางมีค่านี้ตรงกัน
- ห้ามใช้ clock ปนกันในชุดข้อมูลเดียว: การ join ข้อมูลโดยจับคู่ quote จาก clock หนึ่งกับ trade จากอีก clock หนึ่ง จะให้ตัวเลขที่ดูเหมือนเป็นไปได้ แต่จะล้มเหลวในจังหวะที่สำคัญที่สุด
ข้อมูลการซื้อขายชุดเดียวกันที่เรียงลำดับสองแบบ
การเรียงลำดับคือจุดที่แนวคิดเรื่องความเป็นนามธรรมพังทลายลง ตารางด้านล่างนำช่วงเวลา 10 มิลลิวินาทีที่หนาแน่นที่สุดในช่วงครึ่งชั่วโมงนั้นมาแสดง โดยคงลำดับข้อมูลการซื้อขาย 12 รายการแรกตามเวลาของตลาด จากนั้นจัดลำดับ 12 รายการเดิมใหม่ตามเวลาของ consolidated tape ตารางนี้เรียงลำดับตามระยะห่างที่ข้อมูลแต่ละรายการเคลื่อนที่ระหว่างการจัดลำดับทั้งสองแบบ
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 บนเทป ไม่มีลำดับใดที่ผิด ทั้งสองตอบคำถามที่ต่างกัน การศึกษาลำดับการซื้อขายที่รันบนเวลาของเทปจะอ่านข้อมูลชุดนี้ในลำดับที่ไม่มีตลาดใดเคยผลิตออกมา และการตรวจสอบ best execution ที่รันบนเวลาของตลาดก็จะขัดแย้งกับบันทึกทางการ
ข้อมูลการซื้อขายที่เกิดขึ้นนอกตลาดจะมาถึงช้ากว่าเหตุการณ์จริงมาก
การซื้อขายที่เกิดขึ้นนอกตลาด (off exchange) ไม่ว่าจะเป็นที่ wholesaler หรือ dark pool จะถูกรายงานไปยัง trade reporting facility แทนที่จะถูกจับคู่ในสมุดคำสั่งซื้อขายสาธารณะ รายงานดังกล่าวจะมีเวลาที่เกิดการซื้อขายจริง และจะมาถึงเทปในภายหลัง ช่วงเวลานี้คือความล่าช้าในการรายงาน (reporting delay) ไม่ใช่เวลาในการเดินทาง และมีความยาวมากกว่าหลายเท่าตัว
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% รายการตกลงในถังข้อมูล (bucket) under 1 ms ส่วนหางของข้อมูลยาวไปถึงถัง 1 s to 10 s ซึ่งมีข้อมูล 74 รายการ ข้อมูลที่มาถึงช้าสิบวินาทียังคงมีเวลาของตลาดที่เกิดขึ้นจริง แต่จะไปปรากฏในกระแสข้อมูลของเทปที่อยู่ถัดไปสิบวินาที หากเรียงลำดับตามเวลาของเทป ข้อมูลจะไปปรากฏในนาทีที่ผิด ข้อมูลที่รายงานนอกลำดับปกติจะมีรหัสเงื่อนไขการขาย (sale condition codes) ระบุไว้ ซึ่งเป็นหนึ่งในสิ่งที่ trade condition codes มีไว้เพื่อแจ้งเตือน
เหตุใดสองคนจึงสร้างแท่งกราฟจากข้อมูลการซื้อขายชุดเดียวกันได้ต่างกัน
ตั๋วปัญหาที่ระบุว่า "ข้อมูลผิด" เกือบทั้งหมดจบลงที่จุดนี้ แท่งกราฟคือถังรวมข้อมูลการซื้อขาย และถังที่ข้อมูลจะตกลงไปนั้นขึ้นอยู่กับว่าคุณใช้ timestamp ใดในการจัดกลุ่ม ตารางด้านล่างนับจำนวนข้อมูลการซื้อขายที่เปลี่ยนถังเมื่อคุณเปลี่ยนจาก clock ของตลาดมาเป็น clock ของเทป โดยแบ่งตามความยาวของแท่งกราฟสี่แบบที่พบบ่อย
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% ของรายการในช่วงเวลานั้นจะตกลงในแท่งกราฟที่ต่างกันภายใต้ clock ทั้งสองตัว รวมเป็นข้อมูล 8944 รายการ หากขยายแท่งกราฟเป็น 5 minutes ตัวเลขจะลดลงเหลือ 0.024% รูปแบบนี้เป็นไปตามกลไก: ข้อมูลจะเปลี่ยนถังเมื่อใดก็ตามที่ช่องว่างของ clock คร่อมเส้นแบ่ง และแท่งกราฟที่สั้นกว่าจะมีเส้นแบ่งมากกว่า ผู้ให้บริการข้อมูลสองรายอาจถูกต้องทั้งคู่แต่ยังคงเผยแพร่ปริมาณการซื้อขายที่ต่างกันสำหรับนาทีเดียวกัน กระบวนการสร้างแท่งกราฟได้อธิบายไว้ใน how OHLCV bars are built
ฟิลด์ระดับนาโนวินาทีไม่ได้หมายถึงความแม่นยำระดับนาโนวินาที
timestamp ทั้งสองชุดมาในรูปแบบจำนวนเต็มที่มีความละเอียดระดับนาโนวินาที ความละเอียด (resolution) คือสิ่งที่ฟิลด์สามารถแสดงได้ ส่วนความแม่นยำ (accuracy) คือความใกล้เคียงของค่าที่ได้กับเวลาจริง ซึ่งทั้งสองอย่างถูกกำหนดโดยปัจจัยที่ต่างกันโดยสิ้นเชิง
การปรับเทียบเวลาในอุตสาหกรรมถูกควบคุมโดยเกณฑ์ความคลาดเคลื่อนที่หน่วยงานกำกับดูแลกำหนด ไม่ใช่โดยหลักฟิสิกส์ กฎการซิงโครไนซ์เวลาของ FINRA กำหนดให้ clock ของบริษัทสมาชิกต้องมีความคลาดเคลื่อนไม่เกิน 50 มิลลิวินาทีเมื่อเทียบกับมาตรฐานของ NIST ส่วนตลาดและตัวประมวลผลจะใช้มาตรฐานที่เข้มงวดกว่ามาก โดยใช้ Precision Time Protocol (PTP ซึ่งเป็นมาตรฐาน IEEE 1588) ซึ่งกระจายสัญญาณ clock อ้างอิงผ่านเครือข่ายเดียวกับที่ส่งข้อมูล ทำให้เครื่องจักรมีความคลาดเคลื่อนต่ำกว่าระดับไมโครวินาที
ผลที่ตามมามีสองประการ: ภายใน timestamp ขององค์กรเดียว การเรียงลำดับที่ความละเอียดระดับไมโครวินาทีมีความหมาย แต่ระหว่างองค์กร ความแตกต่างเพียงไม่กี่ร้อยนาโนวินาทีระหว่าง timestamp สองชุดนั้นถือว่าอยู่ในช่วงความคลาดเคลื่อน (error bar) และการนำมาใช้เป็นลำดับเหตุการณ์จริงถือเป็นการอ่านค่าสัญญาณรบกวน (noise)
คำถามที่พบบ่อย
SIP timestamp กับ participant timestamp ต่างกันอย่างไร?
Participant timestamp ถูกเขียนโดยตลาดเมื่อเผยแพร่ข้อมูลการซื้อขายบน feed ของตนเอง ส่วน SIP timestamp ถูกเขียนโดยตัวประมวลผล consolidated tape เมื่อข้อมูลการซื้อขายนั้นไปถึง feed รวมที่เป็นทางการ ช่องว่างระหว่างเวลาทั้งสองคือเวลาในการเดินทางและเวลาในคิว ซึ่งวัดเป็นไมโครวินาทีสำหรับข้อมูลในตลาด และมักเป็นมิลลิวินาทีหรือนานกว่านั้นสำหรับข้อมูลที่รายงานผ่าน trade reporting facility
ฉันควรใช้ timestamp ของข้อมูลตลาดตัวใดสำหรับการทำ backtesting?
ใช้ participant timestamp สำหรับสิ่งที่จำลองสิ่งที่ผู้เข้าร่วมตลาดสามารถเห็นหรือทำได้ในตลาดนั้น และใช้ SIP timestamp สำหรับสิ่งที่ต้องกระทบยอดกับบันทึกรวมที่เป็นทางการ ไม่ว่าคุณจะเลือกตัวใด ให้ใช้กับทุกตารางในการศึกษา รวมถึงข้อมูล quote ด้วย
เหตุใดแท่งกราฟหนึ่งนาทีของฉันจึงไม่ตรงกับของผู้ให้บริการข้อมูล?
คำตอบมักเกิดจาก clock ที่ไม่ตรงกัน ข้อมูลการซื้อขายที่มีเวลาของตลาดอยู่ก่อนเส้นแบ่งนาทีเพียงเล็กน้อย อาจมีเวลาของเทปอยู่หลังเส้นแบ่งนั้น ซึ่งทำให้การซื้อขายเดียวกันไปอยู่ในแท่งกราฟที่ต่างกันภายใต้เกณฑ์ทั้งสอง ข้อมูลนอกตลาดที่รายงานล่าช้าจะยิ่งทำให้ผลกระทบนี้กว้างขึ้น
timestamp ระดับนาโนวินาทีมีความแม่นยำถึงระดับนาโนวินาทีหรือไม่?
ไม่ ฟิลด์ดังกล่าวมีความละเอียดระดับนาโนวินาที แต่ความแม่นยำถูกกำหนดโดยความสามารถในการซิงโครไนซ์ clock ของเครื่องที่บันทึกข้อมูล ระบบของตลาดและตัวประมวลผลที่ใช้ PTP จะมีความคลาดเคลื่อนต่ำกว่าไมโครวินาที ในขณะที่ clock ของบริษัทโบรกเกอร์ถูกควบคุมด้วยเกณฑ์ความคลาดเคลื่อน 50 มิลลิวินาที การเปรียบเทียบที่ละเอียดกว่าเกณฑ์ความคลาดเคลื่อนของ clock ที่หยาบกว่านั้นไม่มีนัยสำคัญ
ทุกตารางด้านบนมาพร้อมกับ SQL ที่ใช้สร้างข้อมูล เพื่อให้คุณเห็นว่าตัวเลขแต่ละตัวมาจาก clock ใด หากต้องการเรียงลำดับข้อมูลการซื้อขายของคุณใหม่ภายใต้ clock อื่นและดูการเปลี่ยนแปลงของเทป ให้สอบถามด้วยภาษาอังกฤษทั่วไปบนเทอร์มินัล Strasmore