mech.app

The mech.app newsletter

Agentic AI, minus the noise.

Get practical field notes on AI agents, automation, developer tools and security delivered to your inbox.

No spam. Unsubscribe anytime.

Financial

UQ-LOB: How Uncertainty Quantification Turns Limit Order Book Forecasters Into Risk-Aware Trading Agents

Wire calibrated confidence into order book prediction. UQ-LOB adds uncertainty quantification to deep LOB forecasters so agents know when not to trade.

Source: arxiv.org
UQ-LOB: How Uncertainty Quantification Turns Limit Order Book Forecasters Into Risk-Aware Trading Agents

Most deep learning models for limit order book (LOB) forecasting are point predictors. They output a direction (up, down, stationary) or a displacement value, but they never tell you which predictions can be trusted. In production trading infrastructure, that gap between prediction and execution is where agents blow up accounts.

UQ-LOB is a lightweight uncertainty quantification module that attaches to any pretrained LOB encoder. It conditions each forecast on a context set of recently completed windows whose outcomes are already known. The result is a calibrated confidence signal that supports selective prediction: the agent knows when to sit out.

This is not about improving raw accuracy. It is about exposing which forecasts are reliable enough to act on, and which should trigger a no-trade decision.

The Plumbing Gap in LOB Forecasting

Traditional deep LOB forecasters take a snapshot of the order book (bid/ask levels, volumes, timestamps) and predict the mid-price movement over the next 5, 10, or 15 seconds. Common architectures include:

  • Convolutional encoders that treat the order book as a 2D image
  • Recurrent networks that process sequential snapshots
  • Transformer encoders that attend over time and price levels

These models output a single prediction per window. No confidence interval. No calibrated probability. No signal-to-noise ratio. The execution layer has to treat every prediction as equally trustworthy, which leads to:

  • Trades executed during high-volatility regimes where the model is guessing
  • Position sizing that ignores forecast uncertainty
  • No mechanism to pause trading when the model is out of distribution

UQ-LOB solves this by adding a lightweight module that wraps any pretrained encoder. The module does not retrain the base model. It learns to predict uncertainty by conditioning on a context set of recent windows.

Architecture: Attentive Neural Processes for Order Books

UQ-LOB borrows from attentive neural processes. The core idea is to treat each forecast as a function approximation problem where the context is a set of recently completed windows.

Flow:

  1. Context Set Construction: For each prediction window, collect the N most recent windows whose outcomes are already realized. Each context window includes the LOB snapshot and the actual mid-price displacement that occurred.

  2. Encoder Pass: Run the pretrained LOB encoder on the current window to get a latent representation z_current.

  3. Context Encoding: Run the same encoder on each context window to get z_context_i. Pair each context latent with its realized outcome y_i.

  4. Attention Mechanism: Use cross-attention to weight the context set based on similarity to the current window. This produces a context-aware representation c.

  5. Uncertainty Prediction: A small MLP takes [z_current, c] and outputs:

    • UQ-regression: Mean and variance of a Gaussian over the displacement
    • UQ-classification: Categorical distribution over down/up/stationary, plus a scalar confidence (max class probability)

The module is trained on historical data where ground truth outcomes are known. The loss function penalizes both prediction error and miscalibration (e.g., negative log-likelihood for the Gaussian, or cross-entropy for the categorical).

Key Design Choices:

  • Encoder-agnostic: The module works with any pretrained LOB encoder. You do not need to retrain the base model.
  • Lightweight: The attention mechanism and MLP add minimal latency. The paper reports inference times compatible with real-time trading.
  • Calibrated: The Gaussian variance or class probability is trained to match empirical coverage. If the model says 68% confidence, the true outcome falls within the interval 68% of the time.

Wiring Uncertainty Into Execution Logic

The confidence signal can be wired into execution in three ways:

Integration PointMechanismTrade-off
Threshold FilterOnly execute trades when confidence > threshold (e.g., top 10% of predictions)Simple to implement, but fixed threshold may not adapt to regime changes
Position SizingScale position size by confidence (high confidence = larger position)Smooth integration, but requires careful tuning to avoid over-leveraging
Kill SwitchHalt all trading when average confidence drops below a floorProtects against model drift, but may miss profitable periods if threshold is too conservative

