Strasmore Research
חקירות Matt Connorמאת Matt Connor

הבדלים בין חותמות זמן של SIP מול שעוני בורסה

חותמות זמן של נתוני שוק מגיעות מארבעה שעונים שונים. גלו כיצד מיון עסקאות לפי כל שעון משנה את הנתונים ואיזה שעון מתאים לכל מטרה בניתוח מסחר במניות בארה"ב.

חותמות זמן של נתוני שוק הן השעונים המוטבעים על "פרינט" (trade print) של עסקה בודדת, מרגע יציאתה ממנוע ההתאמה (matching engine) שביצע אותה ועד להופעתה על המסך. ברשומות הציבוריות של עסקאות במניות בארה"ב מופיעות שלוש חותמות כאלו, וכל אחת מהן עונה על שאלה שונה. אם תמיינו את אותם פרינטים של יום מסחר לפי שעון אחד ולאחר מכן לפי שעון אחר, תקבלו שני "טייפים" (tapes) שונים בתכלית.

ארבעת השעונים שפרינט בודד עובר דרכם

פרינט מקבל חותמת שוב ושוב בדרכו אליכם. לפי הסדר:

  1. זמן מנוע ההתאמה (Matching engine time). הרגע שבו מנוע ההתאמה של זירת המסחר הצליב בין שתי פקודות. איש מחוץ לזירה אינו קורא ערך זה ישירות. זוהי "אמת המידה" למועד ביצוע העסקה, וכל שעון שמגיע לאחר מכן הוא קירוב בלבד.
  2. זמן משתתף (Participant time), המכונה גם זמן זירה או זמן בורסה. החותמת שהזירה כותבת בעת פרסום הפרינט בפיד שלה, המועברת בשדה participant_timestamp. מכל מה שניתן לקרוא בפועל, זהו הזמן הקרוב ביותר למנוע ההתאמה.
  3. זמן SIP. החותמת שה-Securities Information Processor כותב כאשר הפרינט מגיע ל-consolidated tape, הפיד הרשמי היחיד המאחד את כל זירות המסחר במניות בארה"ב. זהו שדה sip_timestamp, וסדר הטייפ הרשמי נקבע לפיו. ההבדל בין פיד זה לבין הפיד הישיר של זירה מוסבר ב-SIP לעומת פידים ישירים של בורסות.
  4. זמן קליטה (Capture time). החותמת שכרטיס הרשת שלכם כותב ברגע נחיתת החבילה. היא לעולם אינה מופיעה ברשומות של ספק נתונים, שכן היא מתארת את הנתיב שלכם ולא את השוק. עבודות לכידת חבילות ושידור חוזר מתבססות במלואן על שעון זה.

פרינטים מחוץ לבורסה (Off exchange) נושאים חותמת חמישית, trf_timestamp, המציינת מתי מתקן דיווח עסקאות (TRF) קלט את הדיווח.

מדוע חותמות זמן של נתוני שוק אינן תואמות בין זירות

הפער בין חותמת הזירה לחותמת המאוחדת הוא הזמן שפרינט בילה במעבר ובתור של המעבד. זה אינו מספר קבוע. כל זירה ממוקמת במרחק שונה מהמעבד, על חומרה שונה ומאחורי תור שונה. הפאנל להלן מודד את הפער הזה עבור כל זירה שביצעה פרינט ב-AAPL במהלך חצי שעה קבועה ב-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 מיקרו-שניות, מה שחושף את ה"זנב" שהחציון מסתיר.

באיזו חותמת זמן כדאי להשתמש

ארבעה כללים מכסים כמעט כל מקרה.

  • זמן משתתף לעבודת מיקרו-מבנה ומחקרי אירועים. כל מדידה של מה שקרה בזירה ובאיזה סדר שייכת לשעון הזירה. שחזור ספר פקודות, נושא ה-נתוני ספר פקודות MBO לעומת MBP, אינו ניתן לביצוע על בסיס שעון אחר.
  • זמן SIP לכל מה שחייב להתיישב עם הטייפ הרשמי. דיווחים רגולטוריים, סקירת ביצועים מיטביים (best execution), פתיחה וסגירה רשמיות, וכל נתון שצד נגדי יבדוק מול הרשומה המאוחדת.
  • זמן קליטה רק למדידת הנתיב שלכם. הוא אומר לכם כמה זמן לקח לנתונים להגיע למכונה שלכם. הוא לא אומר דבר על מתי העסקה התרחשה, ושתי מכונות לעולם לא יסכימו עליו.
  • לעולם אל תערבבו שעונים בתוך מערך נתונים אחד. ביצוע Join שמשווה ציטוטים לפי שעון אחד מול עסקאות לפי שעון אחר יחזיר מספרים שנראים סבירים, אך יכשלו בדיוק ברגעים הקריטיים.

