In Development
Finsec market-data load test across 10,000 WebSocket clients

Distributed Systems

Finsec — Real-Time Trading System

Finsec is designed to reduce the time discretionary traders spend watching charts without flattening their strategy into a generic signal. A trader can mark the structure and execution logic of a setup directly on a chart, save it as reusable strategy snapshots, and search historical data for formations that behave like the original idea.

Those candidates are evaluated through both structural similarity and the meaning of the trader's annotations—candle groups, zones, levels, entries, exits and risk marks. The trader can accept or adjust historical examples, replay the strategy in a backtest, and apply the learned formation back to a current chart as it forms.

Finsec separates the workloads that fail differently: live market distribution, authenticated trade execution, and historical retrieval. A slow provider fetch, large chart history, or burst of orders does not sit directly in every user's request path.

Python produces and stores market data, Redis carries hot state between services, Go owns WebSocket fanout and execution, PostgreSQL stores transactional records, and Parquet holds long-range candle history. The product intelligence and the measured real-time paths are two halves of the same system: one encodes the trader's edge and the other keeps the market and execution loop responsive under load.

Key Features

Chart-native strategy capture

Turns a trader's reference setup into reusable candle, zone, level and execution annotations rather than separating the strategy from the chart where it was identified.

Historical structural and semantic matching

Searches earlier market windows for comparable formations, then validates both their price structure and the annotated relationships that make the setup meaningful.

Live formation projection

Evaluates a developing chart against saved strategy snapshots and projects mapped structure, entry, exit and risk marks as the formation progresses.

Strategy-aware backtesting

Replays historical candles through the same forward strategy logic, position lifecycle and performance analysis used to reason about a current setup.

Live market data and chart history

Clients receive an immediate cached chart, live candle updates, and older 500-candle pages over the same WebSocket connection.

Authenticated trade execution

Long-lived trade sockets support market and limit orders while retaining the user's execution context between messages.

Complete position lifecycle

Positions can be opened, updated, batch-closed and reflected in account-level balance, P&L and performance statistics.

Measured system behaviour

Separate K6 scenarios exercise market-data fanout across 10,000 clients and the authenticated execution path at increasing write rates.

Implementation

Structured strategy snapshots

A saved strategy keeps one or more annotated candle examples, including structural regions and execution roles, so the engine can compare evidence across several expressions of the same idea.

Similarity and semantic validation

Candle windows are normalised for structural comparison, aligned across different lengths and then checked against annotation-specific interactions such as touches, crosses, closes and rejections.

Forward formation engine

A growing live or replayed window is evaluated against strategy references, producing formation progress, confidence, consensus and projected chart annotations.

Backtest session engine

Historical candle sessions share the forward strategy path, persist their cursor and positions, and produce trading, drawdown and equity analysis as the market is replayed.

Go service layer

Owns the long-lived WebSocket connections, bounded fanout workers, session-scoped trade handling, and delivery of execution confirmations.

Python data engine

Produces or retrieves market candles, maintains the data pipeline, and prepares historical chart pages for retrieval.

Storage boundary

Redis carries hot state and pending work, PostgreSQL owns transactional records, and Parquet holds long-range candle history.

Architecture in motion

How it works

Chart observation to reusable strategy

The trader selects a reference formation and annotates what makes it meaningful instead of reducing the setup to a screenshot or paragraph. Candle groups, zones, levels and markers record both market structure and execution intent, then multiple examples can be kept as snapshots of the same strategy.

  1. Reference chart
  2. Candle window
  3. Structural annotations
  4. Execution annotations
  5. Strategy snapshots

Annotations encode structure, entry, exit, stop-loss and take-profit roles across candle groups, zones, levels and markers.

Historical matching and trader validation

Saved snapshots are normalised and compared with earlier market windows. Structural scoring finds similar price movement, while semantic checks ask whether the annotated relationships—touches, crosses, closes and rejections—also hold. Candidates remain visible for the trader to accept, reject or adjust.

  1. Saved snapshots
  2. Historical windows
  3. Structural scoring
  4. Semantic checks
  5. Accepted candidate

Candidate ranking combines structural and semantic evidence; required annotations act as qualification gates rather than cosmetic overlays.

Validated strategy back onto the market

Accepted historical candidates become additional strategy snapshots. A forward pass evaluates each growing candle window against those references, tracks formation progress and confidence, and projects the mapped entry, exit and risk annotations onto the current chart.

  1. New candles
  2. Forward formation scan
  3. Reference consensus
  4. Projected annotations
  5. Live chart state

Developing setups move through forming, confirmed or invalidated states while their mapped annotations remain visible on the chart.

