Skip to content

tradectl-lab

tradectl-lab is a local dashboard that bundles the backtest, sweep, and live-monitor experience into a single binary. It runs entirely on your machine — no platform account required — and serves a Next.js frontend over an Axum API on port 9200.

Coming soon: tradectl lab

The lab will become a CLI subcommand of the main tradectl binary. After the next release you'll be able to launch it with:

bash
tradectl lab          # starts the lab on http://localhost:9200

Until then, build and run the standalone binary as described below.

What it gives you

The lab is structured around four pages, all reading from the same SQLite database (~/.tradectl/lab.db).

PagePurpose
DashboardStats and recent activity across backtests, sweeps, and saved configs
BacktestSingle-run UI — pick a strategy + data file, set params, see equity curve, drawdown, and trade log
OptimizeParameter sweeps — define ranges, see variant count estimate, watch ranked results stream in via SSE
DataBrowse ~/.tradectl/data/, preview ticker/trade files, prepare raw logs into the optimized binary format
ConfigsSaved param sets (CRUD) — quick-link to backtest or optimize
ResultsHistory of all backtests and sweeps, with drill-down into trades and equity points
MonitorLive bot dashboard — works for paper or live runs

How /monitor works

The Monitor page is a WebSocket client that connects directly to the bot's MonitorBroadcaster on port 9100 (default). The bot streams ticker snapshots, fills, position state, and (when shadow optimization is enabled) per-variant summaries. The lab backend is not in the loop — the page talks to the bot directly.

This means a single lab install can monitor any bot you have running:

  • Local paper-trading bot — open http://localhost:9200/monitor
  • Local live bot — same URL, the wire format is identical
  • Remote bot — change the URL in the page's "Bot URL" field to point at the remote MonitorBroadcaster

Plugin discovery

The lab scans ~/.tradectl/lib/ on startup for .so / .dylib strategy plugins. Each candidate is probed by spawning a subprocess (tradectl-lab --probe <path>) so a plugin that panics during initialization doesn't crash the lab. The probe loads the plugin, calls params_schema(), and outputs the schema as JSON.

Sweep execution

Sweeps run in two modes:

  • Blocking (POST /api/sweep) — the lab spawns a subprocess (tradectl-lab --sweep) that loads the plugin, generates the Cartesian product of parameter ranges, runs the batch strategy, and writes results back. Subprocess isolation means a panic in the strategy doesn't take down the lab.
  • SSE streaming (POST /api/sweep/stream) — same scoring and DB pipeline, but runs in-process so the UI can stream phase updates over Server-Sent Events.

Variants are ranked by compute_score(pnl%, trade_count, max_dd%, min_trades).

Database

A single SQLite file at ~/.tradectl/lab.db (WAL mode), shared with the CLI. Running tradectl backtest or tradectl sweep writes into the same database, so anything you run from the terminal shows up in the lab UI immediately.

TableHolds
backtestsSingle-run jobs (strategy, data file, params JSON, all metrics)
tradesIndividual fills (polymorphic FK to backtest or sweep_result)
equity_pointsBalance over time (polymorphic FK)
sweepsSweep jobs (config JSON: fixed + ranges, variant counts)
sweep_resultsPer-variant results (params JSON, metrics, score, rank)
configsSaved parameter sets
benchmarksReserved for future use

Build & run (standalone)

Until tradectl lab ships, the lab is a standalone binary. The web frontend is a static export served by the Rust backend.

bash
cd lab
cargo build --release                     # backend
cd web && npm install && npm run build && cd ..   # frontend (static export to web/out/)
./target/release/tradectl-lab             # serves on http://localhost:9200

Override the port with LAB_PORT:

bash
LAB_PORT=9300 ./target/release/tradectl-lab

Relationship to other components

tradectl backtest / tradectl sweep            tradectl run (paper or live)
        │                                            │
        ▼                                            ▼
~/.tradectl/lab.db ◄── shared SQLite ──            MonitorBroadcaster :9100
        ▲                                            ▲
        │                                            │
       lab UI ──────────  Axum API :9200  ────► /monitor (WS client)

The lab UI reads everything that the CLI writes. The /monitor page bypasses the database entirely and connects to the bot's broadcaster directly.

Notes & gotchas

  • No tradectl lab yet — coming in the next CLI release. For now, run the binary directly from lab/target/release/.
  • Plugin filenames — Cargo converts dashes to underscores for cdylib output (shotlibshot.so). The lab handles the mapping.
  • Plugin panics are isolated — the probing subprocess exits, and the strategy is excluded from the dropdown but the lab keeps running.
  • Static export frontend — the Next.js bundle is built once with next build && next export and served from web/out/. Changes to React code require a rebuild.
  • Shared DB locking — both lab and CLI use SQLite WAL mode; concurrent reads are fine. Don't open lab.db in another writer (e.g., a third-party SQLite GUI in write mode) while the lab is running.

tradectl — Automate Crypto Trading