Strasmore Research

Часові мітки market data: SIP чи біржовий годинник

Дані market data проходять через чотири годинники. Дізнайтеся, як кожен змінює порядок угод у стрічці та який використовувати для різних завдань.

Позначки часу в market data — це часові мітки, які отримує один print під час проходження від matching engine, де його було виконано, до екрана, на якому він відображається. У публічному записі угоди з акціями США мають три такі мітки, і кожна відповідає на окреме запитання. Якщо відсортувати prints за однією міткою, а потім за іншою, ви отримаєте дві справді різні стрічки угод.

Чотири часові мітки, які проходить один print

Print отримує часову мітку кілька разів на шляху до вас. У такому порядку:

  1. Час matching engine. Момент, коли matching engine майданчика зводить два ордери. Ніхто за межами майданчика не бачить це значення безпосередньо. Це фактичний час виконання угоди, а кожна наступна мітка є його наближенням.
  2. Час учасника, також час майданчика або біржі. Мітка, яку майданчик записує під час публікації print у власному feed. Вона передається в полі participant_timestamp. Серед усіх значень, які можна прочитати, це найближче до часу matching engine.
  3. Час SIP. Мітка, яку securities information processor записує, коли print потрапляє до консолідованої стрічки — єдиного офіційного feed, що об’єднує всі майданчики торгівлі акціями США. Це поле sip_timestamp, і офіційна послідовність стрічки визначається саме ним. Відмінності між цим feed і власним feed майданчика описані в матеріалі SIP і прямі feeds бірж.
  4. Час захоплення. Мітка, яку записує мережева карта вашої системи, коли пакет надходить. У записах vendor вона не відображається, оскільки описує ваш мережевий маршрут, а не ринок. Робота з захопленням і відтворенням пакетів повністю спирається на цю мітку.

Prints, виконані поза біржею, мають ще одну мітку — trf_timestamp. Вона показує, коли trade reporting facility прийняла звіт.

Чому часові мітки market data відрізняються між майданчиками

Різниця між міткою майданчика і консолідованою міткою — це час, який print провів у дорозі та в черзі processor. Це не одне фіксоване значення. Кожен майданчик має різну відстань до processor, інше обладнання і власну чергу. Панель нижче вимірює цю різницю для кожного майданчика, на якому були prints AAPL протягом фіксованого 30-хвилинного періоду 10 червня 2026 року.

ЗапитЗатримка отримання SIP за майданчиками, AAPL, 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
Run this yourself

Серед цих майданчиків найбільша медіанна різниця між міткою майданчика і консолідованою міткою становила 346.2 мікросекунд — на майданчику NYSE Arca, Inc.. На найшвидшому майданчику в тому самому інтервалі вона становила 13.7 мікросекунд. На колонку p99 варто звернути особливу увагу: для того самого найповільнішого майданчика вона сягнула 420.8 мікросекунд. Це хвіст розподілу, якого не видно за медіаною.

Яку часову мітку використовувати

Майже всі випадки охоплюють чотири правила.

  • Час учасника — для аналізу мікроструктури та event studies. Усе, що вимірює події на майданчику та їхню послідовність, має базуватися на годиннику майданчика. Відновлення order book, описане в матеріалі дані order book MBO і MBP, непридатне на основі будь-якої іншої мітки.
  • Час SIP — для всього, що має узгоджуватися з офіційною стрічкою. Це стосується регуляторної звітності, перевірки best execution, офіційних цін відкриття і закриття, а також будь-яких показників, які контрагент звірятиме з консолідованим записом.
  • Час захоплення — лише для вимірювання власного мережевого маршруту. Він показує, скільки часу market data знадобилося, щоб дістатися вашої машини. Він нічого не говорить про час виконання угоди, і дві машини ніколи не покажуть однакове значення.
  • Ніколи не змішуйте часові мітки в одному наборі даних. Join, який зіставляє quotes за однією міткою з trades за іншою, дає правдоподібні числа, але помиляється саме в найважливіші моменти.

Ті самі prints, відсортовані двома способами

Саме під час сортування стає очевидною різниця між часовими мітками. Панель нижче бере найактивніші десять мілісекунд цього 30-хвилинного періоду, залишає перші дванадцять біржових prints у порядку майданчика, а потім повторно ранжує ті самі дванадцять за порядком консолідованої стрічки. Таблиця відсортована за величиною зміни позиції кожного print між двома рейтингами.

ЗапитТі самі 12 угод, ранжовані за годинником майданчика та годинником стрічки
Точний 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
Run this yourself

Найбільша різниця між двома позиціями print становить 6. Цей print залишив свій майданчик о 10:44:17.160712 і потрапив до стрічки через 305.7 мікросекунд, о 10:44:17.161018. Його позиція змінилася з 6 за годинником майданчика на 12 у стрічці. Жоден із цих порядків не є неправильним. Вони відповідають на різні запитання. Аналіз послідовності угод за часом стрічки відтворює цей сплеск у порядку, якого не створював жоден майданчик. Перевірка best execution за часом майданчика, своєю чергою, не збігатиметься з офіційним записом.

