Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor · · Updated 2026-07-24

เจาะลึกโครงสร้างตลาด 29 มิถุนายน 2026

วิเคราะห์ข้อมูลการซื้อขายและราคาแบบละเอียดประจำวันที่ 29 มิถุนายน 2026 ครอบคลุมข้อมูล Tape, การแก้ไข Volume ตามรหัสข้อมูล และรายละเอียดไฟล์จาก FINRA

นี่คือข้อมูลประกอบเชิงลึกสำหรับ สรุปภาวะตลาดประจำวันที่ 29 มิถุนายน 2026: ข้อมูลดิบที่อยู่เบื้องหลังแท่งกราฟรายนาที — 156.1 ล้านรายการซื้อขาย และ 505.38 ล้านการอัปเดตราคา NBBO ภายในหนึ่งเซสชันของวันจันทร์ปกติ ทุกตัวเลขเป็นผลลัพธ์จากการสืบค้นข้อมูล (สามารถขยายส่วนต่างๆ เพื่อดูคำสั่ง SQL ที่แม่นยำ) และมีการรายงานรายละเอียดเชิงลึกสำหรับสองประเด็นที่ตรวจพบจากการตรวจสอบข้อมูลในวันนี้ ได้แก่ การแก้ไขปริมาณการซื้อขายตามข้อกำหนดของรหัสข้อมูลใน tape และไฟล์ข้อมูลจากผู้ให้บริการที่สิ้นสุดลงที่ตัวอักษร S อย่างผิดปกติ

รายงานการซื้อขาย รายการต่อรายการ

การตรวจสอบเซสชันต้องทำเป็นอันดับแรกและห้ามละเลย: ปฏิทินวันหยุดของตลาดหลักทรัพย์ระบุว่ามี 0 แถว สำหรับวันที่ 29 มิถุนายน 2026 และ SPY มีการบันทึกแท่งราคาแบบรายนาทีจำนวน 390 แท่ง ภายในช่วงเวลาทำการปกติ ซึ่งเป็นหนึ่งเซสชันเต็ม ตั้งแต่เวลา 9:30 น. ถึง 16:00 น. ตามเวลาของนิวยอร์ก สำหรับช่วงเวลาที่ขยายออกไป ข้อมูลการซื้อขายรวมบันทึกจำนวน 156.1 ล้านรายการ

คำสั่งดึงข้อมูลปริมาณการซื้อขายรายวัน: ข้อมูล session และขนาดของทุกรายการ
SQL เบื้องหลังตัวเลขแต่ละตัว
WITH
    (SELECT count() FROM global_markets.stocks_market_holidays WHERE date = '2026-06-29') AS holiday_rows,
    (
        SELECT countIf(window_start >= '2026-06-29 13:30:00' AND window_start < '2026-06-29 20:00:00')
        FROM global_markets.delayed_stocks_minute_aggs
        WHERE ticker = 'SPY' AND window_start >= '2026-06-29 00:00:00' AND window_start < '2026-06-30 00:00:00'
    ) AS spy_bars
SELECT
    holiday_rows AS holiday_rows_jun29,
    spy_bars AS spy_regular_session_bars,
    round(count() / 1e6, 1) AS trades_m,
    round(100.0 * countIf(size < 100) / count(), 2) AS odd_lot_pct_of_trades,
    round(100.0 * toFloat64(sumIf(size, size < 100)) / toFloat64(sum(size)), 2) AS odd_lot_pct_of_shares,
    multiIf(
        countIf(size <= 19) >= 0.5 * count(), 19,
        countIf(size <= 20) >= 0.5 * count(), 20,
        countIf(size <= 21) >= 0.5 * count(), 21,
        countIf(size <= 22) >= 0.5 * count(), 22,
        countIf(size <= 23) >= 0.5 * count(), 23,
        0) AS median_print_shares,
    round(countIf(size = 1) / 1e6, 1) AS one_share_trades_m,
    round(100.0 * countIf(size != round(size)) / count(), 2) AS fractional_pct_of_trades
FROM global_markets.stocks_trades
WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'

72.61% ของรายการซื้อขายทั้งหมดเป็น odd lots หรือการซื้อขายที่น้อยกว่า 100 หุ้น แต่รายการเหล่านี้มีปริมาณหุ้นรวมเพียง 8.79% ของจำนวนหุ้นทั้งหมด ซึ่งเป็นยอดรวมที่ยังไม่ได้ปรับปรุงซึ่งจะถูกตัดออกในส่วนการวิเคราะห์ปริมาณการซื้อขายด้านล่าง ค่ามัธยฐานของรายการซื้อขายคือ 21 หุ้น วัดโดยใช้การตรวจสอบแบบ bracket probe ซึ่งเป็นการนับสะสมที่ 19 ถึง 23 หุ้น โดยจะคืนค่าเป็น 0 หากการตรวจสอบไม่พบ และมีการกำหนดขอบเขตเพื่อไม่ให้ค่าที่ผิดพลาดส่งผลต่อผลลัพธ์ มีรายการซื้อขายจำนวน 17.9 ล้านรายการที่เป็นการซื้อขายเพียงหนึ่งหุ้น และ 4.73% รายการมีการซื้อขายในรูปแบบเศษหุ้น ซึ่งเป็นรูปแบบที่สอดคล้องกับการที่แอปพลิเคชันสำหรับรายย่อยส่งคำสั่งซื้อขายทีละส่วน โดยมี market makers เป็นคู่สัญญาอีกด้านหนึ่ง

