A real-time market engineering terminal for exploring streaming architecture, browser concurrency, resilience, and rendering performance.
MarketStream is not intended to be a trading product. It is an engineering project built to experiment with how a modern frontend can process, measure, and render high-frequency market data while keeping the UI responsive.
Production
https://seibashonia.dev/work/market-stream
MarketStream is deployed as an independently built Next.js application and composed into the main portfolio using a route-based Multi-Zone architecture.
MarketStream focuses on several frontend engineering problems that commonly appear in real-time systems:
- WebSocket connection lifecycle and automatic reconnection
- High-frequency event processing
- RxJS buffering and stream composition
- Main Thread vs Web Worker processing
- Normalized application state with Redux Toolkit
- Real-time OHLC chart generation
- Incremental chart updates
- Large-list virtualization
- Controlled fault injection
- Performance benchmarking
- Unit, integration, and browser E2E testing
- Independent deployment through a Multi-Zone architecture
MarketStream consumes live ticker data through a WebSocket transport.
Coinbase Advanced Trade
↓
Cloudflare Edge Relay
↓
Browser WebSocket
↓
MarketStream
The Cloudflare relay exists as a transport boundary between the browser and the upstream market provider.
The frontend remains responsible for:
- connection lifecycle
- subscriptions
- reconnection
- message parsing
- stream processing
- application state
- rendering
The terminal currently tracks:
- BTC-USD
- ETH-USD
- SOL-USD
Each market displays:
- latest price
- 24-hour change
- selected market state
Ticker state is stored using Redux Toolkit's normalized entity state.
Incoming ticker events are converted into one-second OHLC candles:
Open
High
Low
Close
The chart keeps a bounded rolling history instead of allowing data to grow indefinitely.
Real-time updates use incremental series updates rather than replacing the complete dataset on every event.
This keeps chart rendering work bounded as new market data arrives.
High-frequency simulation can produce thousands of events per second.
Rendering every event directly into the DOM would create unnecessary React and browser work.
MarketStream therefore separates:
Raw events
↓
RxJS batches
↓
Sampled activity
↓
Virtualized list
The application can retain thousands of activity records while only rendering the rows currently visible in the viewport.
The UI exposes both values:
5,000 stored
~20 rendered
This makes the effect of list virtualization directly observable.
The main runtime pipeline is:
Market Source
↓
WebSocket / Simulator
↓
RxJS
↓
Buffered ticker batches
↓
┌─────────────────────────────┐
│ │
▼ ▼
Visualization Engine Analytics Processor
OHLC + activity Main Thread / Worker
│ │
└──────────────┬──────────────┘
↓
Redux Toolkit
↓
React interface
React does not process the raw event firehose directly.
Incoming events are normalized and buffered before application state is updated.
A high-frequency source may produce updates much faster than the UI should render.
Instead of:
10,000 events/sec
↓
10,000 React updates/sec
MarketStream uses RxJS to buffer events into controlled batches:
10,000 events/sec
↓
RxJS
↓
~10 processing batches/sec
↓
Redux
↓
React
This reduces unnecessary application-level commits while preserving the underlying market workload.
The terminal includes a deterministic synthetic market source.
Supported rates include:
100 events/sec
1,000 events/sec
5,000 events/sec
10,000 events/sec
Simulation mode serves two purposes.
It makes it possible to evaluate the UI under controlled workloads.
E2E tests do not need to depend on Coinbase or the Cloudflare relay.
Simulator
↓
RxJS
↓
Processor
↓
Redux
↓
React
↓
Playwright
This keeps browser tests repeatable even when external services are unavailable.
Analytics can run using either:
Main Thread
or:
Web Worker
Both modes use the same analytics algorithm.
Only the execution location changes.
This allows MarketStream to compare the trade-off between:
- raw processing latency
- worker messaging overhead
- UI responsiveness
- frame stability
- long tasks
A Web Worker is not assumed to be automatically faster.
Its purpose is to move CPU-heavy work away from the UI thread when that trade-off is beneficial.
Recent market samples are used to calculate:
- sample count
- mean price
- SMA 20
- SMA 50
- SMA 200
- minimum price
- maximum price
- volatility
The analytics engine maintains rolling state across incoming batches.
Switching processing modes or workloads resets benchmark state so measurements do not incorrectly reuse previous scenarios.
MarketStream includes a repeatable benchmark suite.
It automatically compares:
1,000 events/sec
├── Main Thread
└── Web Worker
5,000 events/sec
├── Main Thread
└── Web Worker
10,000 events/sec
├── Main Thread
└── Web Worker
Each scenario follows the same lifecycle:
Configure workload
↓
Warm up
↓
Measure
↓
Store result
↓
Next scenario
Metrics include:
- actual input events/sec
- UI commits/sec
- average processing time
- p95 processing time
- average processor round-trip
- p95 processor round-trip
- average FPS
- minimum FPS
- maximum frame gap
- long tasks
The benchmark intentionally measures both compute time and caller-visible round-trip time.
For Web Workers, round-trip time includes messaging and scheduling overhead that would be hidden by measuring worker computation alone.
MarketStream includes controlled fault injection so failure behavior can be tested without changing application code.
Temporarily stops market ingestion.
Useful for verifying that the UI remains stable when input stops.
Forces the active WebSocket to close unexpectedly.
The normal reconnection strategy should recover automatically.
Pushes malformed external data through the parser boundary.
Expected behavior:
Malformed message
↓
Parser rejects it
↓
No invalid Redux update
↓
Application keeps running
Tears down the active transport and starts a clean WebSocket connection lifecycle.
The connection manager distinguishes between application events and infrastructure cleanup.
Normal startup:
idle
↓
connecting
↓
connected
Unexpected failure:
connected
↓
error
↓
reconnecting
↓
connected
Manual disconnect:
connected
↓
disconnected
React lifecycle cleanup can tear down the transport silently without incorrectly publishing a user-level disconnected state.
Stale socket events are also ignored so an older connection cannot overwrite the state of a newer one.
Redux Toolkit stores application-level snapshots and configuration.
Current state domains include:
market
connection
benchmark
reliability
terminal
Responsibilities remain separated:
- normalized tickers
- analytics
- candles
- activity feed
- WebSocket status
- message metrics
- reconnect metrics
- data source
- simulation rate
- processing mode
- processor measurements
- stream pause state
- fault-injection commands
- UI state such as selected market
High-frequency mutable transport behavior remains outside Redux.
Redux receives bounded application state rather than every raw network event.
MarketStream is an independent Next.js application.
The main portfolio remains responsible for:
seibashonia.dev/
seibashonia.dev/work
MarketStream owns:
seibashonia.dev/work/market-stream
The applications use Next.js Multi-Zones with external rewrites.
seibashonia.dev
│
Portfolio App
│
rewrite
│
▼
/work/market-stream
│
▼
MarketStream App
MarketStream has its own:
- repository
- dependencies
- application state
- CI pipeline
- Vercel project
- deployment lifecycle
A unique asset prefix prevents Next.js bundles from different zones from colliding.
Portfolio assets
/_next/...
MarketStream assets
/market-stream-static/_next/...
Cross-zone navigation uses normal browser navigation rather than assuming a shared Next.js client router.
- Next.js
- React
- TypeScript
- Tailwind CSS
- WebSocket API
- RxJS
- Web Workers
- Redux Toolkit
- React Redux
- Lightweight Charts
- TanStack Virtual
- Vitest
- React Testing Library
- Playwright
- Cloudflare Workers
- Vercel
- GitHub Actions
- Next.js Multi-Zones
The project intentionally tests different layers separately.
Cover isolated behavior including:
- Coinbase message parsing
- analytics calculations
- Redux reducers
- visualization processing
- WebSocket lifecycle
Cover interactions between components such as:
- RxJS buffering
- batched ticker processing
Playwright runs the application in a real Chromium browser.
Current E2E scenarios include:
- simulated market processing
- switching to Web Worker processing
- pause and resume behavior
The E2E suite uses Simulation Mode so it does not require external market infrastructure.
app/
work/
market-stream/
components/
runtime/
terminal/
features/
benchmark/
connection/
market/
reliability/
terminal/
hooks/
lib/
benchmark/
config/
market-data/
market-processing/
runtime/
store/
websocket/
test/
unit/
integration/
e2e/
The project is organized by application responsibility rather than placing all business logic inside React components.
This repository includes an .nvmrc.
Use the configured Node.js version:
nvm useInstall dependencies:
npm installCopy the example environment file:
cp .env.example .env.localSet the WebSocket relay:
NEXT_PUBLIC_MARKET_WS_URL=wss://your-market-stream-relay.exampleNEXT_PUBLIC_MARKET_WS_URL is intentionally public because the browser connects to the WebSocket endpoint directly.
Do not place credentials or private API keys in NEXT_PUBLIC_* variables.
npm run devOpen:
http://localhost:3000/work/market-stream
When testing the full Multi-Zone setup:
Run on port 3001:
npm run dev -- --port 3001The Portfolio application's .env.local should contain:
MARKET_STREAM_ORIGIN=http://localhost:3001Start the Portfolio application on port 3000.
Then open:
http://localhost:3000/work/market-stream
The Portfolio application proxies the route to the MarketStream zone.
npm run devnpm run buildnpm run lintnpm run fixnpm run format:checknpm run testnpm run test:coveragenpm run test:e2enpm run qualityGitHub Actions runs automated quality checks for pushes and pull requests.
The CI pipeline includes:
Formatting
↓
ESLint
↓
Vitest
↓
Production Build
Playwright runs as a separate E2E job using Chromium.
Vercel Git Integration handles deployment independently from the CI workflow:
Pull Request
├── GitHub Actions
└── Vercel Preview
main
├── GitHub Actions
└── Vercel Production
This keeps CI and deployment responsibilities separate.
Local development:
.env.local
Automated CI:
GitHub Actions environment
Production:
Vercel Environment Variables
Required MarketStream variable:
NEXT_PUBLIC_MARKET_WS_URL=No private credentials are required by the frontend application.
Several decisions in this repository are intentional.
React consumes controlled application snapshots rather than every market event.
Candles and activity history have explicit limits.
Main Thread and Web Worker modes are both available so their trade-offs can be measured rather than assumed.
The WebSocket transport, RxJS pipeline, analytics engine, application state, and presentation layer have separate responsibilities.
Reconnect behavior and malformed-message handling can be triggered directly from the Reliability Lab.
Simulation Mode removes external network infrastructure from the critical automated test path.
MarketStream is an engineering and educational project.
It is not a trading platform, financial service, or source of financial advice.
Market data may be delayed, incomplete, or unavailable depending on the upstream provider and relay availability.
