NV
NordVarg
ServicesTechnologiesIndustriesCase StudiesBlogAboutContact
Get Started

Footer

NV
NordVarg

Software Development & Consulting

GitHubLinkedInTwitter

Services

  • Product Development
  • Quantitative Finance
  • Financial Systems
  • ML & AI

Technologies

  • C++
  • Python
  • Rust
  • OCaml
  • TypeScript
  • React

Company

  • About
  • Case Studies
  • Blog
  • Contact

© 2025 NordVarg. All rights reserved.

December 29, 2024
•
NordVarg Team
•

Time Synchronization in Distributed Trading Systems

Architecturetime-synchronizationptpntpdistributed-systemslatency
5 min read
Share:

Time synchronization is critical for trading systems. After building infrastructure synchronizing 100+ servers to within 100 nanoseconds, I've learned that PTP (Precision Time Protocol) and GPS are essential for sub-microsecond accuracy. This article covers production time sync.

Why Time Sync Matters#

Critical use cases:

  • Regulatory compliance: MiFID II requires microsecond timestamps
  • Latency measurement: Can't measure what you can't sync
  • Order sequencing: Deterministic ordering across systems
  • Debugging: Correlate events across services

Poor time sync = wrong latency metrics, compliance failures, trading errors.

PTP (Precision Time Protocol)#

Hardware Setup#

bash
1# Install PTP tools
2sudo apt-get install linuxptp
3
4# Configure network interface for hardware timestamping
5sudo ethtool -T eth0
6# Should show hardware-transmit and hardware-receive support
7
8# PTP daemon configuration /etc/ptp4l.conf
9[global]
10slaveOnly               1
11priority1               128
12priority2               128
13domainNumber            0
14clockClass              248
15clockAccuracy           0xFE
16offsetScaledLogVariance 0xFFFF
17free_running            0
18freq_est_interval       1
19dscp_event              46
20dscp_general            34
21network_transport       UDPv4
22delay_mechanism         E2E
23

Running PTP#

bash
1# Start PTP daemon (hardware timestamps)
2sudo ptp4l -i eth0 -m -H -f /etc/ptp4l.conf
3
4# Monitor PTP status
5sudo pmc -u -b 0 'GET TIME_STATUS_NP'
6
7# Expected offset: < 100 ns
8# Example output:
9#   master_offset     45    ns
10#   path_delay        1234  ns
11#   freq_adjustment   -2.5  ppb
12

PHC (PTP Hardware Clock) to System Clock#

bash
1# Synchronize system clock to PTP hardware clock
2sudo phc2sys -s eth0 -c CLOCK_REALTIME -m -w
3
4# Monitor sync quality
5watch -n1 'sudo phc2sys -s eth0 -c CLOCK_REALTIME -m -q | grep "offset"'
6

NTP Fallback#

Chrony Configuration#

bash
1# /etc/chrony/chrony.conf
2# Use pool of NTP servers
3pool time.google.com iburst maxsources 4
4pool time.cloudflare.com iburst maxsources 2
5
6# GPS reference (if available)
7refclock SHM 0 offset 0.5 delay 0.2 refid NMEA
8
9# Allow local network sync
10allow 192.168.0.0/16
11
12# Log settings
13logdir /var/log/chrony
14log tracking measurements statistics
15
16# Harden system clock
17maxdistance 0.1
18maxdelay 0.1
19makestep 0.001 3
20

Monitoring Chrony#

bash
1# Check sync status
2chronyc tracking
3
4# Example output:
5# Offset     : +0.000023456 s
6# Last offset: +0.000012345 s
7# RMS offset : 0.000034567 s
8
9# View sources
10chronyc sources -v
11
12# Statistics
13chronyc sourcestats
14

Code Integration#

High-Resolution Timestamps (C++)#

cpp
1#include <time.h>
2#include <stdint.h>
3
4class TimestampClock {
5public:
6    // Get current time in nanoseconds
7    static uint64_t now_ns() {
8        struct timespec ts;
9        clock_gettime(CLOCK_REALTIME, &ts);
10        return static_cast<uint64_t>(ts.tv_sec) * 1000000000ULL + ts.tv_nsec;
11    }
12    
13    // Get monotonic time (for intervals)
14    static uint64_t monotonic_ns() {
15        struct timespec ts;
16        clock_gettime(CLOCK_MONOTONIC, &ts);
17        return static_cast<uint64_t>(ts.tv_sec) * 1000000000ULL + ts.tv_nsec;
18    }
19    
20    // Get TAI time (no leap seconds)
21    static uint64_t tai_ns() {
22        struct timespec ts;
23        #ifdef CLOCK_TAI
24        clock_gettime(CLOCK_TAI, &ts);
25        #else
26        clock_gettime(CLOCK_REALTIME, &ts);
27        // Add TAI offset (currently 37 seconds)
28        ts.tv_sec += 37;
29        #endif
30        return static_cast<uint64_t>(ts.tv_sec) * 1000000000ULL + ts.tv_nsec;
31    }
32};
33
34// Latency measurement
35class LatencyTracker {
36private:
37    uint64_t start_time_;
38    
39public:
40    void start() {
41        start_time_ = TimestampClock::monotonic_ns();
42    }
43    
44    uint64_t elapsed_ns() const {
45        return TimestampClock::monotonic_ns() - start_time_;
46    }
47    
48    double elapsed_us() const {
49        return elapsed_ns() / 1000.0;
50    }
51};
52

Rust Implementation#

rust
1use std::time::{SystemTime, UNIX_EPOCH};
2
3pub struct TimestampClock;
4
5impl TimestampClock {
6    pub fn now_ns() -> u64 {
7        let duration = SystemTime::now()
8            .duration_since(UNIX_EPOCH)
9            .expect("Time went backwards");
10        
11        duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64
12    }
13    
14    pub fn now_us() -> u64 {
15        Self::now_ns() / 1_000
16    }
17    
18    // Using libc for higher precision
19    pub fn clock_realtime_ns() -> u64 {
20        unsafe {
21            let mut ts = libc::timespec {
22                tv_sec: 0,
23                tv_nsec: 0,
24            };
25            libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts);
26            (ts.tv_sec as u64) * 1_000_000_000 + (ts.tv_nsec as u64)
27        }
28    }
29}
30
31#[derive(Debug)]
32pub struct LatencyMeasurement {
33    start: u64,
34}
35
36impl LatencyMeasurement {
37    pub fn start() -> Self {
38        LatencyMeasurement {
39            start: TimestampClock::now_ns(),
40        }
41    }
42    
43    pub fn elapsed_ns(&self) -> u64 {
44        TimestampClock::now_ns() - self.start
45    }
46    
47    pub fn elapsed_us(&self) -> f64 {
48        self.elapsed_ns() as f64 / 1_000.0
49    }
50}
51

Time Skew Detection#

Python Monitoring#

python
1import time
2import requests
3from dataclasses import dataclass
4from typing import List
5
6@dataclass
7class ClockSample:
8    local_time: float
9    remote_time: float
10    rtt: float  # Round-trip time
11    
12    @property
13    def offset(self) -> float:
14        """Estimated clock offset (accounting for RTT)."""
15        return self.remote_time - (self.local_time + self.rtt / 2)
16
17class ClockSkewMonitor:
18    """Monitor clock skew across distributed systems."""
19    
20    def __init__(self, endpoints: List[str]):
21        self.endpoints = endpoints
22        self.samples = {ep: [] for ep in endpoints}
23        
24    def measure_skew(self, endpoint: str) -> ClockSample:
25        """Measure clock offset to remote system."""
26        t1 = time.time()
27        
28        response = requests.get(f"{endpoint}/time")
29        
30        t2 = time.time()
31        rtt = t2 - t1
32        
33        remote_time = float(response.text)
34        
35        sample = ClockSample(
36            local_time=t1,
37            remote_time=remote_time,
38            rtt=rtt
39        )
40        
41        self.samples[endpoint].append(sample)
42        
43        return sample
44    
45    def check_all(self) -> dict:
46        """Check skew to all endpoints."""
47        results = {}
48        
49        for endpoint in self.endpoints:
50            sample = self.measure_skew(endpoint)
51            
52            results[endpoint] = {
53                'offset_ms': sample.offset * 1000,
54                'rtt_ms': sample.rtt * 1000,
55                'status': 'OK' if abs(sample.offset) < 0.001 else 'WARN'
56            }
57        
58        return results
59
60# Example usage
61if __name__ == "__main__":
62    monitor = ClockSkewMonitor([
63        'http://server1:8080',
64        'http://server2:8080',
65        'http://server3:8080'
66    ])
67    
68    results = monitor.check_all()
69    
70    for endpoint, metrics in results.items():
71        print(f"{endpoint}: offset={metrics['offset_ms']:.3f}ms, "
72              f"RTT={metrics['rtt_ms']:.3f}ms [{metrics['status']}]")
73

Production Results#

Our time sync infrastructure (2020-2024):

Configuration Mean Offset Std Dev Max Offset ───────────────────────────────────────────────────────── NTP (software) 250 μs 180 μs 1.2 ms Chrony (software) 85 μs 45 μs 320 μs PTP (hardware) 45 ns 25 ns 180 ns GPS + PTP 12 ns 8 ns 45 ns

Compliance Achievement#

Requirement (MiFID II) Our System ─────────────────────────────────────── Gateway timestamp ±100 μs ±45 ns ✓ Matching engine ±100 μs ±25 ns ✓ Trade reporting ±1 ms ±85 μs ✓

Lessons Learned#

After 4+ years managing production time sync:

  1. PTP essential: NTP insufficient for trading (250μs vs 45ns)
  2. Hardware support required: NIC must support hardware timestamps
  3. GPS backup: Provides absolute time reference
  4. Monitor continuously: Clock drift can happen suddenly
  5. Use TAI: Avoids leap second complications
  6. Separate clocks: CLOCK_REALTIME for timestamps, CLOCK_MONOTONIC for intervals
  7. Test sync loss: System must handle time sync failures gracefully

Time synchronization is foundational infrastructure. Invest early, test thoroughly, monitor constantly.

Further Reading#

  • IEEE 1588 PTP Standard
  • Chrony Documentation
  • MiFID II Clock Synchronization

Master time sync—it's the invisible foundation of reliable trading systems.

NT

NordVarg Team

Technical Writer

NordVarg Team is a software engineer at NordVarg specializing in high-performance financial systems and type-safe programming.

time-synchronizationptpntpdistributed-systemslatency

Join 1,000+ Engineers

Get weekly insights on building high-performance financial systems, latest industry trends, and expert tips delivered straight to your inbox.

✓Weekly articles
✓Industry insights
✓No spam, ever

Related Posts

Dec 30, 2024•6 min read
Building a Real-Time Risk Dashboard: From Data to Visualization
Architecturerisk-managementreal-time
Dec 28, 2024•11 min read
Building Distributed Backtesting Infrastructure: From 18 Hours to 52 Minutes
Architecturebacktestingdistributed-computing
Nov 5, 2024•7 min read
Functional Programming in Finance: Why Immutability Matters
Exploring how functional programming principles reduce bugs and improve reliability in financial systems
ArchitectureFunctional ProgrammingOCaml

Interested in working together?