A self-hosted, real-time cryptocurrency data collection and visualization platform for Binance USDS-M Futures. Combines a high-performance Rust backend with a professional charting frontend to deliver institutional-grade market data tooling.
This project is primarily built for my own use, and I don't have much bandwidth to maintain it. I'm also not a frontend developer, so the frontend code may not follow best practices. That said, pull requests and issues are very welcome — feel free to open an Issue or submit a Pull Request!
As you can see, this is not a simple one-click-to-deploy project, nor is it a beginner-friendly toy. I simply don't have the time or ability to simplify it further. The easiest way to deploy is to let Claude Opus 4.6 study the entire project and have it guide you through the setup.
If deploying the backend is too difficult, you can temporarily use my public backend API endpoint: api-view.cathiefish.org. This way you only need to deploy the frontend to Cloudflare Pages. Note that this endpoint only guarantees data for BTCUSDT, ETHUSDT, SOLUSDT, BNBUSDT, XRPUSDT, SUIUSDT. See the Deployment section for how to configure this.
- Real-time WebSocket streaming — Live 1-minute K-line data from Binance combined streams, with auto-reconnect and gap backfill
- 3-tier historical sync — Monthly ZIP → Daily ZIP → REST API, for fastest possible backfill from Binance Data Archive
- TimescaleDB time-series storage — Hypertable-optimized with
time_bucketaggregation across 8 timeframes (1m, 5m, 15m, 1h, 4h, 1D, 1W, 1M) - Proxy pool support — Up to 100 concurrent proxy clients for high-throughput parallel downloads
- Binance Spot support — Spot market data collection, using
.Psuffix to distinguish Futures from Spot symbols - MCP support — Model Context Protocol integration
- Professional charting engine — Full indicator and drawing tool support
- Custom indicators — Net Volume (NV-C) and Cumulative Volume Delta (CVD-C)
- Multi-chart layouts — Single, vertical split, horizontal split, and 1L+2R layouts with draggable dividers
- Chart save/load — Built-in save/load via
save_load_adapter(metadata in localStorage, chart content in backend API), with auto-save - Dark & Light themes — Full theme toggle synced across widget and UI
- Live watchlist sidebar — Real-time price, 24h change tracking, drag-and-drop reordering
- Multiple custom lists — Create and manage separate watchlists
- Frontend symbol management — Add/remove tracked symbols from the frontend UI (currently
TRACKED_SYMBOLis only controlled via the backend.envfile)
- Google OAuth protection — Email whitelist access control with 144-hour session persistence
- Docker ready — Multi-stage builds, Docker Compose with shared network
- Google Firebase integration — Connect backend API via Firebase for managed authentication and hosting
view/
├── backend/ # Rust data engine & API server
│ ├── src/
│ │ ├── main.rs # Axum HTTP server bootstrap
│ │ ├── binance_collector.rs # WebSocket real-time collection + REST sync
│ │ ├── historical_downloader.rs # Binance data archive (ZIP) downloader
│ │ ├── database.rs # TimescaleDB operations & aggregation
│ │ ├── scheduler.rs # Task coordination & collector lifecycle
│ │ ├── klinechart.rs # KlineChart REST API handlers
│ │ ├── tradingview.rs # TradingView UDF API + WebSocket + Canvas
│ │ ├── structs.rs # Data types (CandleData, Interval, WsMessage…)
│ │ ├── error.rs # Custom error types
│ │ └── lib.rs # Public module exports
│ ├── tests/
│ │ ├── connection_test.rs # Database connection tests
│ │ ├── database_test.rs # CRUD & query tests
│ │ ├── scheduler_test.rs # Scheduler command & lifecycle tests
│ │ ├── sync_test.rs # Single symbol sync tests
│ │ └── sync_full_history_test.rs
│ ├── examples/
│ │ ├── sync_all.rs # Sync all symbols (standard)
│ │ ├── sync_all_fast.rs # Sync all symbols (parallel with proxy pool)
│ │ └── sql.txt # Reference SQL for TimescaleDB setup
│ ├── Dockerfile
│ └── docker-compose.yml
│
├── frontend/ # Charting Library frontend
│ ├── index.html # Main app (widget + watchlist + layouts)
│ ├── login.html # Google OAuth login page
│ ├── auth.js # AuthGuard — session management
│ ├── auth-config.js # OAuth & API configuration
│ ├── charting_library/ # Charting Library assets
│ ├── datafeeds/ # UDF datafeed adapter
│ ├── Dockerfile
│ └── docker-compose.yml
│
└── references/ # Git submodules
├── binance-rust/ # Binance connector SDK
└── library/ # Charting Library source
| Layer | Technology |
|---|---|
| Backend | Rust, Axum, sqlx, tokio, tokio-tungstenite |
| Database | PostgreSQL + TimescaleDB |
| Frontend | Charting Library v29.4, Vanilla JS |
| Auth | Google Identity Services (OAuth) |
| Deployment | Docker, Nginx, Docker Compose |
For first-time users of Nginx Proxy Manager, TimescaleDB, PgAdmin, Docker, or Cloudflare — the easiest way to deploy is to let Claude Opus 4.6 study this project and guide you through each step. If you encounter persistent issues, please open an Issue on this repository.
Deploy the entire stack on your own server with Docker and Nginx Proxy Manager as the reverse proxy.
Note
If you are unsure about any of the steps below, ask Claude Opus 4.6 for guidance — it can walk you through each step in detail.
┌─────────────────── Docker Network: cycle ───────────────────┐
│ │
│ ┌──────────────────┐ ┌──────────────┐ │
│ │ Nginx Proxy Mgr │ │ TimescaleDB │ │
│ │ :80 / :443 │ │ :5432 │ │
│ └────────┬─────────┘ └──────┬───────┘ │
│ │ │ │
│ │ │ │
│ ┌────────▼──┐ ┌───────────┐ │ │
│ │ Frontend │ │ Backend │───┘ │
│ │ :80 │ │ :3000 │ │
│ │ (nginx) │ │ (axum) │ │
│ └───────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
▲
Public domain
view.yourdomain.com
All containers must run on the same Docker network cycle so they can communicate by container name. Only the frontend needs to be exposed to the public via Nginx Proxy Manager — the backend (port 3000) is accessed internally by the frontend container through Docker DNS.
docker network create cycleCreate a docker-compose.yml for TimescaleDB on the cycle network:
# database/docker-compose.yml
services:
timescaledb:
image: timescale/timescaledb:latest-pg16
container_name: timescaledb
networks:
- cycle
environment:
- POSTGRES_USER=quant
- POSTGRES_PASSWORD=your_secure_password
- POSTGRES_DB=crypto_database
volumes:
- timescaledb_data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
timescaledb_data:
networks:
cycle:
external: truedocker compose up -dThen initialize the schema using the SQL in backend/examples/sql.txt:
docker exec -i timescaledb psql -U quant -d crypto_database < backend/examples/sql.txtTip
For database management, consider deploying PgAdmin alongside TimescaleDB. See this guide for PgAdmin setup instructions. For beginners, ask Claude Opus 4.6 to walk you through it.
1. Configure .env
cd backend
cp .env.example .envEdit backend/.env:
RUST_LOG="INFO,binance_sdk::common::utils=off,binance_sdk::common::websocket=off"
DATABASE_URL="postgres://quant:your_secure_password@timescaledb:5432/crypto_database"
TRACKED_SYMBOL=[BTCUSDT,ETHUSDT,BNBUSDT,SOLUSDT,XRPUSDT]
# Proxy settings (optional — leave empty for direct connection)
PROXY_HOST=dc.your-proxy-provider.com
PROXY_USERNAME=your_username
PROXY_PASSWORD=your_password
PROXY_PROTOCOL=https/http/socks5
PROXY_PORT_START=10000
PROXY_PORT_END=10099Note
DATABASE_URL uses the container name timescaledb as the hostname — this works because both containers are on the cycle network. You can also use the database container's absolute IP (find it with docker inspect timescaledb --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'). Make sure the database container is also on the cycle network. TRACKED_SYMBOL currently only supports USDS-M Futures (Swap), not Spot.
Warning
Proxy is strongly recommended. Historical data sync downloads from Binance Data Archive for all tracked symbols. With a multi-port proxy pool (e.g. 100 concurrent connections), a full sync completes in hours. Without a proxy, syncing may take several days. If PROXY_HOST is left empty, the backend falls back to a single direct connection.
2. Build & Run
docker build -t backend .
docker compose up -dBefore building the frontend container, you need to configure authentication and API connection.
1. Configure auth-config.js
Since the frontend runs inside the Docker network, it accesses the backend via the container name:
window.API_CONFIG = { baseUrl: 'http://backend:3000' };
const AUTH_CONFIG = {
clientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
onSuccess: (user) => { console.log('Auth successful:', user.email); },
onError: (error) => { console.error('Auth error:', error); }
};
window.AUTH_CONFIG = AUTH_CONFIG;
window.ALLOWED_EMAILS = ['your-email@gmail.com'];Replace clientId with your Google OAuth Client ID from GCP Console, and ALLOWED_EMAILS with your email whitelist.
To find the backend container IP (useful for debugging):
$ docker inspect backend --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.18.0.3Important
Google OAuth requires a Client ID from GCP Console. Tutorial placeholder — to be added.
3. Build & Run
cd frontend
docker build -t frontend .
docker compose up -dWarning
The frontend and backend containers must be on the same Docker network (cycle). This is already configured in both docker-compose.yml files via networks: cycle: external: true.
Nginx Proxy Manager must also run on the cycle network. Create a docker-compose.yml:
# npm/docker-compose.yml
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
networks:
- cycle
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- npm_data:/data
- npm_letsencrypt:/etc/letsencrypt
restart: unless-stopped
volumes:
npm_data:
npm_letsencrypt:
networks:
cycle:
external: truedocker compose up -dTip
For detailed Nginx Proxy Manager setup and Docker deployment best practices, see this guide. For beginners, ask Claude Opus 4.6 to walk you through it.
Access the admin panel at http://your-server-ip:81 (default: admin@example.com / changeme).
Create a Proxy Host for the frontend:
| Domain | Forward Hostname | Forward Port | SSL |
|---|---|---|---|
view.yourdomain.com |
frontend_tv |
80 |
✅ Let's Encrypt |
Tip
The "Forward Hostname" uses the container name (not IP), which works because all containers share the cycle network. The backend does not need a public proxy — the frontend accesses it internally via Docker DNS (http://backend:3000).
After setup, your platform will be accessible at https://view.yourdomain.com.
Build on top of Option 1 — keep the same server-side setup (Docker network, TimescaleDB, backend), but additionally expose the backend API to the public internet and deploy the frontend to Cloudflare Pages instead of self-hosting it.
Note
If you are unsure about any of these steps, ask Claude Opus 4.6 — this is a straightforward process.
Caution
Do NOT include "tradingview" in your Cloudflare Pages project name or custom domain. TradingView actively enforces their trademark and your deployment will be taken down. Use a neutral name like view, chart, or crypto-dash.
Complete Option 1 Steps 1–3 (create cycle network, deploy TimescaleDB, deploy backend).
Since the frontend will be served from Cloudflare (outside your Docker network), the backend API must be publicly accessible. Add a second Proxy Host in Nginx Proxy Manager:
| Domain | Forward Hostname | Forward Port | SSL |
|---|---|---|---|
api.yourdomain.com |
backend |
3000 |
✅ Let's Encrypt |
Important
Enable Websockets Support for this proxy host — required for real-time candle streaming.
- Fork or clone this repository to your own GitHub account
- Go to Cloudflare Dashboard → Workers & Pages → Create
- Select Pages → Connect to Git
- Authorize Cloudflare to access your GitHub account and select the repository
- Configure the build settings:
| Setting | Value |
|---|---|
| Production branch | main |
| Build command | sh build.sh |
| Build output directory | frontend |
- Add Environment Variables (Settings → Environment Variables):
| Variable | Value | Description |
|---|---|---|
API_BASE_URL |
https://api.yourdomain.com |
Public backend API URL from Step 2 |
GOOGLE_CLIENT_ID |
YOUR_CLIENT_ID.apps.googleusercontent.com |
Google OAuth Client ID from GCP Console |
ALLOWED_EMAILS |
alice@gmail.com,bob@gmail.com |
Comma-separated email whitelist |
- Click Save and Deploy
Note
The build.sh script generates auth-config.js from environment variables at build time. No sensitive values are stored in the repository — all credentials are configured via Cloudflare's environment variables dashboard.
Cloudflare will assign a *.pages.dev domain. You can add a custom domain in Pages → Custom domains.
MIT
Generated By Claude Opus 4.6