การตรวจสอบปริมาณการซื้อขาย: การหา "matched volume" จาก dictionary

ผลรวมของฟิลด์ size ในทุกรายการ print แสดงให้เห็นว่าวันที่ 29 มิถุนายน มีการซื้อขายทั้งหมด 24.79 billion shares ตัวเลขนี้ไม่ถูกต้อง และข้อมูลใน tape ระบุไว้เช่นนั้น ทุกรายการ print จะมี condition codes และ SIP — Securities Information Processor ซึ่งรวบรวมข้อมูลจากทุกแหล่งมาไว้ใน stream เดียวกัน — จะเผยแพร่ dictionary ที่ระบุว่าแต่ละรหัสจะอัปเดต consolidated volume หรือไม่ ข้อความบางอย่างเป็นการส่งข้อมูลซ้ำ (administrative re-broadcasts) ของปริมาณการซื้อขายที่นับไปแล้ว แทนที่จะใช้วิธีระบุรายการรหัสที่ "รู้จัก" ไว้ล่วงหน้า บทความนี้จะหาชุดข้อมูลที่จะยกเว้น (exclusion set) จาก dictionary โดยตรง:

คำสั่งดึงข้อมูลชุดข้อมูลยกเว้นที่ได้จาก dictionary ของ tape (1 แถว)
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    count() AS excluded_codes,
    sum(id) AS excluded_id_checksum,
    arrayStringConcat(arraySort(groupArray(concat(toString(id), ' ', name))), ' · ') AS codes
FROM global_markets.stocks_condition_codes
WHERE asset_class = 'stocks'
  AND type = 'sale_condition'
  AND JSONExtractBool(update_rules, 'consolidated', 'updates_volume') = 0

เงื่อนไขการขาย 3 มีค่า updates_volume = false ในข้อมูล feed แบบ consolidated: 15 Market Center Official Close · 16 Market Center Official Open · 38 Corrected Consolidated Close (per listing market) บทเรียนที่ได้รับคือ: ร่างแรกของสรุปรายวันมีการระบุรหัส 15 และ 16 ไว้ล่วงหน้า ซึ่งเป็นข้อความเปิด/ปิดตลาดอย่างเป็นทางการที่คู่มือข้อมูลทุกฉบับระบุไว้ แต่กลับพลาดรหัส 38 ไปอย่างสิ้นเชิง ดังนั้น dictionary คือแหล่งข้อมูลที่ถูกต้อง (source of truth) ไม่ใช่รายการรหัสที่จดจำไว้

การใช้สอง block เป็นความตั้งใจ: การใช้ scalar subquery บน dictionary ภายในการ scan ข้อมูล 156.1-million-row ใช้เวลานานเกินขีดจำกัด 30 วินาทีของเรา ดังนั้น block ด้านบนจึงคำนวณหาชุดข้อมูลออกมาเป็นแถวเดียว โดยมีการจำกัดขอบเขตด้วยจำนวนรหัสและ id checksum (69) และ block ด้านล่างจะนำค่า literal ที่ได้ไปใช้งาน หาก dictionary เปลี่ยนแปลงไป ค่า checksum จะช่วยแจ้งเตือนให้ตรวจสอบบทความนี้ ทำให้ค่า literal ไม่ล้าสมัยโดยที่เราไม่ทราบ

คำสั่งดึงข้อมูลปริมาณการซื้อขายแบบ Raw เทียบกับ SIP โดยใช้ชุดข้อมูลยกเว้น
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    round(toFloat64(sum(size)) / 1e9, 2) AS raw_shares_bn,
    round(toFloat64(sumIf(size, NOT hasAny(conditions, [15, 16, 38]))) / 1e9, 2) AS matched_shares_bn,
    round(toFloat64(sumIf(size, hasAny(conditions, [15, 16, 38]))) / 1e9, 2) AS excluded_shares_bn,
    countIf(hasAny(conditions, [15, 16, 38])) AS excluded_prints
FROM global_markets.stocks_trades
WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'

