132 lines
3.7 KiB
Python
132 lines
3.7 KiB
Python
|
|
"""
|
||
|
|
Latency Model — simulate realistic feed and order latencies.
|
||
|
|
|
||
|
|
Essential for:
|
||
|
|
- Realistic fill simulation
|
||
|
|
- Latency arbitrage defense
|
||
|
|
- Optimal order timing
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import math
|
||
|
|
from dataclasses import dataclass
|
||
|
|
from typing import Optional
|
||
|
|
|
||
|
|
import numpy as np
|
||
|
|
from numba import njit
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass(frozen=True, slots=True)
|
||
|
|
class LatencyState:
|
||
|
|
"""Latency state for the CWM."""
|
||
|
|
feed_latency_ms: float
|
||
|
|
order_latency_ms: float
|
||
|
|
feed_jitter_ms: float
|
||
|
|
order_jitter_ms: float
|
||
|
|
|
||
|
|
|
||
|
|
@njit(cache=True)
|
||
|
|
def simulate_feed_latency(
|
||
|
|
base_latency_ms: float,
|
||
|
|
jitter_ms: float,
|
||
|
|
rng_seed: int,
|
||
|
|
) -> float:
|
||
|
|
"""
|
||
|
|
Simulate feed latency with jitter.
|
||
|
|
|
||
|
|
Model: base_latency + uniform(-jitter, +jitter)
|
||
|
|
Returns latency in milliseconds.
|
||
|
|
"""
|
||
|
|
# Simple deterministic jitter using seed
|
||
|
|
jitter = jitter_ms * (2.0 * ((rng_seed % 1000) / 1000.0) - 1.0)
|
||
|
|
return max(0.0, base_latency_ms + jitter)
|
||
|
|
|
||
|
|
|
||
|
|
@njit(cache=True)
|
||
|
|
def simulate_order_latency(
|
||
|
|
base_latency_ms: float,
|
||
|
|
jitter_ms: float,
|
||
|
|
queue_position: int,
|
||
|
|
recent_trade_rate: float,
|
||
|
|
rng_seed: int = 0,
|
||
|
|
) -> float:
|
||
|
|
"""
|
||
|
|
Simulate order latency with queue dynamics.
|
||
|
|
|
||
|
|
Model:
|
||
|
|
- Base latency + jitter
|
||
|
|
- Additional latency from queue position (longer queue = slower fill)
|
||
|
|
- Reduced latency when trade rate is high (faster queue consumption)
|
||
|
|
|
||
|
|
Returns latency in milliseconds.
|
||
|
|
"""
|
||
|
|
jitter = jitter_ms * (2.0 * ((rng_seed % 1000) / 1000.0) - 1.0)
|
||
|
|
queue_delay = queue_position / max(recent_trade_rate, 0.01) * 1000.0
|
||
|
|
return max(0.0, base_latency_ms + jitter + queue_delay * 0.1)
|
||
|
|
|
||
|
|
|
||
|
|
@njit(cache=True)
|
||
|
|
def compute_latency_impact(
|
||
|
|
feed_latency_ms: float,
|
||
|
|
order_latency_ms: float,
|
||
|
|
price_change_per_ms: float,
|
||
|
|
) -> float:
|
||
|
|
"""
|
||
|
|
Compute the cost of latency in basis points.
|
||
|
|
|
||
|
|
Model:
|
||
|
|
- Feed latency: price moves before we see it
|
||
|
|
- Order latency: price moves before our order arrives
|
||
|
|
- Total cost = (feed_latency + order_latency) * price_change_per_ms
|
||
|
|
|
||
|
|
Returns cost in basis points.
|
||
|
|
"""
|
||
|
|
total_latency_ms = feed_latency_ms + order_latency_ms
|
||
|
|
# Assume price moves ~1bp per 10ms in volatile markets
|
||
|
|
cost_bps = total_latency_ms * price_change_per_ms
|
||
|
|
return cost_bps
|
||
|
|
|
||
|
|
|
||
|
|
class LatencyModel:
|
||
|
|
"""
|
||
|
|
Latency model for the CWM.
|
||
|
|
|
||
|
|
Simulates realistic feed and order latencies.
|
||
|
|
Used by CWM to make fill simulation realistic.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(
|
||
|
|
self,
|
||
|
|
feed_latency_ms: float = 10.0,
|
||
|
|
order_latency_ms: float = 50.0,
|
||
|
|
feed_jitter_ms: float = 2.0,
|
||
|
|
order_jitter_ms: float = 10.0,
|
||
|
|
) -> None:
|
||
|
|
self._feed_latency = feed_latency_ms
|
||
|
|
self._order_latency = order_latency_ms
|
||
|
|
self._feed_jitter = feed_jitter_ms
|
||
|
|
self._order_jitter = order_jitter_ms
|
||
|
|
self._rng_seed = 0
|
||
|
|
|
||
|
|
def simulate_feed_latency(self) -> float:
|
||
|
|
"""Simulate current feed latency."""
|
||
|
|
self._rng_seed += 1
|
||
|
|
return simulate_feed_latency(self._feed_latency, self._feed_jitter, self._rng_seed)
|
||
|
|
|
||
|
|
def simulate_order_latency(self, queue_position: int = 0, recent_trade_rate: float = 0.5) -> float:
|
||
|
|
"""Simulate current order latency."""
|
||
|
|
self._rng_seed += 1
|
||
|
|
return simulate_order_latency(self._order_latency, self._order_jitter, queue_position, recent_trade_rate, self._rng_seed)
|
||
|
|
|
||
|
|
def compute_latency_cost(self, price_change_per_ms: float = 0.001) -> float:
|
||
|
|
"""Compute latency cost in basis points."""
|
||
|
|
return compute_latency_impact(self._feed_latency, self._order_latency, price_change_per_ms)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def feed_latency_ms(self) -> float:
|
||
|
|
return self._feed_latency
|
||
|
|
|
||
|
|
@property
|
||
|
|
def order_latency_ms(self) -> float:
|
||
|
|
return self._order_latency
|