Paper Trading Bots
After backtesting a strategy, you can save it as an algorithmic bot that **paper trades** on live Binance market data. The bot runs in the cloud (Hetzner VPS) — you don't need to keep your browser open. No real money is at risk; all trades are simulated.
Paper Trading Bots
Overview
After backtesting a strategy, you can save it as an algorithmic bot that paper trades on live Binance market data. The bot runs in the cloud (Hetzner VPS) — you don't need to keep your browser open. No real money is at risk; all trades are simulated.
How It Works
- Backtest your strategy on the Backtest page (
/b/backtest). - Click Deploy → name your bot → select exchange (Binance) → Deploy Paper Bot.
- Your bot is saved to the database with
mode='running'. - The live runner on Hetzner picks up your bot automatically:
- It wakes close to each bot's next candle close, with a 10-second safety ceiling for newly started bots.
- For each bot, it fetches the latest closed OHLCV bars from the Data API.
- It runs the same engine used for backtesting (single source — no divergence).
- When a new candle closes and the strategy generates a buy/sell signal, the runner:
- Records a signal with the ideal strategy bar time and the runner's generation time.
- Fetches a fresh live quote from the exchange.
- Simulates a paper fill at the live top-of-book (
askfor buys,bidfor sells). - Stores the exchange-observed execution time for the simulated fill.
- Updates the bot's paper account using that exchange execution price for cash, position, realized P&L, and equity.
- You monitor your bot on the Bots page (
/p/bots):- List all bots with status, paper P&L, start/pause controls.
- Detail page shows live signals, paper equity curve, and trade history.
Key Constraints
| Constraint | Value | Why |
|---|---|---|
| Minimum resolution | 5 minutes | Sub-5m strategies are rejected at deploy time. Binance 1m candles are too noisy for algorithmic bots on a free VPS. |
| Exchange support | Binance only | Other exchanges (Bitfinex, Bybit, KuCoin) are scaffolded in the UI but the live data pipeline only ingests Binance. |
| Trading mode | Paper only | No real orders are placed. Real Binance trading (with user-connected API keys) is planned for a future phase. |
| Execution location | Hetzner VPS (95.216.219.174) | Vercel free tier cannot run 24/7 serverless functions. The live runner is a Node.js Docker container alongside the Data API. |
| Browser requirement | None | Bots run 24/7 on Hetzner. The browser is only for viewing results. |
Architecture
┌──────────────────────┐ ┌──────────────────────────────────┐
│ Vercel (Next.js) │ │ Hetzner VPS │
│ │ │ │
│ - Bots list page │ │ ┌──────────────────┐ │
│ - Bot detail page │ │ │ sa_data_downloader │ │
│ - Deploy modal │──────→│ │ (FastAPI + CCXT) │ │
│ - Read-only viewer │ │ │ /api/live bars │ │
│ │ │ └──────────────────┘ │
│ All reads from: │ │ ┌──────────────────┐ │
│ GET /api/bots/[id]/ │ │ │ live-runner (Node)│ohonober │
│ paper-trades │ │ │ Polls Supabase DB │honets │
│ GET /api/strategies/ │ │ │ Runs engineWorker │ost │
│ [id]/signals │◀──────│ │ Writes signals + │ohonets │
│ │ │ │ paper trades │ohonets │
│ │婚后称 │ └──────────────────┘honets │
│ Debug only: │ri后称 │ │
│ POST /api/bots/[id]/ │ri后称 │ All writes to: │
│ run (manual tick) │pri称 │ Supabase Postgres (shared) │
└──────────────────────┘ └──────────────────────────────────┘
Engine — Single Source of Truth
The backtest engine (src/lib/trading/engine/engineWorker.js) is the one implementation. It runs as:
- A browser Blob Web Worker for backtesting (via
useBacktestWorker.ts) - A Node.js
worker_threadsWorker for live trading (viascripts/run-live-runner.ts)
Both paths import the same file. There is no Python port, no second copy, no divergence. If you change the engine, both backtesting and live trading change identically.
Idempotency
The strategies.last_bar_time column records the newest bar the engine has consumed. On each tick, the runner only acts on events with timestamp > last_bar_time. This means:
- Re-running the same tick produces no new signals or trades.
- If the runner crashes mid-tick, re-running is safe (the next tick picks up where it left off).
- The first tick for a new bot starts paper trading from "now" — historical signals from the backtest are not replayed into the paper account.
Future Phases
Phase 2: Always-on (already done in this implementation)
The live runner on Hetzner runs 24/7. No browser needed.
Phase 3: Real Binance Trading
- User connects Binance API keys via the Exchange Settings page.
- Keys are encrypted and stored in
exchange_credentials(table already exists). - The live runner, when processing a
paperMode='live'bot, places real CCXT orders instead of simulating. - Position reconciliation uses the existing
signalsvsmatcher_tradescomparison (scaffolded in the codebase).
FAQ
Q: What happens if I close my browser? A: Nothing — your bot keeps running on Hetzner. The browser is only for viewing.
Q: Can I run multiple bots?
A: Yes. The live runner processes all mode='running' bots on each poll cycle.
Q: What fills do I get? A: Paper fills use a fresh live exchange quote when the signal is processed: buys fill at the best ask, sells fill at the best bid. If the quote service is temporarily unavailable, the runner falls back to the strategy bar price so the bot can keep running.
Q: Why 5 minutes minimum? A: 1-minute Binance candles are noisy, the backend downloads them with a delay, and running an engine every minute on a free VPS is wasteful. 5m+ gives reliable closed-bar data and reasonable CPU usage.