The paper evaluates the threshold filter approach. By restricting to the most confident 10% of predictions, directional macro F1 improves by 0.11 to 0.15 for UQ-regression and 0.05 to 0.11 for UQ-classification across all horizons.

For large, economically meaningful moves (defined as displacements above a certain threshold), the tightest confidence tier reaches a directional F1 of 0.88 (down) and 0.83 (up) at the 5-second horizon. This is the regime where selective prediction has the highest payoff: you avoid small, noisy moves and only trade when the model is highly confident about a large move.

Validation: Coverage and Selective Prediction

The paper tests UQ-LOB on 5.2 billion LOB events across seven cryptocurrency assets (BTC, ETH, LTC, XRP, BCH, EOS, TRX) at horizons of 5, 10, and 15 seconds.

Coverage Calibration:

For UQ-regression, the model outputs a Gaussian with mean μ and variance σ². If the model is well-calibrated, 68% of true outcomes should fall within [μ - σ, μ + σ]. The paper reports near-nominal 68% interval coverage across all assets and horizons.

This is not trivial. Many uncertainty quantification methods produce overconfident or underconfident intervals. UQ-LOB achieves calibration by training on a large, diverse dataset and using a loss function that penalizes miscalibration.

Selective Prediction:

The paper bins predictions into confidence tiers (top 10%, 10-20%, etc.) and measures directional F1 within each tier. The top 10% tier consistently outperforms the full dataset by 0.11 to 0.15 points.

This validates the core hypothesis: the confidence signal is informative. High-confidence predictions are more accurate, and low-confidence predictions are less accurate. An agent that only trades on high-confidence predictions will have better risk-adjusted returns.

Latency and Deployment Shape

Real-time trading requires low-latency inference. The paper does not report exact latency numbers, but the architecture is designed for speed:

  • Pretrained Encoder: The base LOB encoder is already optimized for inference. UQ-LOB does not add a second forward pass through a large model.
  • Lightweight Module: The attention mechanism and MLP are small (a few layers, a few hundred parameters). Inference time is dominated by the base encoder, not the UQ module.
  • Context Set Size: The paper uses N = 10 to 20 context windows. Each context window requires a forward pass through the encoder, but these can be batched or cached if the encoder is stationary.

Deployment Options:

  • Edge Inference: Run the model on the trading server, close to the exchange API. This minimizes network latency but requires GPU or optimized CPU inference.
  • Cloud Inference: Run the model in a cloud region near the exchange. This allows for more compute but adds network round-trip time.
  • Hybrid: Run the base encoder on the edge, send latents to a cloud service for uncertainty quantification. This splits the workload but adds serialization overhead.

For cryptocurrency markets, where tick-to-trade latency is measured in milliseconds, edge inference is the default. For traditional equities, where latency requirements are looser, cloud inference may be acceptable.

Failure Modes and Observability

Model Drift:

The pretrained encoder and UQ module are trained on historical data. If market microstructure changes (e.g., new market makers, regulatory changes, liquidity shocks), the model may become miscalibrated. The confidence signal will still be produced, but it may no longer match empirical coverage.

Mitigation: Monitor calibration metrics in production. If 68% interval coverage drops to 50%, the model is overconfident. If it rises to 80%, the model is underconfident. Trigger a retraining pipeline when calibration drifts beyond a threshold.

Context Set Staleness:

The context set is built from recently completed windows. If the market regime shifts rapidly, the context set may not be representative. The model will condition on outdated information and produce poorly calibrated predictions.

Mitigation: Use a sliding window for the context set. Drop old context windows as new ones arrive. Monitor the age of the oldest context window. If it exceeds a threshold (e.g., 5 minutes), flag the prediction as low-confidence.

Adversarial Order Flow:

High-frequency traders and market makers may place and cancel orders to manipulate the order book. If the model is trained on historical data that includes adversarial behavior, it may learn to predict manipulated moves. The confidence signal will not help if the model is systematically wrong.