Strategy-aware backtesting

Historical candles are replayed through the same forward strategy path used for a developing market. The session advances candle by candle, applies the active strategy, manages positions and records the resulting trading and equity behaviour.

  1. Historical session
  2. Candle replay
  3. Strategy forward pass
  4. Position lifecycle
  5. Equity analysis

Backtest analysis reports realised and unrealised P&L, return, win rate, best and worst trades, maximum drawdown and an equity curve.

Market-data delivery

One producer creates each ticker update, Redis publishes it once, and Go fans the same payload out through bounded worker groups. Adding clients expands delivery work without multiplying provider calls or rebuilding the candle for every connection.

  1. Provider / simulator
  2. Python broadcaster
  3. Redis Pub/Sub
  4. Go worker pool
  5. WebSocket clients

10,000 concurrent clients · 598,505 messages delivered · 65.21ms p99 WebSocket connection time

K6 result showing 10,000 WebSocket sessions and 598,505 market-data messages receivedOpen full size ↗

Trade execution

The user is authenticated and their account is loaded when the socket opens. That context stays with the connection, so each trade can move directly into the Redis pending queue, a batched PostgreSQL write, and a confirmation routed back to the originating client.

  1. JWT handshake
  2. Account context
  3. WebSocket order
  4. Redis queue
  5. PostgreSQL batch
  6. Targeted confirmation

300,000 trade messages exercised · 1,000 successful batch closes · 83ms write p99 · 78.96ms close p99

K6 result showing the 300,000-message trade run and 1,000 successful trade closesOpen full size ↗

Historical retrieval

The newest chart page is kept hot in Redis and returned first. Older 500-candle pages are prepared from Parquet in the background, while closed trades use a compound cursor so retrieval stays bounded as account history grows.

  1. Parquet history
  2. 500-candle pages
  3. Redis page 1
  4. (closed_at, id) cursor

500 candles per chart page · closed-trade responses bounded to 100 records

Design Choices

Capture discretion as structured evidence

The trader's edge is represented by annotated chart relationships and multiple accepted examples, retaining nuance while making the strategy searchable and reusable.

Match structure and meaning

Price-shape similarity identifies candidates, but semantic annotation rules decide whether the interactions that matter to the strategy are actually present.

Keep the trader in the validation loop

Historical matches can be accepted, rejected or adjusted before they influence future detection, allowing the strategy library to improve without hiding its evidence.

Use one strategy path for replay and live formation

Backtesting and forward chart application share the same strategy evaluation model so the behaviour being tested is the behaviour used as new candles arrive.

Generate once, fan out many

Market data is fetched or generated once per ticker and interval, then shared across every subscribed client instead of repeating upstream work per connection.

Authenticate once, execute many

JWT verification and account lookup happen when the WebSocket is established. The resulting identity is reused so repeat authentication is removed from the per-trade path.

Serve the newest data first

The first 500 candles are returned from Redis before the full history is processed. Remaining pages are prepared in the background and requested only when needed.

Absorb bursts before PostgreSQL

Redis accepts incoming trades quickly while the flusher groups up to 1,000 rows on a 75ms cadence, reducing database round trips under concurrent load.

Keep history bounded as it grows

Closed trades use cursor pagination on closed time and trade ID with a matching partial index, avoiding increasingly expensive offset scans.

Match storage to data temperature

Redis serves hot chart state, PostgreSQL owns transactional records, and Parquet stores long-range candle history without forcing every read through one database.

Measured performance

Fivefold trade-write throughput

Across four K6 runs, Finsec scaled from 1,000 to 5,000 trade writes per second while database-write p99 moved from 77ms to 116ms.

5,000 writes/s

at 89ms database-write p99

Database-write p99

Trade writes per second

77ms

1,000/s

80ms

2,000/s

83ms

3,333/s

89ms

5,000/s

Write throughput

77→89ms

Database-write p99

450,000

Messages in the largest measured run

100%

Received write confirmations successful

Expected performance

Expected performance at deployment scale

Dedicated compute extends the execution path into larger deployment-scale workloads.

16 vCPU execution tier

10,000/s

≈99-102ms p99

Single-instance target

100,000/s

≈150ms p99

Scale-out target

Trade path

  1. WebSocket order
  2. Redis pending queue
  3. Batched PostgreSQL write
  4. Redis confirmation
  5. Originating client
K6 result summary

EC2 load generator → EC2 application instance

Authenticated WebSocket clients: 1,000

ws_msgs_sent: 300,000

position_write_ms: p99=83ms, max=117ms

position_write_ok: 100.00%

position_close_ms: p99=78.96ms

position_close_ok: 100.00%