Systems project · May 2026
QuantLab
A self-hosted research platform for reproducible portfolio backtesting and risk analytics, with background jobs on Redis/RQ workers.
- Role
- Sole author
- Status
- Release candidate for private, self-hosted use
- Stack
- Python, FastAPI, PostgreSQL, Redis / RQ, Docker, GitHub Actions
- Source
- OHLCV records indexed
- 1M+
- bundled data is synthetic
- Redis/RQ workers
- 4–8
- jobs from 30 seconds to 10 minutes
- API endpoints
- 18
- validated with automated tests in CI
The problem
Backtests are easy to write and easy to get quietly wrong: a signal peeks at tomorrow's price, the universe only holds companies that survived, or nobody can regenerate last month's result.
QuantLab treats each of those failures as an engineering requirement with a test, and runs every long computation as a tracked background job.
Architecture
QuantLab request and job flow
The browser talks to a Next.js front end, which proxies API calls to FastAPI. Routes never run simulations: they record a job in PostgreSQL and enqueue only its id on Redis. RQ workers run the domain library, read versioned Parquet datasets through DuckDB, and write results back to PostgreSQL. A reaper redelivers jobs whose worker died.
- Browser to Next.js web
- Next.js web to FastAPI (/api/*)
- FastAPI to PostgreSQL (record job)
- FastAPI to Redis (enqueue id)
- Redis to RQ workers (deliver)
- RQ workers to Parquet versions (read)
- RQ workers to PostgreSQL (results)
- Reaper to PostgreSQL (scan)
- The domain library imports no web or database code, so every numerical routine is testable in isolation.
- Redis only delivers job ids. Job state lives in PostgreSQL with database-time leases and fencing tokens, so a stale worker cannot overwrite a newer attempt.
- Every result carries a manifest: dataset version, engine, input hash and output fingerprint.
Decisions and trade-offs
Routes never compute
HTTP handlers validate, record a job and enqueue its id; workers do the work.
Trade-off: Every analysis becomes asynchronous, but the API stays responsive and long jobs survive a web restart.
Undefined is reported as undefined
Ratios that cannot be computed are shown as undefined with a reason, never printed as zero.
Trade-off: The output is less tidy, and much harder to misread.
Content-addressed datasets
Market data versions are immutable Parquet files addressed by content.
Trade-off: Storage grows with each version, and any result can be regenerated exactly from its manifest.
How it is tested
- An independent reference engine, written from the methodology without importing the engine, must agree to 1e-9 on 50 seeded synthetic universes.
- Mutation tests inject 36 known defects, such as a one-bar look-ahead or a delisting a day early; the suites must catch each one.
- Integration tests run against real PostgreSQL and Redis, and Playwright end-to-end tests include an axe accessibility audit.
Outcomes
- Backtests compute volatility, beta, Sharpe ratio, drawdown, value at risk and expected shortfall, with a research integrity report on each experiment.
- The release candidate was verified from a fresh clone.
Scope: QuantLab is research infrastructure, not a trading bot. All bundled data is synthetic, results are never investment advice, and it is not publicly deployed.