Skip to main content

chicago colocation - 67ms to 12ms latency improvement, worth the cost

moved execution server to chicago colo march 2024.

3 months data in.

latency dropped 67ms → 12ms average.

why chicago
#

CME exchange location: chicago

my location: san diego

problem: 67ms average latency san diego → CME

solution: colocation in chicago = 12ms

55ms improvement = better fills.

the cost breakdown
#

datacenter: equinix CH1 (chicago)

monthly: $175/month

breakdown:

  • rack space (1U): $85
  • power (200W): $45
  • bandwidth (100mbps): $35
  • remote hands: $10

total annual: $2,100

san diego electricity cost: $18/month

net increase: $157/month ($1,884/year)

the server
#

hardware: dell poweredge r240

bought used on ebay: $800

specs:

  • intel xeon e-2224 (4 cores, 3.4ghz)
  • 32gb ecc ram
  • 2x 500gb ssd (raid 1)
  • dual gigabit ethernet
  • remote management (idrac)

shipped to datacenter direct.

datacenter racked it ($50 one-time).

latency comparison
#

before (san diego home):

avg latency: 67ms

p50: 65ms

p95: 89ms

p99: 124ms

after (chicago colo):

avg latency: 12ms

p50: 11ms

p95: 18ms

p99: 26ms

improvement: 55ms average (82% reduction)

real trading impact
#

slippage improvement:

san diego avg: 2.4 ticks

chicago avg: 1.8 ticks

0.6 tick improvement = $180/month on current volume.

ROI: $180/month saves > $157/month cost

net positive after 1 year.

python monitoring setup
#

import asyncio
import time
import statistics
from datetime import datetime
import redis

class LatencyMonitor:
    """
    Monitor execution latency to exchanges
    Track p50, p95, p99 over rolling windows
    """
    def __init__(self, redis_client):
        self.redis = redis_client
        self.latencies = []
        self.window_size = 1000  # Rolling window

    async def ping_exchange(self, exchange_url):
        """
        Measure round-trip time to exchange
        """
        start = time.perf_counter()

        # Send minimal request
        async with aiohttp.ClientSession() as session:
            try:
                async with session.get(
                    exchange_url,
                    timeout=aiohttp.ClientTimeout(total=5)
                ) as response:
                    await response.text()

                end = time.perf_counter()
                latency_ms = (end - start) * 1000

                return latency_ms

            except Exception as e:
                print(f"Ping failed: {e}")
                return None

    def add_latency(self, latency_ms):
        """
        Add latency measurement to rolling window
        """
        self.latencies.append(latency_ms)

        # Keep only recent measurements
        if len(self.latencies) > self.window_size:
            self.latencies.pop(0)

        # Store in Redis for Grafana
        self.redis.lpush('latency_measurements', latency_ms)
        self.redis.ltrim('latency_measurements', 0, self.window_size - 1)

    def get_stats(self):
        """
        Calculate latency statistics
        """
        if not self.latencies:
            return None

        sorted_latencies = sorted(self.latencies)
        n = len(sorted_latencies)

        stats = {
            'avg': statistics.mean(self.latencies),
            'median': statistics.median(self.latencies),
            'p50': sorted_latencies[int(n * 0.50)],
            'p95': sorted_latencies[int(n * 0.95)],
            'p99': sorted_latencies[int(n * 0.99)],
            'min': min(self.latencies),
            'max': max(self.latencies),
            'count': n
        }

        return stats

    async def monitor_loop(self, exchange_url, interval_seconds=60):
        """
        Continuous monitoring loop
        """
        while True:
            latency = await self.ping_exchange(exchange_url)

            if latency:
                self.add_latency(latency)
                stats = self.get_stats()

                print(f"{datetime.now().isoformat()}")
                print(f"Latency: {latency:.2f}ms")
                print(f"Avg: {stats['avg']:.2f}ms")
                print(f"P95: {stats['p95']:.2f}ms")
                print(f"P99: {stats['p99']:.2f}ms")
                print("---")

            await asyncio.sleep(interval_seconds)

# Usage
redis_client = redis.Redis(host='localhost', port=6379, db=0)
monitor = LatencyMonitor(redis_client)

# Monitor CME latency every minute
asyncio.run(
    monitor.monitor_loop(
        exchange_url='https://cme.com/api/health',
        interval_seconds=60
    )
)

this runs 24/7 on chicago server.

grafana dashboard shows real-time latency.

network path comparison
#

san diego path:

home → ISP → internet backbone → chicago → CME

hops: 18

latency: 67ms avg

chicago colo path:

datacenter → local exchange → CME

hops: 4

latency: 12ms avg

fewer hops = lower latency.

reliability improvement
#

san diego (home internet):

