In real-time forex trading systems, subscribing to multiple currency pairs seems like a simple scalability feature. However, once deployed in production, a subtle but critical issue quickly emerges: data out-of-order delivery.

Especially in high-frequency streaming scenarios—such as simultaneously subscribing to EURUSD, GBPUSD, and USDJPY—you will find that data within the same time window does not arrive in strict chronological order. Instead, it is affected by network latency, server-side parallelism, and client-side scheduling.

At first glance, it looks like a minor sequencing issue. But in trading systems, it can directly impact:

  • Incorrect candlestick generation
  • Misaligned indicator calculations
  • Abnormal quoting behavior in market making strategies
  • False risk triggers

At its core, this is not a “data problem”, but a consistency problem in streaming systems.

1. The Nature of Multi-Currency Subscription: One Connection, Multiple Time Streams

In most Forex APIs, real-time WebSocket design follows a “single connection, multiple subscriptions” model:

  • One TCP connection
  • Multiple symbols subscribed simultaneously
  • Server pushes data concurrently for different currency pairs

For example:

  • EURUSD: high update frequency
  • GBPUSD: volatile and bursty
  • USDJPY: liquidity concentrated in specific sessions

These streams are generated in parallel on the server side but merged into a single message stream on the client side.

This creates a fundamental issue:

Multiple independent timelines are forced into a single-threaded message queue.

As a result, data order becomes unreliable.

2. How Does Out-of-Order Data Occur?

Out-of-order delivery typically comes from four layers:

2.1. Network Jitter

TCP guarantees reliability, not business-level ordering.

Packets may:

  • Take different routing paths
  • Experience varying queue delays
  • Be acknowledged in batches

Result: later-generated data may arrive earlier.

2.2. Server-Side Parallel Publishing

Market data servers are typically multi-threaded:

  • Symbol A generated in thread 1
  • Symbol B generated in thread 2

Publishing is merged into a queue, not strictly time-sorted.

2.3. Client Event Loop Scheduling

In Python/Node.js WebSocket clients:

  • IO thread ≠ business thread
  • Callback execution is asynchronous
  • Processing delays exist

Thus:

Arrival order ≠ generation order ≠ timestamp order

2.4. Shared Buffer Queue Across Symbols

A naive implementation often uses:

All symbols → one on_message → one queue

This destroys structural ordering entirely.

3. Real Impact of Out-of-Order Data

Out-of-order data is not just cosmetic—it breaks trading logic:

3.1. Incorrect Candlestick Formation

For 1-second candles:

  • Tick B arrives first
  • Tick A arrives later

Result: OHLC values become incorrect.

3.2. Distorted Momentum Signals

Short-term strategies rely on sequence:

  • return(t) vs return(t-1)

If order is broken:

Momentum signals become noise-contaminated.

3.3. Abnormal Market Making Quotes

If quote updates are misordered:

  • Spread widens artificially
  • Mid-price becomes unstable
  • Hedging triggers incorrectly

4. Core Solution: Decouple Time from Message Order

The key is not to “prevent disorder”, but to:

Reconstruct deterministic order on the client side.

Three key fields are used:

  • timestamp
  • seq_id
  • symbol

5. Engineering Solution: Three-Layer Reordering Architecture

Layer 1: Separate Streams by Symbol

Never mix all symbols into one queue:

streams = {
    "EURUSD": Queue(),
    "GBPUSD": Queue(),
    "USDJPY": Queue()
}

Each currency pair maintains its own timeline.

Layer 2: Introduce Time Window Buffering

Use a short buffer per symbol:

BUFFER_MS = 200

Logic:

  1. Receive tick
  2. Do not consume immediately
  3. Store in buffer
  4. Sort by timestamp before output

Layer 3: Monotonic Sequence Validation

If API provides seq_id:

last_seq = {}

def check_order(symbol, tick):
    seq = tick["seq"]

    if symbol not in last_seq:
        last_seq[symbol] = seq
        return True

    if seq > last_seq[symbol]:
        last_seq[symbol] = seq
        return True

    return False

This filters most replay or delayed packets.

6. Production-Grade WebSocket Subscription Example

A more realistic multi-symbol structure:

import websocket
import json
import uuid
import time
from collections import defaultdict, deque

API_KEY = "YOUR_API_KEY"
WS_URL = f"wss://quote.alltick.co/quote-b-ws-api?token={API_KEY}"

SYMBOLS = ["EURUSD", "GBPUSD", "USDJPY"]

buffers = defaultdict(deque)
last_seq = {}

def subscribe_msg():
    return {
        "cmd_id": 22004,
        "seq_id": int(time.time()),
        "trace": str(uuid.uuid4()),
        "data": {
            "symbol_list": [{"code": s} for s in SYMBOLS]
        }
    }

def on_open(ws):
    ws.send(json.dumps(subscribe_msg()))

def process_tick(symbol, tick):
    seq = tick.get("seq")

    if seq is not None:
        if symbol in last_seq and seq <= last_seq[symbol]:
            return
        last_seq[symbol] = seq

    buffers[symbol].append(tick)

def on_message(ws, message):
    msg = json.loads(message)

    if msg.get("cmd_id") == 22998:
        tick = msg["data"]
        symbol = tick["code"]
        process_tick(symbol, tick)

def start():
    ws = websocket.WebSocketApp(
        WS_URL,
        on_open=on_open,
        on_message=on_message
    )
    ws.run_forever(ping_interval=10)

if __name__ == "__main__":
    start()

7. Why Forex Is More Prone to Out-of-Order Than Crypto

Compared to crypto markets, forex APIs have unique characteristics:

7.1. Distributed liquidity providers

Quotes come from multiple LPs

7.2. Uneven update frequency

EURUSD vs exotic pairs behave very differently

7.3. Session-based volatility

Asian / London / US sessions

Result:

Data behaves like intermittent bursts, not continuous flow.

8. Advanced Direction: From Ordering Fix to Event-Time Systems

Advanced systems go beyond reordering:

Event-time processing

Not arrival-time, but event-time

Watermarking

Allow bounded lateness:

  • Accept late ticks within window
  • Drop beyond threshold

Symbol-level clock synchronization

Align multiple instruments into a unified time framework

9. When the System Stabilizes

Once ordering issues are properly handled, market data transforms:

  • EURUSD stops “jumping”
  • GBPUSD stops “spiking randomly”
  • USDJPY becomes structurally stable

What emerges is no longer a raw data stream, but a structured temporal system:

  • Each symbol has its own rhythm
  • Each tick has a deterministic position
  • Every movement becomes traceable

On top of this foundation, trading systems—whether market making, arbitrage, or risk control—can operate reliably.

And infrastructures like AllTick API provide not just market data, but an engineering-grade real-time time-stream layer.