Deployment
make docker-* targets. The systemd / SQLite material below describes
the legacy Oracle setup and the still-present make deploy/sync/restart targets.
Package entry points
# All three are equivalent — pick whichever you prefer
python run.py
python -m arb_bot
# On Linux server the systemd service uses:
ExecStart=venv/bin/python run.py
Makefile shortcuts (from local machine)
Requires .env.deploy with DEPLOY_SSH_KEY, DEPLOY_USER (root), DEPLOY_HOST (187.127.181.8), DEPLOY_REMOTE_DIR (/opt/trading-bot). See Infra & Services for the full Docker reference.
| Command | What it does |
|---|---|
make docker-deploy | Build HTML+frontend → rsync → DB migrate → rebuild & restart bot services (and re-resolve nginx) |
make docker-deploy-code | Code-only deploy (skip the frontend rebuild) |
make docker-status | docker compose ps on the server |
make docker-logs | Tail live container logs (Ctrl+C to stop) |
make docker-restart | Restart arb-bot dashboard token-refresher |
make docker-ssh | SSH into the Hostinger server |
make docker-setup | One-shot VPS provisioning (Docker + Node + ufw) |
make ssl-init | Issue the Let's Encrypt certificate (after DNS points here) |
make build-html | Rebuild public/index.html + React app locally (run after editing any section file) |
dashboard container gives it a new IP. nginx caches the
dashboard upstream at startup, so docker_deploy.sh restarts nginx after
every deploy — otherwise the proxy serves 502s (and OTP/login stop working) until nginx
re-resolves.
public/sections/, then run make build-html.
The generated public/index.html is what gets deployed to Netlify. Never edit index.html directly.
make fetch-metrics → downloads legacy metrics/*.json files locally →
run python backtest_recovery.py to analyse partial-fill recovery performance, or
use /analyze-trading-metrics in Claude Code for Config tuning recommendations.
Architecture Overview
The dashboard follows a modern decoupled architecture for better performance and interactivity:
- Frontend: A React Single Page Application (SPA) built with Vite and Tailwind CSS. Located in
frontend/, built intopublic/dist/. - Backend: A FastAPI server (
dashboard_server.py) that provides real-time PnL, position tracking, and documentation metadata via REST APIs. - Nginx Proxy: Acts as the entry point, serving static files and proxying API requests to the FastAPI backend.
Systemd Services
Two separate services run on the production server:
| Service | Purpose | Entry Point |
|---|---|---|
arb-bot.service | The core trading engine | run.py |
token-refresher.service | Dhan token renewal independent of the bot loop | token_refresher.py --interval-sec 300 |
arb-dashboard.service | FastAPI backend for dashboard | dashboard_server.py |
The token refresher unit template lives at scripts/token-refresher.service. The dashboard shows its last stored token health through /api/token/health.
Nginx Configuration
Nginx redirects all port 80 traffic, including direct IP requests, to https://portfolioplanner.online. The HTTPS server serves the SPA from /var/www/html/dist/ and proxies /api/* requests to localhost:8080. Configuration is maintained in scripts/nginx_dashboard.conf.
The dashboard server block includes 92.4.66.95, portfolioplanner.online, and www.portfolioplanner.online as accepted host names so domain traffic does not fall through to the default Nginx site.
Deployed documentation routes are not public. Requests to /docs/* use Nginx auth_request against /api/auth/nginx-check, which accepts only a valid dashboard session cookie. Unauthenticated users are redirected to /login before any docs route is served.
Dashboard Session Policy
Dashboard login uses Telegram OTP and an HttpOnly session cookie. Only one dashboard session is active at a time: a successful login issues a new signed JWT session id and invalidates any older dashboard cookie on its next API request or websocket connection. Logging out from an older invalidated browser cannot clear the current active session.