This document describes how the main parts of perp-bot fit together and which source files own each responsibility.
perp-bot has two main execution styles:
- one-shot commands such as
backfill,backtest,review, andcompare - the long-running
tradedaemon, which can be observed throughstatusandtui
The trading daemon uses:
- SQLite for persistent historical data, trades, and small pieces of durable bot state
- a Unix socket for volatile runtime state and control commands
- JSON logs to stdout and an optional rotating file for the TUI log pane
Owns CLI argument parsing and command dispatch. It is also the composition root for the trading loop, backtests, and reporting commands.
Key responsibilities:
- load config
- instantiate the database, clients, executors, and risk/signal engines
- run the main trading tick loop
- expose
statusandtui
Defines the typed dataclass config model and loads:
config.yaml.envin the working directory, or next to the explicit--configpath
The config loader is a central dependency for almost every command.
Owns exchange and prediction market data access.
Important files:
client.py: Hyperliquid REST wrapperws_client.py: WebSocket wrapper with mid-price cache and reconnect logicdb.py: SQLite schema and persistence APIingest.py: orchestration for full backfill and incremental updatesprediction_client.py: prediction market adapters
Owns indicator calculation and signal evaluation.
Important files:
indicators.py: Z-score, Bollinger Bands, RSI, ADX, Hurst exponentengine.py: converts indicator state intoLONG,SHORT,CLOSE, orNONEprediction.py: prediction-market-derived regime classification
The backtest and live trading loop intentionally reuse this logic.
Owns runtime risk checks and position sizing.
Important responsibilities:
- max open positions
- daily realized loss limit
- cooldown after stop loss
- position timeout
- regime-adjusted position sizing
The backtest has a separate adapter in backtest/risk_adapter.py so the simulation can mirror live behavior.
Owns order execution.
executor.py: abstract interface plusPaperExecutorlive_executor.py: Hyperliquid order placement, leverage setup, stop-loss attachment, slippage tracking, and exchange reconciliation helpers
This separation keeps the trading loop mostly mode-agnostic.
Owns historical simulation and analysis.
Important files:
engine.py: row-by-row backtest driverexecutor.py: simulated trade executorcost_model.py: fees, slippage, fundingmetrics.py: performance metricswalk_forward.py: train/test window analysissensitivity.py: parameter sweepsresults.py: backtest result model and summaries
Owns daemon state sharing and control.
state.py: thread-safe daemon state containerserver.py: Unix socket state serverclient.py: socket client used bystatusand TUI actionsprotocol.py: command names and socket path helper
Owns the Textual dashboard.
The TUI attaches to a running daemon, reads volatile state over IPC, reads trades from SQLite in read-only mode, and tails the rotating log file.
Owns derived reporting on top of stored trades and backtests.
weekly.py: weekly performance reviewcompare.py: paper-vs-backtest comparison
Shared operational utilities:
logging.py: JSON logginghealth.py: periodic heartbeat alertsalerts.py: Discord and Telegram integration
Command: perpbot backfill
- Load config and open the database.
- Create
HyperliquidClientandDataIngestor. - For each configured symbol, backfill candles for every configured timeframe.
- Backfill funding history.
- Persist rows into SQLite with
INSERT OR IGNORE.
Command: perpbot trade
- Load config and open the database.
- Create the data ingestor, signal engine, risk manager, and executor.
- In live mode, validate
HL_PRIVATE_KEY, set exchange leverage per symbol, and reconcile exchange state with local DB state. - Start WebSocket mid-price subscription.
- Start the IPC server for
statusandtui. - Enter the candle-aligned main loop.
Each trading tick performs roughly this sequence:
- Reconnect WebSocket if the price feed is stale.
- Poll prediction markets on schedule and compute regime state.
- Incrementally update candles for each symbol.
- Load the latest candle window from SQLite.
- Compute indicators.
- Check position-level exits first, including capital stop loss and position timeout.
- Evaluate signal-engine exits or entries.
- Run pre-entry risk checks before opening anything new.
- Update daemon state for the IPC/TUI layer.
- Emit heartbeat alerts and sleep until the next candle boundary.
Command: perpbot backtest
- Load historical candles and funding history from SQLite.
- Compute indicators once across the full frame.
- Simulate the strategy row by row after the warmup window.
- Apply fee, slippage, funding, entry-delay, and cancel-if-signal-gone rules.
- Produce a
BacktestResultwith trades, equity curve, and metrics.
The backtest deliberately shares signal logic with live trading to reduce divergence.
Commands: perpbot status, perpbot tui
statusis a simple socket client that prints the daemon snapshot as JSON.tuiattaches to the same socket, reads open and recent trades directly from SQLite, and tails the daemon log file.
This split keeps the socket payload small while letting SQLite remain the source of truth for historical trade data.
The SQLite database currently contains these tables:
candlesfunding_ratesprediction_snapshotstradesbot_state
Notable usage:
candlesandfunding_ratespower both live evaluation and backtestsprediction_snapshotsdecouples regime polling from signal evaluationtradesis the primary audit trail for paper and live executionbot_statestores small durable flags such as stop-loss cooldown timestamps
Given data.db_path, the process derives:
- SQLite DB: configured directly, for example
perp_bot.db - Unix socket:
<db directory>/perp-bot.sock - rotating log file for TUI:
<db directory>/perp-bot.log
Keeping these together makes it possible for status and tui to locate the daemon from the same config.
Important runtime controls implemented in code:
- max positions
- daily realized loss cap
- cooldown after stop loss
- 24-hour position timeout
- position-size reduction in high-risk prediction regimes
- new-entry block in crisis regimes
- startup halt after three consecutive losing weeks unless
--forceis used - live position reconciliation before the losing-weeks halt is enforced
Common places to extend the system:
- add a new report in
perp_bot.reporting - add a new CLI command in
perp_bot.cli - add new indicators in
perp_bot.signals.indicators - add a new prediction source behind
prediction_client.py - add new TUI widgets under
perp_bot.tui.widgets
When extending behavior, keep live and backtest logic aligned wherever possible. The current design intentionally shares signal semantics across both modes.