אותם פרינטים, ממוינים בשתי דרכים

מיון הוא המקום שבו ההפשטה נשברת. הפאנל להלן לוקח את עשר המילי-שניות העמוסות ביותר מאותה חצי שעה, שומר על שנים-עשר הפרינטים הראשונים בבורסה לפי סדר הזירה, ואז מדרג את אותם שנים-עשר שוב לפי סדר הטייפ המאוחד. הטבלה ממוינת לפי המרחק שכל פרינט עבר בין שני הדירוגים.

שאילתהאותן 12 עסקאות (prints), מדורגות לפי שעון זירה ושעון 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
Run this yourself

הפער הגדול ביותר בין שני הדירוגים של פרינט הוא 6. פרינט זה עזב את הזירה שלו ב-10:44:17.160712 והגיע לטייפ 305.7 מיקרו-שניות לאחר מכן, ב-10:44:17.161018, כשהוא עובר ממיקום 6 בשעון הזירה למיקום 12 בטייפ. אף אחד מהסדרים אינו שגוי. הם עונים על שאלות שונות. מחקר רצף עסקאות שרץ על שעון הטייפ קורא את פרץ הפעילות הזה בסדר שאף זירה לא ייצרה, וסקירת ביצועים מיטביים שרצה על שעון הזירה אינה תואמת את הרשומה הרשמית.

פרינטים מחוץ לבורסה מגיעים זמן רב אחרי האירוע

עסקה שבוצעה מחוץ לבורסה, אצל סיטונאי או ב-Dark Pool, מדווחת למתקן דיווח עסקאות במקום להיות מותאמת בספר פקודות ציבורי. הדיווח נושא את זמן הביצוע, והוא מגיע לטייפ לאחר מכן. מרווח זה הוא עיכוב דיווח ולא זמן מעבר, והוא גדול בסדרי גודל.

שאילתהעסקאות 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

מתוך פרינטים של AAPL מחוץ לבורסה באותו חלון זמן, 24.8% נוחתים בתוך ה-bucket של under 1 ms. הזנב נמשך עד ל-bucket של 1 s to 10 s, המכיל 74 פרינטים. פרינט שמגיע באיחור של עשר שניות עדיין נושא את זמן שעון הזירה שבו הוא בוצע בפועל, בעודו יושב בזרם של הטייפ עשר שניות אחרי הזמן שלו. אם תמיינו לפי זמן טייפ, הוא יופיע בדקה הלא נכונה. פרינטים המדווחים מחוץ לרצף הרגיל נושאים קודי תנאי מכירה המציינים זאת, וזהו אחד הדברים ש-קודי תנאי מסחר נועדו לסמן.

מדוע שני אנשים בונים גרפים שונים מאותן עסקאות

כמעט כל תלונה בנוסח "הנתונים שגויים" מגיעה לכאן. גרף (bar) הוא bucket של פרינטים, וה-bucket שאליו פרינט נופל תלוי בחותמת שלפיה אתם מבצעים את הקיבוץ. הפאנל להלן סופר את הפרינטים שמשנים bucket כאשר עוברים משעון הזירה לשעון הטייפ, על פני ארבעה אורכי גרף נפוצים.

שאילתהעסקאות שמשנות עמודה (bar) במעבר בין שעונים, לפי אורך עמודה
ה-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% מהפרינטים באותו חלון זמן נופלים לגרף שונה תחת שני השעונים, סך הכל 8944 פרינטים. אם תמתחו את הגרף ל-5 minutes, המספר ירד ל-0.024%. התבנית מכנית: פרינט משנה bucket בכל פעם שהפער בשעון שלו חוצה גבול, וגרפים קצרים יותר נושאים יותר גבולות. שני ספקים יכולים שניהם להיות צודקים ועדיין לפרסם נפח מסחר שונה לאותה דקה. בניית הגרפים עצמה מוסברת ב-כיצד נבנים גרפי OHLCV.