Prints поза біржею значно відстають від події

Угода, виконана поза біржею — у wholesaler або dark pool, — звітується до trade reporting facility, а не зводиться в публічному order book. Звіт містить час виконання, але надходить до стрічки пізніше. Цей інтервал є затримкою звітності, а не часом передачі, і він на порядки більший.

ЗапитПозабіржові угоди AAPL за затримкою звітування, 10 червня 2026 року
Точний 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
Run this yourself

Із позабіржових prints AAPL у цьому періоді 24.8% потрапили до категорії under 1 ms. Хвіст розподілу сягає категорії 1 s to 10 s, до якої належать 74 prints. Print, який надійшов із запізненням у десять секунд, усе одно містить час майданчика, коли угоду було фактично виконано, але в потоці стрічки він розташований на десять секунд пізніше. Якщо сортувати за часом стрічки, він опиниться не в тій хвилині. Prints, звітовані поза звичайною послідовністю, мають коди умов продажу, які це позначають. Саме це, зокрема, фіксують коди умов торгів.

Чому двоє людей будують різні bars з однакових trades

Майже кожен запит зі скаргою «дані неправильні» зводиться до цього. Bar — це кошик prints, а те, до якого кошика потрапить print, залежить від мітки, за якою ви його групуєте. Панель нижче рахує prints, які змінюють bar під час переходу з годинника майданчика на годинник стрічки, для чотирьох поширених довжин bars.

ЗапитУгоди, що змінюють бар під час перемикання годинників, за тривалістю бару
Точний 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
Run this yourself

На сітці з інтервалом 1 second у 8.966% prints цього періоду за двома годинниками потрапляють до різних bars — загалом 8944 prints. Якщо збільшити довжину bar до 5 minutes, показник знижується до 0.024%. Механізм простий: print змінює кошик, коли різниця між його двома мітками перетинає межу, а коротші bars мають більше таких меж. Два vendor можуть бути одночасно правими й усе одно публікувати різний обсяг за ту саму хвилину. Сам принцип побудови розглянуто в матеріалі як будуються bars OHLCV.

Поле з наносекундною роздільною здатністю не означає наносекундної точності

Обидві мітки надходять як цілі числа з наносекундною роздільною здатністю. Роздільна здатність показує, що може виразити поле. Точність показує, наскільки значення близьке до фактичного часу. Ці характеристики визначаються зовсім різними чинниками.

Синхронізація годинників у галузі регулюється нормативним допуском, а не фізикою. Правило FINRA щодо синхронізації вимагає, щоб робочі годинники фірм-учасників відхилялися від еталона NIST не більш як на 50 мілісекунд. Біржі та processors працюють із набагато вищою точністю. Вони використовують Precision Time Protocol (PTP, стандартизований як IEEE 1588), який передає еталонний час тією самою мережею, що й дані, та забезпечує синхронізацію машин на рівні менше однієї мікросекунди.

З цього випливають два висновки. У межах позначок однієї організації порядок подій із точністю до мікросекунди має практичне значення. Між різними організаціями різниця в кілька сотень наносекунд між двома мітками перебуває в межах похибки. Вважати її реальною послідовністю означає приймати шум за сигнал.

FAQ

У чому різниця між часовою міткою SIP і міткою учасника?

Мітку учасника майданчик записує, коли публікує угоду у власному feed. Мітку SIP processor консолідованої стрічки записує, коли ця угода потрапляє до офіційного об’єднаного feed. Різниця між ними — це час передачі та очікування в черзі. Для біржових prints він вимірюється мікросекундами, а для prints, звітованих через trade reporting facility, часто становить мілісекунди або більше.

Яку часову мітку market data використовувати для backtesting?

Використовуйте мітку учасника для моделей, які відтворюють, що учасник міг побачити або зробити на певному майданчику. Використовуйте мітку SIP для всього, що має узгоджуватися з офіційним консолідованим записом. Яку б мітку ви не обрали, застосовуйте її до всіх таблиць дослідження, включно з quotes.

Чому мої one-minute bars не збігаються з bars постачальника даних?

Зазвичай причина — невідповідність часових міток. Print, мітка майданчика якого припадає безпосередньо перед межею хвилини, може мати мітку стрічки вже після цієї межі. За двома правилами одна й та сама угода потрапить до різних bars. Позабіржові prints із затримкою звітності посилюють цей ефект.

Чи є наносекундні часові мітки точними до наносекунди?

Ні. Поле має наносекундну роздільну здатність, а точність залежить від синхронізації годинника машини, яка записує мітку. Системи бірж і processors, що використовують PTP, забезпечують синхронізацію на рівні менше однієї мікросекунди. Робочі годинники брокерів мають нормативний допуск у 50 мілісекунд. Порівняння з точністю, вищою за допуск менш точного годинника, не має змісту.


Кожна панель вище містить SQL, за допомогою якого її побудовано. Ви можете точно побачити, з якої часової мітки взято кожне число. Щоб повторно відсортувати власний період prints за іншою міткою та побачити, як змінюється стрічка, поставте запит простою англійською мовою в терміналі Strasmore.