TT Lab
Get started
Learn Learning paths Courses

Capital Markets and Settlement

After the clock change, two markets closed at the same instant

Continue in TT Lab

Goal

You convert the local-time execution records of four markets to UTC using time zone rules, separate the special times on the daylight saving transition days, assign session windows and trading-day labels, and then find the days on which the markets' closes overlap in UTC and the contradictions in the calendar itself.

Why it matters

If you do time conversion with a fixed offset, no exception is raised. A value that is simply an hour off gets stored, and that fact only comes out after the transition weekend has passed and reconciliation does not match. In capital markets this drift reaches the closing batch and the tallies right away. On top of that, the transition dates differ by market, so every year there are several weeks in which one side is already on summer time and the other is still on winter time. It really does happen that the closes of two markets fall at the same moment only during those weeks.

Steps

  1. Use python3 to create markets.json, calendar.json, and trades.csv in /root/session/data.
  2. Convert the local times to UTC using time zone rules and write them to /root/session/normalized.csv as trade_id,market,utc_ms. Do not include times that do not exist in that local time.
  3. Convert the same executions with a fixed winter offset and write the ones whose result differs to /root/session/fixed_diff.csv.
  4. Write the nonexistent times to /root/session/nonexistent.csv and the twice-occurring times to /root/session/ambiguous.csv.
  5. Write each execution's session window to /root/session/phase.csv as trade_id,phase.
  6. Write the executions whose calendar date and trading day differ to /root/session/tradingday.csv.
  7. For each reference date, write each market's regular close to /root/session/close_utc.csv, and the market pairs whose closes are close together to /root/session/overlap.csv.
  8. Write the calendar's own contradictions to /root/session/calendar_defects.csv, and summarize the counts from the earlier steps in /root/session/summary.json.

Notes

Generate the session definitions, calendars, and execution data for four markets

Use python3 to create markets.json, calendar.json, and trades.csv in /root/session/data. Use the generation script as is, which uses no random numbers.

We cannot use the customer's data as it is, so we build synthetic data of the same shape. Without random numbers, the same data comes out no matter who runs it how many times. The grader converts the data to a canonical form and compares fingerprints, so if you edit it by hand, all the later steps get blocked.

Convert local time to UTC by time zone rules

Put the first line trade_id,market,utc_ms in /root/session/normalized.csv and write the executions in UTC milliseconds. Do not include times that do not exist in that local time.

Attach the time zone with zoneinfo.ZoneInfo. You can tell whether a time does not exist by going to UTC and coming back to the same time zone and checking whether the wall-clock value is unchanged. For a twice-occurring time, use the side that passed first.

Convert with a fixed offset and find the days that are off

Put the first line trade_id,tz_utc_ms,fixed_utc_ms,diff_min in /root/session/fixed_diff.csv and write the executions whose value converted with the fixed winter offset differs from the value converted by time zone rules.

The winter offset is that time zone's offset at 2025-01-15 12:00. Do not write the value in the code; derive it from the time zone. diff_min is the difference in minutes obtained by subtracting the rule value from the fixed-offset value. Nonexistent times do not go in this table.

Separate nonexistent times from twice-occurring times

Write trade_id,market,local_ts in /root/session/nonexistent.csv and trade_id,market,local_ts,first_utc_ms,second_utc_ms in /root/session/ambiguous.csv.

In a market that moves the clock forward in spring, the skipped hour does not exist. In a market that sets the clock back in autumn, the same wall-clock value passes twice. If you check whether the offsets of the fold 0 and 1 cases differ, you can catch both cases together, and which of the two it is gets decided by the round-trip test.

Assign a session window to each execution

Put the first line trade_id,phase in /root/session/phase.csv and assign every execution one of pre, regular, post, closed, or holiday.

Judge market holidays first. Then look in the order pre-open, regular, post-close, and if it is in none of them, it is a closed time. On a day with a half-day session, only the regular close changes. A window includes its start and excludes its end.

Attach trading-day labels for sessions that cross midnight

Put the first line trade_id,calendar_date,trading_day in /root/session/tradingday.csv and write only the executions whose two values differ.

The markets that have a session crossing midnight are written in the data. In such a market, an execution earlier than the time the post-close window ends belongs to the previous day's session. Other markets are always equal to the calendar date.

Gather each market's close in UTC and find overlapping batch windows

Write market,date,close_utc_ms in /root/session/close_utc.csv and date,market_a,market_b,gap_min in /root/session/overlap.csv.

The list of reference dates and the criterion for closeness are in the data. A market that is on holiday has no close that day. The close of a session that crosses midnight falls on the next day, so you have to add one day to the date. For a market pair, write the one that comes first in dictionary order first.

Find the calendar's own contradictions and summarize everything

Write market,date,defect in /root/session/calendar_defects.csv, and summarize trades, nonexistent, ambiguous, fixed_diff, phases, trading_day_differs, overlaps, and calendar_defects in /root/session/summary.json.

There are two contradictions. One is the case where the same day is written as both a market holiday and a half-day, and the other is where the half-day close is later than the regular close. Do not count the numbers in the summary by hand; take them from the same computations as in the earlier steps. phases is a count object keyed by window name.