การคำนวณที่ถูกต้อง: ปริมาณดิบคือ 24.79 billion shares, 20.66 billion คือจำนวนที่นับโดย SIP, และถูกยกเว้นไป 4.13 billion จากรายการ print เชิงบริหารจัดการ (administrative prints) จำนวน 91175 รายการ นิยามหนึ่งเดียวที่ระบุไว้อย่างชัดเจนคือ: "matched volume" หมายถึงปริมาณการซื้อขายที่กฎเงื่อนไขของ SIP นับรวมเข้ากับยอดรวมแบบ consolidated เท่านั้น ไม่มีความหมายอื่น เราไม่ได้ตัดสินว่าการซื้อขายใดมีความสำคัญทางเศรษฐกิจ แต่เรากำลังใช้หลักการบัญชีของ tape เอง

การตีความรายการซื้อขายขนาดใหญ่ที่สุด

รายการซื้อขายที่มีขนาดใหญ่ที่สุดของวัน คือจุดที่รหัสเงื่อนไข (condition codes) มีความสำคัญมากกว่าแค่ข้อมูลทั่วไป:

คำสั่งดึงข้อมูลรายการซื้อขายขนาดใหญ่ที่สุดของ 29 มิถุนายน (หักรหัสที่ยกเว้นใน dictionary)
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    ticker,
    formatDateTime(toTimeZone(sip_timestamp, 'America/New_York'), '%H:%i:%S') AS et_time,
    round(toFloat64(size) / 1e6, 2) AS shares_m,
    toFloat64(price) AS price,
    round(toFloat64(size) * toFloat64(price) / 1e6, 1) AS notional_usd_m,
    round(100 * (toFloat64(size) * toFloat64(price)) / max(toFloat64(size) * toFloat64(price)) OVER (), 1) AS pct_of_biggest_dollar_print,
    arrayStringConcat(arrayMap(c -> toString(c), conditions), ', ') AS condition_id
FROM global_markets.stocks_trades
WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
  AND size >= 10000000
  AND NOT hasAny(conditions, [15, 16, 38])
ORDER BY size DESC
LIMIT 10

พิจารณาแถวบนสุดเพื่อเป็นตัวอย่าง รายการซื้อขายที่ใหญ่ที่สุดของวัน คือหุ้น CAG (Conagra) จำนวน 61.69 ล้านหุ้น ที่ราคา $13.98 คิดเป็นมูลค่าประมาณ $862.5 ล้าน โดยระบุเวลา 16:04:53 ET ภายใต้รหัส 8, 41 (8 Closing Prints, 41 Trade Thru Exempt) ซึ่งเป็นผลจากการประมูลปิดตลาดของ NYSE ที่ประกาศหลังเวลา 16:00 น. รายการนี้จะถูกนับรวมในปริมาณการซื้อขายเพียงครั้งเดียว จากนั้นหุ้นจำนวน 61.69 ล้านหุ้นเดิมจะปรากฏขึ้นอีกครั้งภายใต้รหัส 15 (Market Center Official Close) ซึ่งเป็นการรายงานซ้ำที่พจนานุกรมข้อมูลไม่นำมานับรวม และเป็นส่วนหนึ่งของยอด 4.13 พันล้านที่ถูกตัดออกไปข้างต้น แถวรหัส-15 ดังกล่าวถูกออกแบบมาให้ตัดออกจากตารางด้านบน ดังนั้นข้อมูลการรับรายการนี้จึงมีแผงข้อมูลแยกต่างหากหนึ่งแถว:

คำสั่งดึงข้อมูลการเปรียบเทียบ: การปิดตลาดของ CAG และการรายงานซ้ำด้วย code-15
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    round(toFloat64(maxIf(size, has(conditions, 8))) / 1e6, 2) AS auction_print_shares_m,
    round(toFloat64(maxIf(size, has(conditions, 15))) / 1e6, 2) AS official_close_reprint_shares_m,
    round(toFloat64(maxIf(size, has(conditions, 8))) - toFloat64(maxIf(size, has(conditions, 15))), 2) AS share_difference,
    formatDateTime(toTimeZone(argMaxIf(sip_timestamp, size, has(conditions, 15)), 'America/New_York'), '%H:%i:%S') AS reprint_et,
    argMaxIf(exchange, size, has(conditions, 15)) AS reprint_exchange_id,
    countIf(has(conditions, 15)) AS cag_code15_prints
FROM global_markets.stocks_trades
WHERE ticker = 'CAG'
  AND sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'

รายการที่ใหญ่ที่สุดในบรรดาข้อความปิดตลาดอย่างเป็นทางการ 6 รายการของ CAG มีจำนวน 61.69 ล้านหุ้น ซึ่งเท่ากับจำนวน 61.69 ล้านหุ้นจากการประมูลพอดี (ส่วนต่างคือ 0) ระบุเวลา 16:04:53 ET จาก exchange id 10 ซึ่งคือ NYSE ในสารบบหุ้น: เป็นการรายงานผลการประมูลซ้ำโดยตลาดหลักทรัพย์ที่จดทะเบียน

