Paper trading → deploy to a VPS
Prove the bot on a testnet, then deploy it headless on a VPS with a systemd unit, log rotation, and a real kill switch — with safety flags standing between you and a mistake.
Your bot works on your laptop. Two things remain: prove it does the right thing without real money, and run it somewhere that stays up when your laptop sleeps. This lesson does both — and every step keeps a safety switch between you and an accident.
Stage 1 — paper/testnet, for real time
A backtest proved the *logic* over history. Paper trading on a live testnet proves the *plumbing* over real time: that your bar-close detection is right, that reconnection actually reconnects, that reconciliation survives a restart, that fills come back in the shape you expect. Run it for days, not minutes — long enough to cross a network hiccup, a full trade cycle, and a deliberate restart.
# Talk to the sandbox, and let it place REAL orders — on fake balances.
export TESTNET=true # sandbox endpoints, fake money
export DRY_RUN=false # actually place orders (on the testnet)
export LIVE=true # arm the loop
source .venv/bin/activate
python bot.py # watch the logs; leave it for a few daysStage 2 — the kill switch
Before deploying anything that can trade real money, you need a way to stop it instantly that does not depend on the bot behaving. We built two into the loop already: a STOP file and the LIVE flag. Either one flattens the position and idles the bot. On a server you can trip it with a single command, even over a flaky connection.
# Instant stop #1: drop a STOP file. The loop sees it, flattens, idles.
touch /opt/livebot/STOP
# Instant stop #2: disarm and restart (LIVE=false makes it idle).
sudo systemctl set-environment LIVE=false
sudo systemctl restart livebot
# Resume trading: remove the file (and re-arm if you disarmed).
rm /opt/livebot/STOPStage 3 — a VPS, running headless
A VPS (a cheap always-on Linux server near the exchange) runs the bot when your laptop is closed. Deployment is unglamorous and that is good: copy the code, create a virtual environment, put the secrets in the environment (never in the repo), and hand the process to systemd so the OS keeps it alive.
# On the VPS (Ubuntu), as a non-root user in a dedicated directory.
sudo mkdir -p /opt/livebot && sudo chown "$USER" /opt/livebot
cd /opt/livebot
git clone <your-private-repo> . # code only — NO secrets committed
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
# Secrets live in a root-only env file, NOT in the repo or the unit file.
sudo tee /etc/livebot.env >/dev/null <<'EOF'
EXCHANGE_ID=binance
SYMBOL=BTC/USDT
TIMEFRAME=1h
API_KEY=your_LIVE_key
API_SECRET=your_LIVE_secret
TESTNET=false
DRY_RUN=false
LIVE=true
RISK_FRACTION=0.01
EOF
sudo chmod 600 /etc/livebot.env # readable only by rootStage 4 — the systemd unit
systemd is Linux’s service manager. A unit file tells it how to start the bot, where to read the environment, and — critically — to restart it if it ever exits. Combined with the loop’s own resilience, this gives two layers of "keep running": the loop survives errors, and if the process dies entirely, systemd brings it back.
[Unit]
Description=AlgoPro live trading bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=botuser
WorkingDirectory=/opt/livebot
EnvironmentFile=/etc/livebot.env # secrets injected here, not in code
ExecStart=/opt/livebot/.venv/bin/python /opt/livebot/bot.py
Restart=on-failure # bring it back if it crashes
RestartSec=10 # ...after a 10s pause
StartLimitIntervalSec=300
StartLimitBurst=5 # but stop flapping if it can't stay up
# Hardening: limit what a compromised bot can touch.
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/livebot # only its own dir is writable
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable livebot # start automatically on boot
sudo systemctl start livebot
sudo systemctl status livebot # is it running?
journalctl -u livebot -f # tail the live logs (from stdout)Stage 5 — log rotation
A bot that runs for months will fill the disk with logs if you let it. The systemd/stdout logs are managed by journald automatically. For the file log our logger writes, rotate it so it never grows unbounded — either swap FileHandler for a RotatingFileHandler, or hand it to the system logrotate.
/opt/livebot/livebot.log {
daily
rotate 14 # keep 14 days
compress
missingok
notifempty
copytruncate # rotate without needing to restart the bot
}You built a live bot
Step back and look at what you assembled: a config that defaults to safe, a ccxt exchange wrapper that refuses to touch real money until armed, an order manager with idempotent client IDs and per-loop reconciliation, a resilient loop with backoff and a kill switch, and a systemd deployment that keeps it alive and its logs bounded. That is a real system — the five components of Part 1, made live.
From here the road is width, not novelty: more instruments, better sizing (Part 7), monitoring and alerts, and the same loop pointed at a different broker. The architecture does not change — which is exactly the payoff of building it properly once.