Zerodha System Design Masterclass
Discover the robust, frugal, and highly efficient distributed architecture that powers India's largest retail brokerage. Learn how 10+ million clients place 15+ million orders daily with sub-50ms latency using Go, customized WebSockets, event streams, and sharded storage systems.
The Core Challenge & Scalability Targets
At first glance, a stock trading application looks like standard e-commerce: users browse products (stocks), click buy or sell, and complete transactions. However, the financial markets present uniquely stringent engineering constraints: perfect accuracy, atomic transactions, microsecond latency, and absolute resilience to sudden volatility spikes (e.g., during market openings, budget announcements, or global crashes).
Functional Architecture Constraints
- • Atomic Order Routing: Prevent order doubling at all costs. An order must execute exactly once, or fail explicitly, with no "hanging" state.
- • Strict Risk Control: Perform margin calculations and compliance checks within 10 milliseconds before allowing an order to touch the exchange.
- • Real-time Broadcaster: Stream millisecond tick updates to millions of active screens concurrently, minimizing bandwidth and battery drain.
- • Exchange Compliance: Bridge modern stateless microservices with rigid legacy exchange architectures using specialized leased lines (FIX/FAST).
Scalability & Performance Metrics
High-Level System Architecture
Zerodha's architecture utilizes a **frugal, highly optimized multi-tier architecture** with Go as the principal backend language. Go provides extreme network performance, lightweight execution threads (goroutines), and robust memory management, which are critical for scaling high-frequency real-time connections with extremely small engineering teams.
Kite Connect Gateway
Built completely in Go, the API Gateway acts as the gateway for all HTTP/REST requests. It handles stateless routing, API rate-limiting per client token, session validation via high-speed Redis Lookups, and payload serialization under 3 milliseconds.
Kite Ticker (WebSockets)
Kite Ticker streams live market updates to clients. Using high-performance goroutines, it handles millions of concurrent TCP WebSocket sockets, dynamically subscribing/unsubscribing clients to binary tick streams and routing feed packets with zero latency overhead.
Exchange Connector (OMS)
Connects the internal order routing engines directly to exchange front-ends (NSE/BSE/MCX) over dedicated multiplexed leased lines. It supports raw protocol encoding (FIX/FAST) and manages network connections under absolute strict high availability.
Live Market Feed (Ticks) Processing
During active market hours, the exchange streams billions of tick changes. Broadcasting these raw rates directly to millions of screens is impossible due to bandwidth bottlenecks. Zerodha optimizes this stream using a **highly specialized Event Fanout pattern**. Instead of clients polling data, ticks are aggregated, queued in Kafka partitions, and distributed via Go WebSocket broadcast systems using dynamic binary payload packing.
How tick distribution scales efficiently:
If 1,000,000 users are concurrently viewing the NIFTY 50 index, the system does not execute 1,000,000 database reads. Instead:
- The Exchange broadcasts the UDP tick packets.
- Colocated feed handler parsers read and normalize the raw binary data into structural JSON/Protobuf formats.
- Parsed ticks are published to corresponding Kafka partitions mapped by stock index IDs.
- Stateless Go WebSocket servers consume ticks from Kafka.
- Using optimized Goroutine-based Channels, each pod reads a single tick once and duplicates (fans out) the binary byte packets to all users currently subscribed to that symbol's active connection.
High-Speed Order Placement Pipeline
Executing financial transactions requires microsecond precision. When a user taps 'Buy', the request is routed through a split-second pipeline that guarantees absolute security, verification, and speed. The architecture splits processing into a **synchronous critical path** (for checks) and an **asynchronous persistent path** (for ledger updates).
1. Synchronous Margin Check (< 10ms)
Before routing the order to the exchange, the Risk Management System (RMS) validates the user's available cash margin, maximum open position limit, Daily loss circuit limits, and compliance restrictions. This data is read from high-speed distributed Redis memory, executing under 8ms to prevent blocking trading activities.
2. Asynchronous Execution Buffer
Once RMS validates the order, it's pushed to a highly partitioned Kafka Order queue. This buffers the exchange from massive downstream traffic loads. The Exchange Connector pulls sequentially from the queue, translating it into the specialized FAST/FIX socket protocol to lease-lines, and publishes updates back asynchronously.
Kubernetes Architecture & Preemptive Auto-Scaling
Retail trading experiences massive volatility surges. When the market opens at 9:15 AM, traffic ramps up by 15x within minutes. Standard reactive CPU-based auto-scaling fails because starting containers takes too long. Zerodha solves this via **preemptive metrics-driven auto-scaling** orchestrated inside Kubernetes.
Custom Scaling Metrics
Zerodha's scaling rules bypass simple CPU/memory targets. Instead, the custom Prometheus metrics adapter streams active WebSocket connection requests, API request queues, and downstream Kafka consumer lag directly to Kubernetes Custom Resource Definitions (CRDs) to launch container instances preemptively *before* resources exhaust.
Graceful Circuit Breaking
If downstreams fail, the API Gateway activates dynamic **rate-limit throttles and circuit breakers**. Highly granular configurations isolate slow processes (e.g. historical charting databases), ensuring that core transactions—order executions—continue with absolute priority.
Polyglot Storage & Database Partitioning
No single database can serve real-time high-speed transactions alongside massive analytical historical data queries. Zerodha employs **Polyglot Persistence**—allocating specific storage technologies to match highly specialized performance workloads.
PostgreSQL (User Sharded)
To prevent single database locks, user accounts are sharded across separate PostgreSQL physical instances. All ACID-critical operations, such as order books, active trades, and wallet transactions, execute localized queries within their corresponding isolated database.
Redis In-Memory Engine
Acts as an ultra-fast cache storing active session variables, security tokens, and dynamic system state indicators. Real-time instrument lists containing thousands of stock attributes are cached directly in Redis to avoid complex database joins.
Columnar Analytical Store
Historical charts and backtesting metrics are served from columnar-oriented time-series databases like ClickHouse. ClickHouse stores millions of historical ticks in a compressed vector format, processing complex analytical aggregation queries in fractions of a millisecond.
Risk Management Engine (RMS)
The Risk Management System (RMS) is the financial firewall of Zerodha. If a client attempts to buy leverage options without matching capital, the broker is exposed to regulatory penalties and catastrophic losses. Every single order is validated by a custom high-performance RMS engine before it leaves the internal network.
Example Scenario: Option Volatility Surge
Suppose an retail client with a total account cash balance of ₹10,000 tries to place an order to buy call options value ₹10,00,000 during extreme market volatility. If the system allows this to execute, any bad price movement exposes Zerodha to massive financial debt. The RMS interceptor captures this immediately: it calculates the required span and exposure margins from in-memory cached files in Redis, flags insufficient funds, and immediately short-circuits and auto-rejects the order at the backend system within < 8 milliseconds, before the request ever leaves the internal network.
Fault Tolerance & Disaster Recovery
In financial platforms, database corruption or data loss is unacceptable. Zerodha ensures high availability through an **Active-Active warm-failover multi-region architecture**, combined with transactional replication and immediate self-healing capabilities.
Cross-Region Replication
Transactional database masters asynchronously replicate data across physical geographical regions (e.g. primary region in Mumbai to disaster recovery site in Bangalore). All analytical time-series logs are continuously backed up in secondary cloud storage buckets.
Active Session Failover
User sessions and authorization tokens cached in Redis are continuously mirrored to a fallback cluster. If an active region faces compute failure, the DNS routing switches to the recovery region instantly, preserving logged-in sessions without forcing re-authorization.
Partition Isolation
Since PostgreSQL and Kafka storage topics are strictly partitioned/sharded by Client IDs, a database corrupt or partition crash on one cluster shard never compromises the entire user base—it strictly isolates failure to a tiny subset of clients.
Monitoring & Metrics Observability
During extreme volatility, keeping track of latency drops across thousands of microservices is essential. Zerodha tracks the health of its entire infrastructure using an **OpenTelemetry-instrumented metrics pipeline** aggregated on global Grafana dashboards.
Critical Health Indicators
- API Latency Percentiles: Tracking p99 and p99.9 latency of all core HTTP endpoints.
- Kafka Consumer Lag: Monitoring event brokers to identify slow transactional subscribers.
- Connection Count: Tracking WebSocket connection limits across active fanout clusters.
- Exchange Line Latency: Monitoring round-trip network times through colocated servers.
Monitoring Technology Stack
System Design Interview Cheat Sheet
Use this cheat sheet to confidently tackle high-frequency trading platform or fintech system design interview questions.
Q1. How does the system scale to support millions of real-time WebSocket connections?
Answer: Establish stateless WebSocket Broadcast servers (Ticker Pods) behind an HAProxy Layer 4 Load Balancer. The Load Balancer distributes incoming TCP connections evenly. The Ticker pods do not fetch database records; instead, they subscribe to specialized Kafka partitions for tick updates. Using Go's highly optimized runtime scheduler, goroutines fanout incoming tick events directly into corresponding active TCP client connection buffers.
Q2. Why is Kafka selected instead of standard pub/sub systems like RabbitMQ for tick feeds?
Answer: Kafka acts as an immutable, partitioned, append-only log, enabling exceptionally high write-throughput (millions of ticks/sec). It allows multiple independent microservice consumers (real-time websocket broadcasters, historical DB loggers, compliance engines) to consume from the same topic partitions at their own speed (backpressure protection) without degrading database write performance.
Q3. How is the system safeguarded against double-order processing?
Answer: Enforce strict **Idempotency Keys** on the client applications. Every tap of the Buy/Sell button generates a unique Client Request ID (e.g., UUID-timestamp-hash). When the API Gateway receives the request, it checks for this key in a distributed Redis database with a set TTL (Time-To-Live, e.g., 60 seconds). If a duplicate request with the identical key is intercepted, the system immediately returns the pending or completed state, preventing duplicate order placements.
Q4. How does the database design ensure high transactional write throughput under volatile trading hours?
Answer: Employ physical **Database Sharding** partitioned by Client Account IDs. Instead of single PostgreSQL clusters choking on lock queues, users AB1234 and XY5678 query completely separate physical PostgreSQL instances. Caching active holdings, user margins, and security tokens in Redis reduces PostgreSQL read-load to zero, dedicating full physical capacity to transactional order placement.
Global End-to-End System Flow
Below is the ultimate, unified architecture topology representing the complete trading ecosystem working in synergy. It traces how **real-time feed ticks** and **transactional order requests** flow concurrently through our microservices cluster, sharded databases, and direct exchange leased lines.
Lifecycle of a Trade: Step-by-Step System Journey
Continuous Feeds & Dynamic Ticks (Kite Ticker Layer)
The exchange matching engines continuously stream multicast UDP binary packets containing millisecond-level tick updates. Our co-located **Feed Handler** Go scripts intercept this feed, parse raw formats, and publish ticks straight into highly ordered partitions of a **Kafka Ticks Topic**. The stateless **Kite Ticker** pods consume from Kafka, routing compressed data packages to your active screen via established, high-capacity WebSockets connection.
User Transaction Action & Secure Gate Ingress
When you tap the buy/sell button on the Kite interface, a secure HTTP POST request containing a unique client-side **Idempotency Key** is routed to our **Ingress Load Balancers**. HAProxy filters traffic, decrypts SSL, and forwards it to the **API Gateway**. The gateway performs session lookups, verifies JWT authorization signatures, and checks transaction rate-limiting counts under 3ms.
Synchronous Risk Shield Interception (RMS Phase)
The vetted order lands in the **Order microservice**, which routes it to the **Risk Management System (RMS)**. To satisfy strict financial compliance, the RMS validates account metrics: available cash balances, daily circuit loss controls, margin exposure formulas, and blocklists. It reads this data from an in-memory replicated **Redis Margin Cache**, concluding synchronous validation in **< 8ms**.
Decoupled Queue Serialization (Kafka Buffer)
Once the RMS approves the transaction, it is instantly serialized and pushed into a highly partitioned, partition-keyed **Kafka Order Queue**. Partition-keying ensures that all transactions originating from a single Client Account ID land sequentially in the same queue broker, preserving strict causal execution order and preventing race conditions or duplications.
Protocol Translation & Exchange Leased Line Delivery
The dedicated **Exchange Connector** service consumes events from the Kafka order partition. It encodes the structural microservice data package into the rigid **FIX/FAST protocol** natively required by the exchange matching hardware. This encoded message is pushed directly over multiplexed, high-capacity, low-latency physical leased lines to the NSE/BSE co-located routers.
Atomic Execution & Real-Time Push Feedback
The exchange matching core completes the trade execution and immediately returns an execution report back over the leased line. The Exchange Connector parses the message, publishes an update back into Kafka, and prompts the **Kite Ticker** WebSocket cluster to push an instant "Order Completed" visual notification to your screen in **< 50ms total round-trip**.
Double-Entry Asynchronous Settlement & Historical Analytics
In the background, async consumer processes consume the execution logs. They write a double-entry accounting record to your isolated **PostgreSQL DB Account Shard** to settle ledger wallets, update open portfolio positions, and adjust holdings. Simultaneously, raw trade details are published to **ClickHouse** to index historical charting data points and populate post-trade tax analytics.