ส่วนอีก CAG แถวที่เหลือเป็นคนละประเภทกัน รายการซื้อขายจำนวน 15.17 ล้านหุ้นภายใต้รหัส 12, 2 (12 Form T / extended hours, 2 Average Price Trade) และอีกสองรายการภายใต้รหัส 12 คือรายการหลังการประมูลที่รายงานผ่าน FINRA ซึ่งมีเงื่อนไข updates_volume = true ระบบ SIP นับรายการเหล่านี้เป็นรายการซื้อขายจริง และข้อมูลที่จับคู่ของเราก็นับเช่นกัน

ในส่วนอื่นๆ รหัส 9 หมายถึงการจับคู่ปิดตลาดของ Nasdaq โดยรายการของ NVDA มูลค่า $3389.5 ล้าน คือรายการที่มีมูลค่าดอลลาร์สูงที่สุดในตาราง และรหัสในรายการ SNAP (53, 41) รวมถึงรหัส 53 ซึ่งเป็น Qualified Contingent Trade

เวลา: สิบนาทีที่ครองตลาดทั้งวัน

เมื่อพิจารณาความละเอียดระดับนาที ปริมาณการซื้อขายเกือบทั้งหมดจะกระจุกตัวอยู่บริเวณช่วงเปิดและช่วงปิดตลาด:

คำสั่งดึงข้อมูล10 นาทีที่มีการซื้อขายหนาแน่นที่สุด (จำนวนรายการหลักล้าน)
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    formatDateTime(toTimeZone(toStartOfMinute(sip_timestamp), 'America/New_York'), '%H:%i') AS et_minute,
    round(count() / 1e6, 2) AS trades_m
FROM global_markets.stocks_trades
WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
GROUP BY et_minute
ORDER BY trades_m DESC
LIMIT 10

ในช่วง 15:59 ET มีการซื้อขายเกิดขึ้น 3.98 ล้านรายการ — หรือมากกว่าหกหมื่นรายการต่อวินาที ส่วนข้อมูลว่าหุ้นตัวอื่นในกลุ่ม top ten จะกระจุกตัวอยู่เฉพาะช่วงเปิดและช่วงปิดตลาดหรือไม่นั้น สามารถพิจารณาได้จากบรรทัดเดียว:

คำสั่งดึงข้อมูลจำนวนนาทีที่หนาแน่นที่สุดที่อยู่นอกช่วงเปิดและปิดตลาด
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    countIf(NOT (et_minute BETWEEN '09:30' AND '09:39' OR et_minute BETWEEN '15:50' AND '16:00')) AS entries_outside_open_close,
    countIf(et_minute BETWEEN '09:30' AND '09:39' OR et_minute BETWEEN '15:50' AND '16:00') AS entries_in_open_close,
    maxIf(et_minute, NOT (et_minute BETWEEN '09:30' AND '09:39' OR et_minute BETWEEN '15:50' AND '16:00')) AS outside_entry_minute,
    maxIf(trades_m, NOT (et_minute BETWEEN '09:30' AND '09:39' OR et_minute BETWEEN '15:50' AND '16:00')) AS outside_entry_trades_m
FROM (
    SELECT
        formatDateTime(toTimeZone(toStartOfMinute(sip_timestamp), 'America/New_York'), '%H:%i') AS et_minute,
        round(count() / 1e6, 2) AS trades_m
    FROM global_markets.stocks_trades
    WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
    GROUP BY et_minute
    ORDER BY trades_m DESC
    LIMIT 10
)

9 ของหุ้นในกลุ่มสิบอันดับแรก มีปริมาณการซื้อขายในช่วงสิบนาทีแรกของการเปิดตลาดหรือสิบนาทีสุดท้ายก่อนปิดตลาด มีเพียงข้อยกเว้นเดียวคือ 10:15 ET ซึ่งมีการซื้อขายพุ่งสูงขึ้นถึง 0.88 ล้านรายการ และถือเป็นความผิดปกติในช่วงสายของวัน ข้อมูลนี้ไม่สามารถระบุสาเหตุที่แน่ชัดได้ และเราจะไม่คาดเดาเหตุผลดังกล่าว โดยช่วงเวลาเดียวกันนี้จะปรากฏในส่วนถัดไปด้านล่าง

กระแสข้อมูลราคา: การอัปเดตครึ่งพันล้านครั้ง

National Best Bid and Offer หรือราคาเสนอซื้อและเสนอขายที่ดีที่สุดจากทุกตลาดหลักทรัพย์ ซึ่งจะมีการเผยแพร่ใหม่ทุกครั้งที่มีการเปลี่ยนแปลงราคา มีการอัปเดตทั้งหมด 505.38 ล้านครั้ง เมื่อวันที่ 29 มิถุนายน โดยมีจำนวนการซื้อขายอยู่ที่ 156.1 ล้านรายการ