uptime: 99.2% (comcast)

outages in 2023: 6 times

longest outage: 4 hours

chicago colo:

uptime: 99.95% (equinix SLA)

outages since march: 0

datacenter power + network > home internet.

remote management
#

idrac (dell remote management):

  • KVM over IP
  • remote power cycling
  • BIOS access
  • OS installation
  • monitoring (temps, fans, power)

accessed from san diego.

feels like server is local.

never needed “remote hands” service yet.

security setup
#

firewall rules:

only allow:

  • my home IP (ssh, idrac)
  • exchange IPs (trading)
  • monitoring (grafana cloud)

everything else blocked.

VPN required for emergency access.

2FA on all logins.

bandwidth usage
#

allocated: 100mbps

actual usage: 8-12mbps avg

spikes: 40mbps during high vol

plenty of headroom.

100mbps adequate for my volume.

power consumption
#

measured: 180W avg

allocated: 200W

cost: $45/month for 200W

efficient server = lower cost.

comparing to cloud (aws)
#

aws equivalent:

c6i.xlarge in us-east-1 (closest to chicago):

  • 4 vcpu
  • 8gb ram
  • $140/month (3yr reserved)
  • network: $50/month estimate
  • total: $190/month

equinix colo:

  • dedicated hardware
  • better latency
  • $175/month

colo wins on performance + cost.

lessons learned
#

1. location matters for futures

CME in chicago = chicago colo optimal.

2. latency compounds

55ms × 30 trades/month = 1.65 seconds saved.

better fills add up.

3. remote management essential

idrac saved 3 trips to chicago.

worth the hardware cost.

4. bandwidth overprovisioning

100mbps allocated, using 12mbps.

headroom for growth.

5. datacenter reliability

zero outages in 3 months.

home internet = 2 outages in same period.

ROI calculation
#

costs:

colo: $157/month net increase

server: $800 one-time (amortized $22/month over 3 years)

total: $179/month

benefits:

slippage improvement: $180/month

reliability: $0 measured (but prevented 2 missed trading days)

net: $1/month positive

barely break-even on slippage alone.

reliability value hard to quantify.

worth it for serious trading.

when NOT to use colo
#

don’t use colo if:

  1. trading <10 times per month (latency doesn’t matter)

  2. swing trading or longer holds (seconds don’t matter)

  3. account <$100k (cost not justified)

  4. no remote management experience (learning curve steep)

  5. testing strategies (home is fine)

colo for serious automation only.

what NexusFi traders say
#

found great infrastructure thread on NexusFi discussing colo vs cloud vs home.

consensus: colo for futures, cloud for stocks/options, home for testing.

matches my experience exactly.

future upgrades
#

considering:

dual servers (active/standby failover)

10gbps network upgrade

chicago CH2 datacenter (even closer to CME)

current setup adequate.

not upgrading unless volume 10x.

tonight
#

chicago colocation.

67ms → 12ms latency.

0.6 tick slippage improvement.

$157/month for reliability + speed.

worth it for serious algo trading.


11:42pm thursday. chicago colocation 3 months review. latency 67ms → 12ms (82% reduction). slippage improved 0.6 ticks = $180/month savings. colo cost $157/month net increase. barely break-even but reliability + speed worth it. zero outages vs 2 at home. dedicated hardware beats cloud for futures.

-AK

Related

upgrading chicago colocation to 10gbe - latency improvements
chicago colocation server needed upgrade. 1gbe connection = bottleneck. current setup # location: chicago datacenter (equinix CH1)
polygon.io vs alpha vantage - which data feed for algo trading
data feeds = foundation of algo trading. garbage data = garbage trades. i’ve used both polygon.io and alpha vantage extensively. spent months researching data feeds when i started trading. NexusFi community helped narrow down options to these two.
refactored data pipeline to async - 3x faster market data processing
been running synchronous data fetching since january. works but slow during market open. refactored to async this week. 3x speed improvement. the problem with sync code # # Old synchronous approach def fetch_market_data(symbols): results = [] for symbol in symbols: data = fetch_from_api(symbol) # Blocks here results.append(data) return results # With 10 symbols, takes 10 * 180ms = 1,800ms total each API call blocks until complete.
added redis caching - cut market data latency by 60%
been noticing market data latency creeping up. average fetch time: 180ms from polygon API. slowing down entry execution. the problem # every time algo needs current price:
rebuilt backtesting pipeline - 10x faster parameter optimization
spent last 3 days rebuilding backtest optimization pipeline. went from 6 hours to 35 minutes for full parameter sweep. the problem # old approach: sequential parameter testing.
using python async for real-time market data
rewrote my market data pipeline to use async. 3x faster, way cleaner code. the problem # old synchronous code: