Design Mobile Stock Trading & Market Ticker App
1. Problem Statement & Scope
System Mission
Design a mission-critical, low-latency mobile stock trading application and real-time market data streaming pipeline supporting 120 FPS live price ticker visualization, server-side market data conflation, hardware-backed biometric cryptographic order signing via Apple Secure Enclave / Android KeyStore, strict idempotent order routing, and zero double-execution guarantees during network transitions and market volatility spikes.
Transactional CP Execution Guarantee: While market price streaming operates on an AP model with monotonic sequence reconciliation, the order routing and ledger pipeline is strictly CP (Consistency over Availability). Account buying power reservations execute pessimistic row locks (SELECT FOR UPDATE) in Amazon Aurora PostgreSQL paired with conditional DynamoDB idempotency locks, guaranteeing zero double-execution under retry storms.
Synthesizing vector architecture diagram...
Functional Requirements
- 120 FPS Real-Time Market Ticker Streaming: Render Level 1 quotes (Bid, Ask, Last Price, Volume) at a buttery 120 FPS ( frame budget) using off-main-thread order book assembly and reactive throttling (
sample(50ms)). - Hardware Biometric Cryptographic Order Signing: Require biometric verification (FaceID / TouchID / Android BiometricPrompt) to sign financial orders with a non-exportable ECDSA P-256 (
secp256r1) private key anchored in hardware security modules. - Idempotent Order Placement Pipeline: Support Market, Limit, and Stop orders with client UUIDv7 idempotency keys, dual-token authorization (OAuth 2.0 Access Token + Biometric hardware signature), sub-second confirmation over persistent WebSocket / gRPC channel.
- Market Data Cache Eviction & Drift Recovery: Periodic reconciliation of streaming ticks against authoritative REST snapshot endpoints to detect and repair quote drift caused by dropped network packets.
- Stale Quote Protection & Kill Switch: Automatically flag market prices as
STALEand disable order placement if market data heartbeat is interrupted for .
Non-Functional Requirements (SLAs/SLOs)
- Order Execution Latency: Client submission to exchange gateway receipt ().
- Zero Double-Execution: Absolute idempotency guarantee (zero duplicate orders created under network retransmission).
- UI Smoothness & Frame Budget: Strict 120 FPS rendering ( frame budget) without dropped frames during extreme market volatility.
- High Availability: uptime during active market trading hours ( downtime allowed per calendar year).
- Security & Compliance: Signing keys generated and used only inside hardware-isolated key stores (Secure Enclave / StrongBox); FIPS 140-3 validated crypto modules server-side; zero plaintext storage of private keys or trading tokens.
- CAP / PACELC Classification: The order execution engine is strictly CP (Consistency over Availability) operating in PC/EC mode. Market data streaming is AP with monotonic sequence reconciliation.
Out-of-Scope
- Complex algorithmic high-frequency options pricing Black-Scholes engines.
- Decentralized crypto blockchain settlement layers.
2. Capacity & Scale Estimation
Traffic & Scale Math
- Active Mobile Traders: 10 Million accounts; 3 Million concurrent traders during the market open and close ( and ).
- Watchlist Symbol Distribution: Average trader monitors 15 active symbols concurrently.
- Raw Exchange Ticker Frequency: 100 ticks/sec per volatile equity symbol.
- Raw Ticks/sec without Conflation: (Would destroy mobile device battery in 15 minutes and melt mobile cellular radios).
- Server-Side Conflation Engine:
- The backend conflates raw ticks into 2 updates/sec per symbol per mobile client ( conflation window).
- Conflated Multi-Cast Stream:
- Conflated tick frame footprint (SSE / Protobuf): .
- Order Placement Throughput:
- Average 2 Million orders per trading day.
- Peak Opening Bell Throughput ():
- Order request payload (JSON + ECDSA signature + headers): .
Cloud Storage Calculations (3-Year Horizon)
- Transactional Ledger Storage (Amazon Aurora PostgreSQL):
2 Million orders/day .
Each trade generates:
- 1 Order row ()
- 1 Execution row ()
- 2 Double-Entry Ledger entries () With Amazon Aurora 6-way synchronous replication across 3 AZs (physical footprint; Aurora bills the logical 1.97 TB):
- DynamoDB Idempotency Mutex Table: 24-hour TTL rolling window: (DynamoDB is disk-backed; this is not a RAM figure).
Memory & Cache Sizing
- ElastiCache Redis Market Ticker Cache: 10,000 active US equities latest quote snapshot (fits in L2/L3 cache). Level 2 Order Book Cache (Top 10 bids/asks): 10,000 symbols .
- Client-Side Ring Buffer Memory: lock-free circular buffer per device; zero memory allocation in the render loop.
Fleet Sizing & Compute Infrastructure
- Amazon Aurora PostgreSQL Cluster:
Provision
db.r6g.16xlarge(64 vCPU, 512 GB RAM) primary instance capable of sustaining with dedicated read replicas. - Stateless Order Ingestion Service (ECS Fargate): Peak 5,000 QPS. Each container task handles 250 orders/sec .
- Conflation & Fanout Service (ECS Fargate): Handles 3 Million concurrent SSE connections. Each container handles 20,000 SSE connections .
- Kinesis Raw Quote Ingest: 10,000 symbols × 100 ticks/s = 1,000,000 raw ticks/s. A shard accepts 1,000 records/s or 1 MB/s; without aggregation that is 1,000 shards. With KPL record aggregation (40-byte ticks packed into ~25 KB records) the byte limit binds instead: shards minimum, provisioned as 64 shards for headroom, partition-keyed by symbol.
3. AWS-First High-Level Architecture
Synthesizing vector architecture diagram...
Follow the two separate paths. Market data (read-only, extremely high volume): exchange feeds enter Kinesis, and in the "Market Conflation & Fanout Tier" panel, 150 workers merge each symbol's many ticks into the latest snapshot, keep it in Redis, and stream it to phones over SSE through CloudFront. Orders (few, but they must be exact): in the "ACID Transactional Order Engine" panel, the order service checks the order's UUIDv7 against a DynamoDB idempotency table, so a retried tap never places two orders, records it in the Aurora double-entry ledger, and queues it in SQS FIFO to the FIX gateway that talks to the exchanges. On the phone, in the "Mobile Client Architecture Deep Dive" panel, orders are signed with a biometric-protected hardware key, and the chart redraws from a ring buffer on every display frame. Keeping the two paths apart means a flood of quotes can never slow down order placement.
Data Flow Architecture
- Market Data Streaming Path: Market exchanges stream raw Level 1 ticks (100 Hz) into Amazon Kinesis. The ECS Conflation Fleet aggregates ticks to 2 updates/sec per symbol and multicasts to mobile clients over HTTP/2 Server-Sent Events (SSE).
- Client 120 FPS Rendering Path: Inbound SSE frames stream into a lock-free circular ring buffer on a background thread.
CADisplayLink(iOS) orChoreographer(Android) samples the buffer at native 120 Hz ( cadence), performing atomic pointer swaps to repaint the Metal/Vulkan GPU canvas without main-thread I/O. - Biometric Order Signing Path: When the trader submits an order, the OS biometric prompt delegates to the hardware Secure Enclave to sign the canonical order hash
SHA256(account + symbol + side + qty + price + idempotency_key + quote_reference_ts)using an unexportable ECDSA P-256 private key. TheIdempotency-Keymust be inside the signed payload: if it were only a header, anyone holding a captured signed order could replay it under a fresh key and the idempotency mutex would happily accept it as a new order. - Idempotent Order Execution Path: Signed orders dispatch with a UUIDv7
Idempotency-Key. The backend acquires a conditional mutex in DynamoDB, locks account balance in Amazon Aurora PostgreSQL viaSELECT FOR UPDATE, writes double-entry ledger entries, and routes the order to Amazon SQS FIFO for FIX execution.
End-to-End Request Tracing Walkthrough
| Step # | Event / Action | Component State | Distributed Transition | Output / Response |
|---|---|---|---|---|
| Step 1 | Trader launches watchlist view | Market stream IDLE | Client establishes HTTP/2 SSE connection via CloudFront to ECS Conflation Fleet | Unidirectional quote stream open () |
| Step 2 | Conflated tick frame received (2 Hz) | Ring buffer enqueue | Background thread receives 40B quote; checks monotonic sequence | Tick enqueued in lock-free circular ring buffer |
| Step 3 | Display refresh sync pulse () | Render cadence trigger | CADisplayLink / Choreographer samples ring buffer; swaps atomic pointer | Metal/Vulkan GPU canvas repaints at 120 FPS ( main thread I/O) |
| Step 4 | Trader swipes order pad to buy equity | Order pad DRAFT | OS prompts FaceID/BiometricPrompt; Secure Enclave signs canonical order hash | Non-exportable ECDSA P-256 signature generated |
| Step 5 | Signed order dispatched to cloud | Pipeline DISPATCHING | HTTPS POST /v1/trading/orders sent with UUIDv7 Idempotency-Key and signature | Request passes AWS WAF token bucket rate limiter |
| Step 6 | DynamoDB idempotency mutex check | Ingestion task active | Order Service executes PutItem with attribute_not_exists(PK); status IN_PROGRESS | Mutex lock acquired (retransmissions return cached 200 payload) |
| Step 7 | ACID buying power reservation | Database transaction | Aurora PostgreSQL executes SELECT buying_power FROM accounts FOR UPDATE | Row-level lock acquired; funds reserved atomically |
| Step 8 | Double-entry ledger commit & SQS route | Ledger committing | Inserts trading_orders, records debit/credit ledger rows; pushes to SQS FIFO | SQS FIFO guarantees order sequencing to FIX Gateway |
| Step 9 | Mutex commit after ledger + enqueue | Order Service finalizing | Aurora commit and SQS FIFO enqueue succeeded; Order Service updates DynamoDB mutex status to COMMITTED with the 201 body | Idempotency record finalized; exchange ACK arrives later, asynchronously |
| Step 10 | Mobile client receives broker acceptance | Order ACCEPTED | Mobile app receives HTTP 201 Created (ACCEPTED = accepted by the broker, not yet filled) | Green confirmation toast; later ROUTED / FILLED transitions arrive as push events from the FIX engine |
4. API Interface Design & Wire Protocols
1. Submit Biometrically Signed Order (POST /v1/trading/orders)
httpPOST /v1/trading/orders HTTP/1.1 Host: trade.production.aws.internal Content-Type: application/json Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... Idempotency-Key: 018e3d2a-7f12-7000-8000-123456789abc X-Biometric-Signature: MEUCIQDN3b6r8vK... (ECDSA P-256 Signature) X-Client-Timestamp: 1773648000120 X-Quote-Reference-Timestamp: 1773647999800 { "account_id": "acc_998124a87", "symbol": "NVDA", "side": "BUY", "order_type": "LIMIT", "limit_price": 125.50, "quantity": 100, "time_in_force": "DAY", "estimated_total_usd": 12550.00 }
Response: 201 Created
json{ "order_id": "ord_20260916_998124", "status": "ACCEPTED", "symbol": "NVDA", "side": "BUY", "quantity": 100, "limit_price": 125.50, "reserved_funds_usd": 12550.00, "created_at": 1773648000180 }
2. Market Ticker Stream (Server-Sent Events)
httpGET /v1/marketdata/tickers/stream?symbols=NVDA,AAPL,MSFT HTTP/1.1 Host: stream.trade.aws.internal Accept: text/event-stream Cache-Control: no-cache
Response Stream: 200 OK
textevent: tick data: {"symbol":"NVDA","bid":125.48,"ask":125.52,"last":125.50,"vol":4829340,"seq":109812,"ts":1773648000500} event: tick data: {"symbol":"AAPL","bid":220.10,"ask":220.15,"last":220.12,"vol":1928340,"seq":88129,"ts":1773648000500}
3. Status Codes & Error Contracts
| HTTP Status | Reason String | Error Contract Payload | Client Handling / Recovery Strategy |
|---|---|---|---|
201 Created | ORDER_ACCEPTED | Standard order confirmation payload | Render green success toast; play subtle haptic feedback |
400 Bad Req | STALE_MARKET_PRICE | {"error": "Quote timestamp skew exceeds 1500ms"} | Invalidate local ticker cache; refresh quote snapshot |
401 Unauth | BIOMETRIC_MISMATCH | {"error": "ECDSA signature validation failed"} | Re-prompt biometric sensor; verify hardware public key |
402 Pay Req | INSUFFICIENT_FUNDS | {"error": "Required $12,550 exceeds buying power $8,200"} | Prompt user to deposit funds; cancel submission |
409 Conflict | ORDER_IN_PROGRESS | {"error": "Order with key is currently committing"} | Wait ; poll order status endpoint |
429 Too Many | ORDER_RATE_LIMIT | {"error": "Exceeded 5 orders/second", "retry_after": 1} | Enforce local UI button cooldown with haptic warning |
503 Service | MARKET_HALTED | {"error": "Trading halted for symbol NVDA by exchange"} | Display yellow regulatory halt badge on order pad |
5. Data Models & Storage Architecture
Local Client Storage: Lock-Free Ring Buffer & SQLite Cache
Configured in mobile memory for zero-allocation 120 FPS chart and order book rendering:
sql-- SQLite Local Ticker Snapshot Table (Drift Recovery & Offline Fallback) CREATE TABLE local_market_snapshots ( symbol TEXT PRIMARY KEY NOT NULL, last_price REAL NOT NULL, bid_price REAL NOT NULL, ask_price REAL NOT NULL, volume INTEGER NOT NULL, sequence_number INTEGER NOT NULL, -- Monotonic sequence counter: incoming > current server_timestamp INTEGER NOT NULL, is_stale INTEGER NOT NULL DEFAULT 0 ); CREATE INDEX idx_snapshots_symbol ON local_market_snapshots(symbol); -- Local Offline Orders Cache (Audit trail) CREATE TABLE local_order_audit ( client_order_id TEXT PRIMARY KEY NOT NULL, -- UUIDv7 symbol TEXT NOT NULL, side TEXT NOT NULL, quantity INTEGER NOT NULL, limit_price REAL, status TEXT NOT NULL, biometric_signature TEXT NOT NULL, submitted_at INTEGER NOT NULL ); -- Hardware Biometric Key Lifecycle & Automated Re-Pairing State CREATE TABLE biometric_credentials ( credential_id TEXT PRIMARY KEY NOT NULL, account_id TEXT NOT NULL, key_alias TEXT NOT NULL, public_key_spki TEXT NOT NULL, enrollment_status TEXT NOT NULL CHECK (enrollment_status IN ('ACTIVE', 'INVALIDATED', 'PENDING_PAIRING')), created_at INTEGER NOT NULL, invalidated_at INTEGER );
Backend PostgreSQL Schema: Double-Entry Transactional Ledger (trading_core_db)
sql-- Trading Orders Table CREATE TABLE trading_orders ( order_id VARCHAR(64) PRIMARY KEY, account_id VARCHAR(64) NOT NULL, idempotency_key VARCHAR(128) UNIQUE NOT NULL, symbol VARCHAR(16) NOT NULL, side VARCHAR(4) CHECK (side IN ('BUY', 'SELL')), order_type VARCHAR(16) CHECK (order_type IN ('MARKET', 'LIMIT', 'STOP')), limit_price NUMERIC(12, 4), quantity INTEGER NOT NULL CHECK (quantity > 0), filled_quantity INTEGER NOT NULL DEFAULT 0, status VARCHAR(32) NOT NULL CHECK (status IN ('PENDING', 'ACCEPTED', 'ROUTED', 'FILLED', 'REJECTED', 'CANCELLED')), created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_orders_account ON trading_orders(account_id, created_at DESC); -- Double-Entry Financial Ledger Table (Strict Audit Invariant: Sum of debits = Sum of credits) CREATE TABLE ledger_entries ( entry_id VARCHAR(64) PRIMARY KEY, account_id VARCHAR(64) NOT NULL, order_id VARCHAR(64) REFERENCES trading_orders(order_id), entry_type VARCHAR(16) CHECK (entry_type IN ('DEBIT', 'CREDIT')), asset_type VARCHAR(16) NOT NULL CHECK (asset_type IN ('CASH', 'EQUITY')), symbol VARCHAR(16), -- ISO currency for CASH ('USD'), ticker for EQUITY ('NVDA'); never encode tickers in the enum amount NUMERIC(18, 4) NOT NULL CHECK (amount > 0), created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_ledger_account ON ledger_entries(account_id, created_at DESC);
Backend DynamoDB Idempotency & Mutex Table (ProductionTradingIdempotency)
| Partition Key (PK) | Sort Key (SK) | Attributes | TTL Attribute |
|---|---|---|---|
IDEMPOTENCY#<uuidv7> | MUTEX | status (IN_PROGRESS / COMMITTED), response_payload, account_id | ttl_timestamp (24h) |
Unlock Complete Architecture & Production Runbooks
You have explored the free architectural preview (~43%). Spend 1 Coin to unlock the remaining 6 production deep-dive sections for a full 24 hours.