This document covers day-to-day operation of perp-bot, from local paper trading to systemd deployment.
Default mode. Uses PaperExecutor and records simulated trades in SQLite.
Recommended for:
- local development
- validating config changes
- checking TUI behavior
- smoke testing a new release
Uses LiveExecutor and places real orders on Hyperliquid.
Additional startup behavior:
- requires
HL_PRIVATE_KEY - sets leverage for configured symbols
- reconciles exchange positions with local DB state
- attempts to attach server-side stop-loss protection
cp .env.example .env
uv sync --group dev
source .venv/bin/activate
perpbot backfill
perpbot tradeIn another terminal:
perpbot status
perpbot tui- SQLite DB at the configured
db_path - Unix socket next to that DB
- rotating log file next to that DB
- open and closed trades recorded in SQLite
Before enabling live mode:
- confirm
mode: "live"intentionally - verify symbol list and leverage
- verify
capital_usd, margin cap, and stop-loss settings - verify
HL_PRIVATE_KEY - verify alert endpoints if you depend on Discord or Telegram
- run a paper session with the same config shape first
- verify
perpbot statusandperpbot tuiagainst the daemon
After startup, confirm:
- leverage setup succeeded
- position reconciliation logs look correct
- WebSocket feed is healthy
- daemon status returns valid JSON
Built-in controls include:
- per-trade capital stop loss
- daily loss limit
- cooldown after stop loss
- 24-hour position timeout
- max position count
- crisis-regime entry block
- three-consecutive-losing-weeks startup halt
If trade refuses to start because of the losing-weeks halt, you can override it with:
perpbot trade --forceUse that only intentionally.
perpbot statusThis reads the daemon state over the Unix socket and prints JSON.
perpbot tuiKey bindings include:
p: pause the daemonr: resume the daemone: emergency close flowq: quit the TUI
The TUI is safe to attach and detach repeatedly.
The app uses JSON structured logs.
Outputs:
- stdout
- rotating file log when the trading daemon starts the IPC/TUI stack
Log file location:
<db directory>/perp-bot.log
Fields include:
- timestamp
- level
- logger name
- message
- exception, when present
The repository includes:
deploy/deploy.shdeploy/perp-bot.service
The deploy script assumes:
- a GCP Compute Engine instance
gcloudauthenticated locally- SSH access to the instance
uvinstalled on the remote machine
./deploy/deploy.sh <instance-name> [zone]The script:
- copies the repo to
/opt/perp-bot - runs
uv sync --frozenremotely - installs the bundled systemd service
- restarts the service
- prints service status
The service file currently expects:
- app directory:
/opt/perp-bot - environment file:
/opt/perp-bot/.env - service user:
perp-bot - executable:
/opt/perp-bot/.venv/bin/perpbot trade
If your target environment differs, edit deploy/perp-bot.service before deployment.
On the host:
sudo systemctl status perp-bot --no-pager
sudo journalctl -u perp-bot -n 200 --no-pager
sudo journalctl -u perp-bot -f
sudo systemctl restart perp-bot
sudo systemctl stop perp-botCheck:
- whether
perpbot tradeis actually running - whether the socket path matches the configured
db_path - whether the daemon has permissions to create files in the DB directory
Check:
- the daemon is already running
- the TUI is pointed at the same config file
- the daemon has produced at least one tick
- the log file exists next to the socket
Check:
perpbot backfillhas been run- the configured
primary_timeframeexists in the DB history_daysand timeframe are sufficient to satisfy indicator warmup
Check:
HL_PRIVATE_KEY- exchange connectivity
- leverage configuration for the selected symbols
- whether reconciliation surfaced inconsistent local state
Check:
- network stability on the host
- whether the Hyperliquid WebSocket feed is reachable
- daemon logs around reconnect attempts
The bot will try to reconnect automatically, but repeated staleness should be treated as an operational issue.