# πŸ“Š View β€” Crypto chart Platform [πŸ‡¨πŸ‡³ δΈ­ζ–‡η‰ˆ README](README_CN.md) 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](#-deployment) section for how to configure this. ![tv-1](https://img.cathiefish.art/ns/tv-1.png) ![tv-2](https://img.cathiefish.art/ns/tv-2.png) ![tv-3](https://img.cathiefish.art/ns/tv-3.png) ![tv-3](https://img.cathiefish.art/ns/tv-4.png) ## ✨ Features ### Data Collection & Storage - [x] **Real-time WebSocket streaming** β€” Live 1-minute K-line data from Binance combined streams, with auto-reconnect and gap backfill - [x] **3-tier historical sync** β€” Monthly ZIP β†’ Daily ZIP β†’ REST API, for fastest possible backfill from [Binance Data Archive](https://data.binance.vision/) - [x] **TimescaleDB time-series storage** β€” Hypertable-optimized with `time_bucket` aggregation across 8 timeframes (1m, 5m, 15m, 1h, 4h, 1D, 1W, 1M) - [x] **Proxy pool support** β€” Up to 100 concurrent proxy clients for high-throughput parallel downloads - [ ] **Binance Spot support** β€” Spot market data collection, using `.P` suffix to distinguish Futures from Spot symbols - [ ] **MCP support** β€” Model Context Protocol integration ### Charting & Visualization - [x] **Professional charting engine** β€” Full indicator and drawing tool support - [x] **Custom indicators** β€” Net Volume (NV-C) and Cumulative Volume Delta (CVD-C) - [x] **Multi-chart layouts** β€” Single, vertical split, horizontal split, and 1L+2R layouts with draggable dividers - [x] **Chart save/load** β€” Built-in save/load via `save_load_adapter` (metadata in localStorage, chart content in backend API), with auto-save - [x] **Dark & Light themes** β€” Full theme toggle synced across widget and UI ### Watchlist & Interaction - [x] **Live watchlist sidebar** β€” Real-time price, 24h change tracking, drag-and-drop reordering - [x] **Multiple custom lists** β€” Create and manage separate watchlists - [ ] **Frontend symbol management** β€” Add/remove tracked symbols from the frontend UI (currently `TRACKED_SYMBOL` is only controlled via the backend `.env` file) ### Security & Deployment - [x] **Google OAuth protection** β€” Email whitelist access control with 144-hour session persistence - [x] **Docker ready** β€” Multi-stage builds, Docker Compose with shared network - [ ] **Google Firebase integration** β€” Connect backend API via Firebase for managed authentication and hosting ## πŸ“ Project Structure ``` 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 ``` ## πŸ› οΈ Tech Stack | 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 | ## πŸš€ Deployment 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. ### Option 1: Full Self-Hosted Deployment 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. #### Network Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ 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. --- #### Step 1: Create Docker Network ```bash docker network create cycle ``` --- #### Step 2: Database Setup (TimescaleDB) Create a `docker-compose.yml` for TimescaleDB on the `cycle` network: ```yaml # 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: true ``` ```bash docker compose up -d ``` Then initialize the schema using the SQL in [backend/examples/sql.txt](backend/examples/sql.txt): ```bash docker exec -i timescaledb psql -U quant -d crypto_database < backend/examples/sql.txt ``` > [!TIP] > For database management, consider deploying **PgAdmin** alongside TimescaleDB. See [this guide](https://n8n.cathiefish.art/n8n-instances-twitter-ai-%E5%88%86%E6%9E%90%E7%9B%91%E6%8E%A7-5b706bb444d7) for PgAdmin setup instructions. For beginners, ask **Claude Opus 4.6** to walk you through it. #### Step 3: Deploy Backend **1. Configure `.env`** ```bash cd backend cp .env.example .env ``` Edit `backend/.env`: ```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=10099 ``` > [!NOTE] > `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](https://data.binance.vision/) 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** ```bash docker build -t backend . docker compose up -d ``` --- #### Step 4: Deploy Frontend Before 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**: ```js 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): ```bash $ docker inspect backend --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 172.18.0.3 ``` > [!IMPORTANT] > Google OAuth requires a Client ID from GCP Console. Tutorial placeholder β€” to be added. **3. Build & Run** ```bash cd frontend docker build -t frontend . docker compose up -d ``` > [!WARNING] > 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`. --- #### Step 5: Nginx Proxy Manager Nginx Proxy Manager must also run on the `cycle` network. Create a `docker-compose.yml`: ```yaml # 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: true ``` ```bash docker compose up -d ``` > [!TIP] > For detailed Nginx Proxy Manager setup and Docker deployment best practices, see [this guide](https://n8n.cathiefish.art/rsshub-install-f5ad036a4dd9). 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`. --- ### Option 2: Backend Self-Hosted + Frontend on Cloudflare Pages 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`. #### Step 1: Backend + Database Complete **Option 1 Steps 1–3** (create `cycle` network, deploy TimescaleDB, deploy backend). #### Step 2: Expose Backend API via Nginx Proxy Manager 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. #### Step 3: Deploy to Cloudflare Pages 1. Fork or clone this repository to your own GitHub account 2. Go to [Cloudflare Dashboard](https://dash.cloudflare.com/) β†’ **Workers & Pages** β†’ **Create** 3. Select **Pages** β†’ **Connect to Git** 4. Authorize Cloudflare to access your GitHub account and select the repository 5. Configure the build settings: | Setting | Value | |---------|-------| | Production branch | `main` | | Build command | `sh build.sh` | | Build output directory | `frontend` | 6. 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 | 7. 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**. --- ## πŸ“œ License MIT Generated By Claude Opus 4.6