คำสั่งดึงข้อมูลข้อมูล NBBO ทั้งหมดในแถวเดียว — ระบุชื่อ QQQ และ NVDA
SQL เบื้องหลังตัวเลขแต่ละตัว
WITH
    (
        SELECT (round(countIf(ticker = 'QQQ') / 1e6, 2), round(countIf(ticker = 'NVDA') / 1e6, 2),
                round(countIf(ticker = 'AAPL') / 1e6, 2), round(countIf(ticker = 'TSLA') / 1e6, 2))
        FROM global_markets.cache_stocks_quotes
        WHERE ticker IN ('QQQ', 'NVDA', 'AAPL', 'TSLA')
          AND sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
    ) AS pinned
SELECT
    round(count() / 1e6, 2) AS updates_m,
    pinned.1 AS qqq_updates_m,
    pinned.2 AS nvda_updates_m,
    round(pinned.1 - pinned.2, 2) AS qqq_minus_nvda_updates_m,
    round(pinned.2 - greatest(pinned.3, pinned.4), 2) AS nvda_minus_next_single_m
FROM global_markets.cache_stocks_quotes
WHERE sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'

ผลิตภัณฑ์ดัชนีเป็นกลุ่มที่มีการเสนอราคามากที่สุด โดย QQQ เพียงอย่างเดียวมีการอัปเดตถึง 5.23 ล้านครั้ง เมื่อเทียบกับ 1.8 ล้านครั้งของ NVDA ซึ่งเป็นหุ้นรายตัวที่มีการเสนอราคามากที่สุดจากการตรวจสอบของเรา โดยมีจำนวนการอัปเดตมากกว่าหุ้นรายตัวลำดับถัดไปถึง 0.67 ล้านครั้ง เนื่องจากการสำรวจข้อมูลทุกหลักทรัพย์อย่างละเอียดไม่สามารถดำเนินการได้ภายในข้อจำกัดเวลา 30 วินาที ดังนั้นตารางอันดับด้านล่างนี้จึงเป็นเพียงชุดข้อมูลที่ผ่านการตรวจสอบแล้ว ไม่ใช่การจัดอันดับทั้งหมด

คำสั่งดึงข้อมูลการอัปเดตราคา NBBO สำหรับ 8 tickers ที่มีการเสนอราคาหนาแน่น (หลักล้าน)
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT ticker, round(count() / 1e6, 2) AS updates_m
FROM global_markets.cache_stocks_quotes
WHERE ticker IN ('QQQ', 'SPY', 'TQQQ', 'IWM', 'SQQQ', 'NVDA', 'AAPL', 'TSLA')
  AND sip_timestamp >= '2026-06-29 00:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
GROUP BY ticker
ORDER BY updates_m DESC

QQQ เป็นผู้นำในชุดข้อมูลที่ตรวจสอบ โดยมีการอัปเดต 5.23 ล้านครั้ง ทั้งนี้ อันดับต้นๆ ของตารางคือกลุ่มกองทุนดัชนีและกองทุนที่มีอัตราทด (leveraged) ไม่ใช่หุ้นรายตัวที่เป็นที่รู้จักทั่วไป

เจาะลึก SPY: ข้อมูลระดับนาโนวินาที และความแตกต่างของ spread สองรูปแบบ

คำสั่งดึงข้อมูลเจาะลึก SPY: ช่องว่างระดับ nanosecond, spread สองด้าน และคุณภาพราคา
SQL เบื้องหลังตัวเลขแต่ละตัว
WITH
    (
        SELECT (formatDateTime(toTimeZone(toStartOfSecond(sip_timestamp), 'America/New_York'), '%H:%i:%S'), count())
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'SPY' AND sip_timestamp >= '2026-06-29 13:30:00' AND sip_timestamp < '2026-06-29 20:00:00'
        GROUP BY toStartOfSecond(sip_timestamp)
        ORDER BY count() DESC, toStartOfSecond(sip_timestamp) ASC
        LIMIT 1
    ) AS busiest_sec,
    ordered AS (
        SELECT
            toFloat64(ask_price - bid_price) AS spread,
            bid_price,
            ask_price,
            toFloat64(sip_timestamp - lagInFrame(sip_timestamp) OVER (ORDER BY sip_timestamp, sequence_number)) AS gap_s,
            greatest(toFloat64(least(leadInFrame(sip_timestamp, 1, toDateTime64('2026-06-29 20:00:00', 9)) OVER (ORDER BY sip_timestamp, sequence_number ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING), toDateTime64('2026-06-29 20:00:00', 9)) - sip_timestamp), 0) AS dwell_s,
            rowNumberInAllBlocks() AS rn
        FROM global_markets.cache_stocks_quotes
        WHERE ticker = 'SPY' AND sip_timestamp >= '2026-06-29 13:30:00' AND sip_timestamp < '2026-06-29 20:00:00'
    )