שדה ננו-שנייה אינו אומר דיוק של ננו-שנייה

שתי החותמות מגיעות כמספרים שלמים ברזולוציה של ננו-שנייה. רזולוציה היא מה ששדה יכול להביע. דיוק הוא עד כמה הערך קרוב לזמן האמת, ושני אלו נקבעים על ידי דברים שונים לחלוטין.

יישור שעונים בתעשייה נשלט על ידי סובלנות רגולטורית ולא על ידי פיזיקה. כלל סנכרון השעונים של FINRA מחייב את שעוני העסקים של החברות החברות להיות בטווח של 50 מילי-שניות מהתקן של NIST. בורסות ומעבדים רצים בדיוק גבוה בהרבה, תוך שימוש ב-Precision Time Protocol (PTP, מתוקנן כ-IEEE 1588), המפיץ שעון ייחוס על גבי אותה רשת שנושאת את הנתונים ושומר על המכונות ביישור של תת-מיקרו-שנייה.

שני דברים נובעים מכך. בתוך חותמות של ארגון אחד, סדר ברזולוציה של מיקרו-שנייה הוא בעל משמעות. בין ארגונים, הבדל של כמה מאות ננו-שניות בין שתי חותמות נמצא בתוך טווח השגיאה, והתייחסות אליו כאל סדר אמיתי היא קריאת רעש.

שאלות נפוצות

מה ההבדל בין חותמת הזמן של ה-SIP לבין חותמת הזמן של המשתתף?

חותמת הזמן של המשתתף נכתבת על ידי הזירה כאשר היא מפרסמת עסקה בפיד שלה. חותמת הזמן של ה-SIP נכתבת על ידי מעבד הטייפ המאוחד כאשר העסקה מגיעה לפיד המשולב הרשמי. הפער ביניהם הוא זמן מעבר ותור, הנמדד במיקרו-שניות עבור פרינטים בבורסה ולעיתים קרובות במילי-שניות או יותר עבור פרינטים המדווחים דרך מתקן דיווח עסקאות.

באיזו חותמת זמן של נתוני שוק עלי להשתמש ל-backtesting?

השתמשו בחותמת הזמן של המשתתף לכל מה שמדמה מה משתתף יכול היה לראות או לעשות בזירה, ובחותמת ה-SIP לכל מה שחייב להתיישב עם הרשומה המאוחדת הרשמית. לא משנה במי תבחרו, החילו זאת על כל טבלה במחקר, כולל ציטוטים.

מדוע גרפי הדקה שלי אינם תואמים לאלו של ספק הנתונים שלי?

אי-התאמה בשעונים היא התשובה הרגילה. פרינט שחותמת הזירה שלו נופלת רגע לפני גבול דקה יכול לשאת חותמת טייפ רגע אחריו, מה שמציב את אותה עסקה בגרפים שונים תחת שתי המוסכמות. פרינטים מדווחים באיחור מחוץ לבורסה מרחיבים את האפקט הזה.

האם חותמות זמן של ננו-שנייה מדויקות עד לרמת הננו-שנייה?

לא. השדה מכיל רזולוציה של ננו-שנייה, והדיוק נקבע לפי רמת הסנכרון של שעון המכונה הכותבת. מערכות בורסה ומעבד המשתמשות ב-PTP שומרות על יישור של תת-מיקרו-שנייה, בעוד שעוני עסקים של ברוקרים מוחזקים בסובלנות רגולטורית של 50 מילי-שניות. השוואות עדינות יותר מהסובלנות של השעון הרופף יותר אינן בעלות משמעות.


כל פאנל לעיל מגיע עם ה-SQL שיצר אותו, כך שתוכלו לראות בדיוק מאיזה שעון הגיע כל מספר. כדי למיין מחדש חלון פרינטים משלכם תחת שעון אחר ולצפות בטייפ משתנה, שאלו את השאלה באנגלית פשוטה בטרמינל Strasmore.