25 - Latency Monitor
Overview
OpenAlgo tracks order execution latency at multiple stages to help identify performance bottlenecks and ensure SLA compliance.
Latency records live in their own database, LATENCY_DATABASE_URL (default sqlite:///db/latency.db), separate from both openalgo.db and logs.db.
Architecture Diagram
┌──────────────────────────────────────────────────────────────────────────────┐
│ Latency Monitoring Architecture │
└──────────────────────────────────────────────────────────────────────────────┘
Order Request
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ Latency Tracking Points │
│ │
│ T0: Request Received ───────────────────────────────────────────────────► │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Validation │ ← T1: validation_latency_ms │
│ │ (API key, │ │
│ │ schema) │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Broker API │ ← T2: rtt_ms (Round-Trip Time) │
│ │ Request/ │ │
│ │ Response │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Response │ ← T3: response_latency_ms │
│ │ Processing │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ T4: Response Sent ─────────────────────────────────────────────────────► │
│ │
│ total_latency_ms = T4 - T0 │
│ overhead_ms = validation_ms + response_ms │
└──────────────────────────────────────────────────────────────────────────────┘Metrics Tracked
Latency Components
rtt_ms
Broker API round-trip time
validation_latency_ms
Pre-request validation
response_latency_ms
Post-response processing
overhead_ms
Total OpenAlgo overhead
total_latency_ms
End-to-end time
The column names on OrderLatency are validation_latency_ms and response_latency_ms. The request_body and response_body columns exist but are always written as None: _log_latency_async() passes request_body=None, response_body=None to save space, so no payloads are stored.
Database Schema
Implementation
Latency Tracker Class
utils/latency_monitor.LatencyTracker records named stages rather than fixed marks:
get_rtt() returns the broker round trip, get_overhead() the sum of the non-broker stages, and get_total_time() the end-to-end elapsed time. The record itself is written off the request thread by _log_latency_async() on a single-worker ThreadPoolExecutor, so the SQLite commit never delays the order response.
Decorator Wiring
track_latency(api_type) is not applied by hand to route functions. init_latency_monitoring(app) walks the Flask-RESTX namespaces and wraps every resource method through wrap_resource_methods(), mapping each namespace name to an uppercase api_type.
An unmapped namespace falls back to its uppercased name.
Dashboard
Access
Routes
latency_bp is registered with url_prefix="/latency". All routes require a valid session.
GET /latency/
Older server-side dashboard template, still registered
GET /latency/api/logs
Recent records; limit defaults to 100 and is capped at 1,000
GET /latency/api/stats
Overall stats, per-broker stats, and per-broker RTT histograms
GET /latency/api/broker/<broker>/stats
One broker's stats plus its histogram
GET /latency/export
Export all records as latency_logs.csv
Histograms are computed with 30 numpy bins over the observed RTT range.
Dashboard View
SLA Targets
Performance Thresholds
get_latency_stats() reports two independent things: the share of orders under three hard-coded millisecond thresholds, and four percentiles of total_latency_ms. The thresholds are not configurable and there is no 175 ms bucket.
sla_100ms
Percent of orders under 100 ms end to end
sla_150ms
Percent of orders under 150 ms end to end
sla_200ms
Percent of orders under 200 ms end to end
p50_total, p90_total, p95_total, p99_total
Percentiles of total_latency_ms
SLA Calculation
The whole statistics block is computed in a handful of aggregate queries and cached for 60 seconds (_stats_cache, a TTLCache of size 1). Percentiles are computed only over the last PERCENTILE_WINDOW_DAYS (30) days so the fetch cannot grow without bound.
Broker Comparison
Per-Broker Stats
Retention
init_latency_monitoring(app) calls purge_old_data_logs(days=7) once at startup.
Order-execution records are kept forever. Everything else, meaning data and account queries, is deleted after seven days. The keep-forever set is PLACE, SMART, MODIFY, CANCEL, CLOSE, CANCEL_ALL, BASKET, SPLIT, OPTIONS, OPTIONS_MULTI, GTT_PLACE, GTT_MODIFY, and GTT_CANCEL. The same set is written out twice, in utils/latency_monitor.KEEP_FOREVER_TYPES and in database/latency_db.purge_old_data_logs, and the two must stay in step: a type listed in only the first is purged despite being an order.
Alerting
There is no latency alerting. Nothing in the codebase raises a notification on a slow order or a broker timeout. The monitor records, aggregates, and displays; acting on a threshold is left to the operator watching the dashboard.
HTTP Client Integration
Connection Timing
Analytics Queries
Common Queries
Key Files Reference
utils/latency_monitor.py
Tracking utilities
database/latency_db.py
Latency model
blueprints/latency.py
Dashboard and export routes
utils/httpx_client.py
HTTP timing hooks
frontend/src/pages/monitoring/LatencyDashboard.tsx
React dashboard
Last updated