Mitigation: Filter training data to remove windows with abnormal order flow (e.g., high cancel-to-fill ratios, spoofing patterns). Use adversarial validation to test the model on held-out periods with known manipulation.

Observability Stack:

  • Prediction Logs: Log every prediction (timestamp, asset, horizon, mean, variance, confidence, realized outcome). Store in a time-series database (InfluxDB, TimescaleDB).
  • Calibration Dashboard: Plot empirical coverage vs. predicted coverage for each confidence tier. Update every hour.
  • Trade Attribution: For each executed trade, log the prediction that triggered it. Measure P&L by confidence tier.
  • Drift Alerts: Trigger alerts when calibration drifts, context set age exceeds threshold, or average confidence drops below floor.

Code Sketch: Wiring Uncertainty Into a Trading Agent

import numpy as np
from typing import Tuple, Optional

class UQLOBAgent:
    def __init__(self, encoder, uq_module, confidence_threshold=0.9):
        self.encoder = encoder
        self.uq_module = uq_module
        self.confidence_threshold = confidence_threshold
        self.context_buffer = []
        
    def update_context(self, lob_snapshot, realized_outcome):
        """Add a completed window to the context buffer."""
        self.context_buffer.append((lob_snapshot, realized_outcome))
        if len(self.context_buffer) > 20:
            self.context_buffer.pop(0)
    
    def predict(self, lob_snapshot) -> Tuple[Optional[str], float]:
        """
        Returns (direction, confidence) or (None, 0.0) if below threshold.
        """
        # Encode current window
        z_current = self.encoder(lob_snapshot)
        
        # Encode context set
        if len(self.context_buffer) < 5:
            return None, 0.0  # Not enough context
        
        context_latents = [
            self.encoder(ctx[0]) for ctx in self.context_buffer
        ]
        context_outcomes = [ctx[1] for ctx in self.context_buffer]
        
        # Run UQ module
        mean, variance, confidence = self.uq_module(
            z_current, context_latents, context_outcomes
        )
        
        # Threshold filter
        if confidence < self.confidence_threshold:
            return None, confidence
        
        # Map mean to direction
        if mean > 0.001:
            direction = "up"
        elif mean < -0.001:
            direction = "down"
        else:
            direction = "stationary"
        
        return direction, confidence
    
    def execute_trade(self, direction, confidence, position_sizer):
        """
        Execute trade with position size scaled by confidence.
        """
        if direction is None:
            return  # No trade
        
        size = position_sizer.compute(direction, confidence)
        # Send order to exchange
        print(f"Trade: {direction}, size={size}, confidence={confidence:.2f}")

This sketch shows the core loop. The agent maintains a context buffer of recently completed windows. For each new prediction, it encodes the current window and the context set, runs the UQ module, and applies a confidence threshold. If the prediction passes the threshold, it executes a trade with position size scaled by confidence.

Technical Verdict

Use UQ-LOB when:

  • You have a pretrained LOB forecaster that produces point predictions and you need to add risk awareness without retraining the base model.
  • Your trading strategy can tolerate selective prediction (i.e., sitting out low-confidence periods is acceptable).
  • You have access to a large historical dataset with realized outcomes to train the UQ module.
  • Your execution layer can consume confidence signals and wire them into threshold filters, position sizing, or kill switches.

Avoid UQ-LOB when:

  • Your trading strategy requires high-frequency execution on every tick. Selective prediction will reduce trade volume, which may hurt strategies that rely on volume (e.g., market making).
  • Your market has low liquidity or high latency. The context set requires recently completed windows, which may not be available in illiquid markets.
  • You do not have infrastructure to monitor calibration in production. Miscalibrated confidence signals are worse than no confidence signals.
  • Your base encoder is already poorly calibrated or out of distribution. UQ-LOB will not fix a broken base model.

The paper demonstrates that uncertainty quantification is not just a research curiosity. It is a production-ready layer that bridges the gap between prediction and execution. The key insight is that knowing when not to trade is as valuable as knowing when to trade.

Tags

agentic-ai orchestration infrastructure

Primary Source

arxiv.org ↗