SELECT
    round(count() / 1e6, 2) AS spy_updates_m,
    busiest_sec.1 AS busiest_second_et,
    toUInt8(startsWith(busiest_sec.1, '10:15')) AS busiest_second_in_1015,
    busiest_sec.2 AS busiest_second_updates,
    round(minIf(gap_s, rn > 0 AND gap_s > 0) * 1e9) AS min_gap_between_updates_ns,
    round(quantileExactIf(0.5)(gap_s, rn > 0) * 1e6, 1) AS median_gap_us,
    countIf(rn > 0 AND gap_s = 0) AS identical_timestamp_pairs,
    round(sumIf(spread * dwell_s, bid_price > 0 AND ask_price >= bid_price) / sumIf(dwell_s, bid_price > 0 AND ask_price >= bid_price) * 100, 2) AS time_weighted_avg_spread_cents,
    round(avgIf(spread, bid_price > 0 AND ask_price >= bid_price) * 100, 2) AS per_update_avg_spread_cents,
    round((avgIf(spread, bid_price > 0 AND ask_price >= bid_price) - sumIf(spread * dwell_s, bid_price > 0 AND ask_price >= bid_price) / sumIf(dwell_s, bid_price > 0 AND ask_price >= bid_price)) * 100, 2) AS per_update_premium_cents,
    countIf(bid_price > 0 AND ask_price > 0 AND ask_price < bid_price) AS crossed_updates,
    countIf(bid_price <= 0 OR ask_price <= 0) AS zero_bid_or_ask_updates
FROM ordered

ราคาเสนอซื้อขายของ SPY มีการเปลี่ยนแปลงถึง 3.98 ล้านครั้งเฉพาะในช่วงเวลาทำการปกติเท่านั้น ช่วงเวลาที่มีการซื้อขายหนาแน่นที่สุดคือ 10:15:43 ET ภายในช่วงเวลา 10:15 นาทีที่ระบุไว้ข้างต้น โดยมีการอัปเดตข้อมูลถึง 1461 ครั้ง ระยะห่างที่สั้นที่สุดระหว่างการอัปเดตที่ต่อเนื่องกันคือ 1375 nanoseconds โดยมีค่ามัธยฐานอยู่ที่ 245.6 microseconds และมีข้อมูล 75138 คู่ที่ต่อเนื่องกันที่มีค่า timestamp ระดับ nanosecond เหมือนกัน ซึ่งแสดงให้เห็นว่าแม้แต่ความละเอียดระดับ nanosecond ก็ยังไม่สามารถจัดลำดับข้อมูลในตลาดสมัยใหม่ได้อย่างสมบูรณ์

ข้อมูลระดับ nanoseconds เหล่านี้มีความสำคัญในการวัด bid-ask spread หากนับการอัปเดตทุกครั้งด้วยน้ำหนักที่เท่ากัน ค่าเฉลี่ย spread ของ SPY จะอยู่ที่ 2.72 cents แต่หากถ่วงน้ำหนักแต่ละราคาตามระยะเวลาที่ราคานั้นคงอยู่ ค่าเฉลี่ยจะอยู่ที่ 2.34 cents ตัวเลขต่อการอัปเดตหนึ่งครั้งมีค่ากว้างกว่าในเชิงกลไกอยู่ 0.38 cents เนื่องจากข้อมูลการอัปเดตมักจะกระจุกตัวอยู่ในช่วงเวลาที่ spread กว้างและกำลังเคลื่อนไหว ในขณะที่คำสั่งซื้อขายที่รออยู่ในตลาดจะถูกนับตามเวลาจริง เมื่อคุณเห็นสถิติ spread ควรพิจารณาว่าใช้การวัดด้วยนาฬิกาประเภทใด ข้อมูลสถิติเหล่านี้มีการเปิดเผยอย่างชัดเจน: มีการอัปเดตในช่วงเวลาทำการปกติจำนวน 1544 ครั้งที่เกิดสภาวะราคาตัดกัน (ราคา ask ต่ำกว่า bid ซึ่งเกิดจากการสลับกันของราคาจากแหล่งซื้อขายที่ต่างกัน) และมี 0 ครั้งที่มีราคา bid หรือ ask เป็นศูนย์ โดยสถิติ spread จะไม่นับรวมทั้งสองกรณีนี้และจะนับเฉพาะข้อมูลที่ถูกคัดออกเท่านั้น

ส่วนต่างราคา (Spread) มีช่วงเวลาที่แน่นอน

การจัดกลุ่มค่า spread ของ SPY ตามช่วงเวลาทุกครึ่งชั่วโมง ช่วยให้เห็นโครงสร้างตลาดในรูปแบบเส้นโค้งที่นักเทรดสามารถนำไปใช้ประโยชน์ได้:

คำสั่งดึงข้อมูลค่ามัธยฐานของ spread ของ SPY รายครึ่งชั่วโมง ตั้งแต่ 4:00 am ถึง 8:00 pm ET
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    formatDateTime(toTimeZone(toStartOfInterval(sip_timestamp, INTERVAL 30 MINUTE), 'America/New_York'), '%H:%i') AS et_time,
    round(quantileExactIf(0.5)(toFloat64(ask_price - bid_price), bid_price > 0 AND ask_price >= bid_price) * 100, 1) AS median_spread_cents,
    count() AS quote_updates
FROM global_markets.cache_stocks_quotes
WHERE ticker = 'SPY' AND sip_timestamp >= '2026-06-29 08:00:00' AND sip_timestamp < '2026-06-30 00:00:00'
GROUP BY et_time
ORDER BY et_time

ค่ามัธยฐานอยู่ที่ 8 cents ในช่วง premarket ของ 04:00 ET, 3 cents ในช่วงเปิดตลาด 09:30 และอยู่ที่ 2 cents เมื่อถึงเวลา 14:00 — การซื้อขายหุ้นตัวเดียวกันในช่วงเปิด premarket มีต้นทุนสูงกว่าค่า spread ของคำสั่งซื้อขายในช่วงบ่ายต้นประมาณสี่เท่า ส่วนต่างราคาไม่ใช่ค่าคงที่ แต่เป็นตารางเวลาที่กำหนดไว้ชัดเจน

หลักฐานเชิงนิติวิทยาศาสตร์: ไฟล์ข้อมูลที่สิ้นสุดลงที่ตัวอักษร S

ไฟล์ข้อมูลรายวันจากผู้ให้บริการทุกรายต้องผ่านการตรวจสอบความครบถ้วนก่อนที่เราจะนำตัวเลขไปเผยแพร่ โดยการนับจำนวน tickers ตรวจสอบช่วงตัวอักษร และเปรียบเทียบกับข้อมูลในวันใกล้เคียง ไฟล์ข้อมูลปริมาณการขายชอร์ตนอกตลาด (off-exchange short-volume) ของ FINRA ประจำวันที่ 29 มิถุนายน คือตัวอย่างที่แสดงให้เห็นถึงเหตุผลดังกล่าว:

คำสั่งดึงข้อมูลการตรวจสอบความครบถ้วน: ขอบเขตข้อมูล short-volume ของ FINRA, 26-30 มิถุนายน
SQL เบื้องหลังตัวเลขแต่ละตัว
SELECT
    date,
    uniqExact(ticker) AS tickers,
    min(ticker) AS first_ticker,
    max(ticker) AS last_ticker,
    countIf(ticker = 'TSLA') AS tsla_rows,
    round(100 * sum(short_volume) / sum(total_volume), 2) AS short_pct_of_reported
FROM global_markets.stocks_short_volume
WHERE date BETWEEN '2026-06-26' AND '2026-06-30'
GROUP BY date
ORDER BY date

ไฟล์ของวันที่ 26 มิถุนายน ครอบคลุม 15052 tickers ตั้งแต่ A ถึง ZYME ส่วนไฟล์ของวันที่ 29 มิถุนายน ครอบคลุมเพียง 5489 ตั้งแต่ A ถึง SSUS โดยข้อมูลหยุดลงกลางคันในช่วงชื่อที่ขึ้นต้นด้วยตัว S ทุก ticker ที่อยู่ถัดไปตามลำดับตัวอักษร รวมถึง TSLA ซึ่งมีจำนวน 0 แถวในวันที่ 29 มิถุนายน เทียบกับ 1 ในวันที่ 26 มิถุนายน ได้ขาดหายไป และเมื่อถึงวันที่ 30 มิถุนายน ไฟล์ข้อมูลกลับมามีจำนวน 15362 tickers การตรวจสอบความครบถ้วนนั้นไม่ซับซ้อนไปกว่าการตรวจสอบความถูกต้องของข้อมูลรายวันเช่นนี้ ซึ่งเป็นความแตกต่างระหว่างการเผยแพร่สถิติกับการเผยแพร่ข้อมูลที่ผิดพลาด

เมื่อพิจารณาจากการเปิดเผยข้อมูลดังกล่าว — และต้องพิจารณาควบคู่กันเท่านั้น — นี่คืออัตราส่วน: ปริมาณการขายชอร์ต (marked-short volume) คิดเป็น 45.85% ของปริมาณการซื้อขายนอกตลาดที่รายงานในวันที่ 29 มิถุนายน เทียบกับ 46.89% ในวันที่ 26 มิถุนายน และ 48.82% ในวันที่ 30 มิถุนายน ตัวเลขของวันที่ 29 มิถุนายน ครอบคลุมเพียงข้อมูลช่วงตัวอักษร A ถึง S ดังนั้นจึงควรพิจารณาว่าเป็นข้อมูลเพียงบางส่วน ปริมาณการขายชอร์ตเกือบครึ่งหนึ่งเป็นระดับปกติที่พบได้ในทุกช่วงเวลาที่ใกล้เคียงกัน และปริมาณการขายชอร์ตรายวันไม่ใช่ short interest

หมายเหตุข้อมูล

หมายเหตุข้อมูลฉบับเต็ม
  • ช่วงตลาดเงียบเหงาเวลา 17:00 ถึง 18:00 น. กระแสราคาของ SPY จะเกือบหยุดนิ่งในช่วงครึ่งชั่วโมงของ 17:00 ET (233) และจะกลับมาเคลื่อนไหวอีกครั้งในช่วง 18:00 (40608) ซึ่งเป็นรูปแบบการส่งข้อมูลและช่วงเวลาที่เกิดขึ้นกับทุก ticker ไม่ใช่ข้อมูลที่ขาดหายไป
  • มีการใช้หุ้นเศษส่วน (Fractional shares) ในทุกที่ 4.73% ของรายการที่บันทึกมีขนาดไม่เป็นจำนวนเต็ม ดังนั้นต้องรวมปริมาณการซื้อขาย (volume) โดยใช้ค่าทศนิยม
  • พจนานุกรมรหัสมีการใช้ id ซ้ำกันในแต่ละประเภท Id 15 เป็นทั้งเงื่อนไขการขาย (Market Center Official Close) และเงื่อนไขราคา (Closed) ทุกการสืบค้นพจนานุกรมในที่นี้จะกรองด้วย asset_class = 'stocks' และ type = 'sale_condition' การสืบค้นด้วย id เพียงอย่างเดียวจะทำให้การระบุรหัสผิดพลาด
  • การยกเว้นคุณภาพราคาถูกนับรวมไว้ในเนื้อหา จำนวนรายการที่ราคาตัดกัน (crossed) และรายการที่ไม่มีราคาเสนอซื้อหรือเสนอขาย (zero-bid-or-ask) จะแสดงอยู่ในแผง SPY microscope ด้านบน โดยไม่มีสถิติ spread ใดที่ปกปิดการลดลงของข้อมูลดังกล่าว
  • การสแกนข้อมูลทั้งแถบ (Whole-tape scans) เป็นแผงข้อมูลแบบ batch-only ข้อมูลบล็อกทั่วทั้งตลาดที่แสดงที่นี่วัดค่าโดยใช้เวลาประมาณ 30 วินาทีต่อบล็อกบน cold cache (หากบล็อกหมดเวลาจะมีการลองใหม่หนึ่งครั้ง; การประมวลผลแบบ warmed pass จะล้างข้อจำกัดดังกล่าว) ข้อมูลเหล่านี้จะทำงานเพียงครั้งเดียว ณ เวลาที่ผู้เขียนจัดทำผ่านเส้นทางอ่านอย่างเดียว (read-only path) และผลลัพธ์ที่จัดเก็บไว้คือข้อมูลที่บันทึกไว้

ระเบียบวิธี

  • ข้อมูลเวลาถูกจัดเก็บในรูปแบบ UTC และกรองด้วยขอบเขต UTC ดั้งเดิม (เวลาทำการปกติในวันที่ 29 มิถุนายน คือ 13:30 ถึง 20:00 UTC) โดยฟังก์ชัน toTimeZone จะปรากฏเฉพาะในรายการ SELECT เท่านั้น
  • ประเภทของเซสชันจะถูกตรวจสอบเทียบกับปฏิทินวันหยุดและช่วงเวลาทำการปกติ 390-bar ที่ตรวจพบ โดยไม่มีการสมมติขึ้นเอง
  • "SIP-counted (matched) volume" คือปริมาณการซื้อขายที่กฎเงื่อนไขของ SIP นับรวมเข้ากับยอดรวมรวม (consolidated total) โดยชุดข้อมูลที่ถูกยกเว้นจะถูกดึงมาจาก dictionary และมีการจำกัดด้วย checksum โดยไม่มีการกำหนดค่าตายตัว
  • สถิติ spread ต่อการอัปเดต (per-update spread statistics) จะให้ค่าน้ำหนักกับการอัปเดต NBBO แต่ละครั้งเท่ากัน ส่วนสถิติแบบถ่วงน้ำหนักด้วยเวลา (time-weighted statistics) จะให้ค่าน้ำหนักตามระยะเวลาที่ราคาเสนอขายนั้นคงอยู่ โดยทั้งสองแบบจะมีการระบุข้อมูลที่ถูกยกเว้นไว้
  • คอลัมน์ราคา/ขนาดที่เป็นทศนิยมจะถูกรวมค่าด้วยวิธี native และแปลงเป็น 64-bit floats ก่อนการคำนวณอัตราส่วนหรือราคาคูณขนาด
  • ตาราง Tick จะเก็บประวัติฉบับเต็มโดยไม่มีการหมดอายุแบบ rolling; ผลลัพธ์ที่จัดเก็บและมีการระบุเวอร์ชันเหล่านี้คือบันทึกที่ถาวร ข้อมูลคลังข้อมูล ณ วันที่ 3 กรกฎาคม 2026

ทุกแผงข้อมูลด้านบนคือออบเจกต์ที่จัดเก็บไว้หนึ่งชุด ซึ่งประกอบด้วย กราฟ ตาราง และ SQL รวมกัน คุณสามารถเปิดรายการใดก็ได้ คัดลอกคำสั่ง query และนำไปใช้งานต่อบนเทอร์มินัล Strasmore ได้ทันที