Goal: serve financial price history and technical analysis tools (OHLCV candles, indicators, time-series utilities) to a Next.js web app with low latency and high availability.
This document explains how the whole system runs online: topology, DNS/Routing, deployments, security posture, performance targets, observability, and day-2 operations. It complements the per-repo READMEs for Backend (API) and Frontend (Next.js).
-
Frontend: Next.js (app router) deployed as a static site on Cloudflare Pages. It renders charts (e.g., lightweight-charts) and calls the public API over HTTPS.
-
Backend (API): C++ service exposing REST + WebSocket for OHLCV data. Runs on AWS EC2 (Amazon Linux 2023) in sa-east-1 (São Paulo). Persists data in DuckDB on an attached volume. Ingests live candles from Binance WS, and (optionally) backfills via Binance REST.
-
Edge & DNS: Cloudflare is the public edge (DNS + proxy).
tradingchart.ink→ frontend (Pages)api.tradingchart.ink→ backend (EC2, proxied by Cloudflare)
The platform focuses on ingesting, persisting, and serving historical market data (OHLCV) while exposing primitives for technical analysis (interval selection, symbol metadata, and server-side indicator computation roadmap). The frontend consumes these datasets to render interactive charts and TA overlays.
-
Apex:
tradingchart.inkCloudflare Pages project hosts the static Next.js build.- Pages build command:
pnpm run build(ornext build) - Output directory:
out/(viaoutput: 'export'innext.config.js)
- Pages build command:
-
API:
api.tradingchart.inkCloudflare DNS CNAME (proxied) → the EC2 public IP (or ALB DNS if using a load balancer). SSL/TLS terminates at Cloudflare (orange cloud). Health checks hit/healthz.
- Pure static export (
output: 'export') so Pages can serve it from the edge. - Calls the API using
https://api.tradingchart.ink. - Avoids a Next.js
/apilayer to keep it static and fast.
Build hints
-
next.config.js:/** @type {import('next').NextConfig} */ const nextConfig = { output: 'export', eslint: { ignoreDuringBuilds: true }, typescript: { ignoreBuildErrors: true }, images: { unoptimized: true }, }; export default nextConfig;
-
package.json(relevant scripts):{ "scripts": { "build": "next build" } }
-
Endpoints:
GET /api/v1/symbolsGET /api/v1/intervals?symbol=...GET /api/v1/candles?symbol=...&interval=...&limit=...GET /healthz,GET /stats,GET /version- WebSocket:
ws://<host>:<port>/ws(via Cloudflare proxy →wss://api.tradingchart.ink/ws)
-
Data: DuckDB file at
/data/market.duckdb -
Live ingestion: Binance WS (kline 1m); backfill via Binance REST.
-
CORS: Configurable via flags/env; Cloudflare adds extra perimeter controls.
-
Production
- Region: sa-east-1 (low latency to Latin America)
- Instance: Amazon Linux 2023, Docker runtime
- Security Group: inbound
80/tcpfrom Cloudflare only (recommended), or 0.0.0.0/0 during bootstrap; outbound443/tcpfor Binance. - Cloudflare: DNS proxied, WAF basic rules enabled.
-
Staging/Dev
- Optional separate EC2 or local Docker with CORS set to
*or LAN origin. - Pages deploy preview branches as needed.
- Optional separate EC2 or local Docker with CORS set to
-
Build the image (locally or on the instance)
docker build -t tradingchart-api:local . -
Prepare storage
sudo mkdir -p /var/ttv/data sudo chown 1000:1000 /var/ttv/data sudo chmod u+rwX,g+rwX /var/ttv/data
-
Run
sudo docker run -d --name tradingchart-api \ -p 80:8080 \ -v /var/ttv/data:/data \ tradingchart-api:local \ ./bin/api --live=1 --storage duck --duckdb /data/market.duckdb \ --exchange binance \ --live-symbols "BTCUSDT,ETHUSDT" \ --live-intervals "1m" \ --log-level info \ --http.cors.enable=1 \ --http.cors.origin "https://www.tradingchart.ink" -
Smoke tests (from your laptop)
curl -s https://api.tradingchart.ink/healthz curl -s "https://api.tradingchart.ink/api/v1/symbols" curl -s "https://api.tradingchart.ink/api/v1/candles?symbol=BTCUSDT&interval=1m&limit=5" curl -s "https://api.tradingchart.ink/stats"
Note: If the API can’t write to DuckDB, check volume permissions and run the container as the same UID/GID that owns
/var/ttv/dataor loosen permissions during bootstrap.
-
Repo: connect GitHub.
-
Build command:
pnpm run build(ornext build) -
Output dir:
out/ -
Environment variables (Pages):
NEXT_PUBLIC_API_BASE=https://api.tradingchart.ink
DNS to Pages
- Map
tradingchart.ink(andwww) to the Pages project in Cloudflare → Custom domain.
-
REST CORS: controlled by flags/env:
--http.cors.enable=1--http.cors.origin "https://www.tradingchart.ink"(prod)- For dev you can use
"*"(without credentials) or a CSV once multi-origin support is added.
-
WebSocket: browser WS handshake isn’t governed by HTTP CORS. Validate
Originupstream (Cloudflare WAF) and restrict who can reach the EC2 (Security Group and CF IPs).
- Perimeter: Cloudflare (TLS, WAF, rate limiting if enabled)
- Origin: EC2 security group restricts inbound ports. Prefer allowing only Cloudflare IP ranges to port 80.
- Transport to Binance: verify TLS (bundle CA in the image or rely on system CA).
- Auth/Rate limiting: not yet in the API; recommend enabling Cloudflare WAF rules / rate limits. Roadmap includes API keys/JWT if needed.
- Data at rest: DuckDB file on EBS/EFS; enable encrypted volumes.
-
Targets
- REST latency p95 < 200 ms for
limit <= 600(typical UI views). - WS: thousands of concurrent clients for final candle frames (small payloads).
- Ingestion lag: ideally < 2 intervals (≤2 minutes at 1m).
- REST latency p95 < 200 ms for
-
Tuning knobs
--threads <n>for HTTP concurrency.- Use São Paulo (sa-east-1) region to minimize RTT for LATAM.
- Ensure
/datais on general purpose SSD (gp3) or EFS with appropriate throughput if sharing.
-
Known hotspots
- JSON serialization for large
limitfetches. - DuckDB reads when not cached.
- WS backpressure (implementation hardening recommended).
- JSON serialization for large
- Health:
GET /healthz→{"status":"ok"}when live ingestion is healthy. - Stats:
GET /stats→ uptime, reconnect counters, route latencies,ws_state,last_msg_age_ms. - Logging: structured logs to stdout/stderr (Docker). Increase with
--log-level debugduring incidents.
Suggested alerts
ws_state == 0for > 2mlast_msg_age_ms > 120000(for 1m interval)- Sudden growth in
rest_catchup_candles_total
-
Rotate / Redeploy
sudo docker pull <image:tag> # if pushing to a registry sudo docker rm -f tradingchart-api sudo docker run ... # same invocation as above
-
Backfill (optional)
- Run a one-off backfill container with
--backfillflags (see backend README), mounting/datato persist the result.
- Run a one-off backfill container with
-
Vacuum / maintenance
- Periodic DuckDB maintenance (VACUUM) if the DB grows due to re-ingestion or schema updates.
-
Cost watch
- Cloudflare: check Analytics → Bandwidth/Requests, enable rate limiting if needed.
- AWS: Billing & Cost Management → Cost Explorer and Budgets alarms. EC2 size can be tuned down if idle.
-
API
./bin/api --live=1 --storage duck --duckdb data/market.duckdb \ --exchange binance \ --live-symbols "BTCUSDT,ETHUSDT" \ --live-intervals "1m" \ --http.cors.enable=1 \ --http.cors.origin="http://localhost:3000" \ --log-level debug
-
Frontend
npm run dev # NEXT_PUBLIC_API_BASE=http://localhost:8080
-
Frontend loads but “no chart data”
GET /api/v1/candlesreturnsdata: []→ likely DuckDB write permission or ingestion down.- Check
/stats→ws_state,last_msg_age_ms. - On EC2:
docker logs -f tradingchart-apifor DuckDB permission errors.
-
522 from Cloudflare
- Origin not reachable: Security Group, container down, or port mismatch.
- Test origin directly:
curl -I http://<EC2_PUBLIC_IP>/healthz.
-
CORS errors in browser
- Ensure
--http.cors.enable=1and--http.cors.originmatches the exact frontend origin. - Preflight (OPTIONS) support: if the browser sends preflight and API returns 404, add OPTIONS handling (roadmap item).
- Ensure
-
P0
- Enforce WS backpressure; expose metrics for slow client closes.
- Add
/readyzendpoint (warmup vs ready). - TLS verification hardening for upstream providers.
-
P1
- API auth/rate limiting at the edge (Cloudflare) + optional API keys/JWT in the API.
- Connection pooling / reduced per-request DuckDB open/close for high QPS.
-
P2
- Server-side indicator endpoints.
- Multi-origin CORS (CSV + preflight OPTIONS) first-class support.
- Official Dockerfile & CI to publish images.
-
Frontend:
https://tradingchart.ink -
API:
https://api.tradingchart.ink- Health:
/healthz - Stats:
/stats - Symbols:
/api/v1/symbols - Intervals:
/api/v1/intervals?symbol=BTCUSDT - Candles:
/api/v1/candles?symbol=BTCUSDT&interval=1m&limit=600 - WebSocket:
wss://api.tradingchart.ink/ws
- Health:
Contact/On-Call
- EC2 access (SSH key), Cloudflare account, and GitHub repos are required for operations.
- Keep a runbook with the exact
docker runline used in production, plus the current security group rules and Pages build settings.
