Up and running, fast.
Install ParadoxLab, connect data providers, plug in AI agents and understand how data flows. This covers everything you need to self-host the terminal.
Quick start
One command does it. The installer detects your OS, writes a single .env, generates secrets, seeds an admin account and starts the terminal. It uses Docker if you have it, and local Python and Node otherwise.
$ git clone https://github.com/AnandDamdiyal/ParadoxLab.git $ cd ParadoxLab $ ./install.sh # Windows: ./install.ps1 ✓ ParadoxLab is ready → http://localhost:8000 email: admin@paradoxlab.local password: <generated>
The credentials are also saved in .env, so change the password after your first login. To force a mode, set OTUI_MODE=docker or OTUI_MODE=local.
Prerequisites
You need either Docker, or Python and Node for the local path. There's nothing else to configure.
| SOFTWARE | VERSION | NEEDED FOR |
|---|---|---|
| Docker | 20.10+ with Compose v2 | Recommended: the full stack in containers |
| Python | 3.11+ | Local backend |
| Node.js | 20+ | Local frontend |
| Hardware | 2 cores · 4 GB RAM · 2 GB disk | Minimum; 4 cores and 8 GB are recommended |
Docker image
Every release publishes a multi-arch image (linux/amd64 and linux/arm64) to GitHub Container Registry, tagged X.Y.Z, X.Y and latest. No clone needed.
$ docker run -p 8000:8000 -v paradoxlab-data:/app/data ghcr.io/ananddamdiyal/paradoxlab:latest ✓ http://localhost:8000
Pass keys with -e FMP_API_KEY=… or --env-file .env. The volume keeps the SQLite database across upgrades. Without Redis, the quote bus automatically falls back to in-memory.
Docker Compose
$ cp .env.example .env # add API keys if you have them $ docker compose up --build # backend + Redis, SQLite $ docker compose --profile postgres up --build # with PostgreSQL 16 $ docker compose down -v # stop and wipe the database
The backend container serves the UI and API at http://127.0.0.1:8000. Compose maps host.docker.internal so the container can reach LM Studio on your host.
Local development
$ python3.11 -m venv .venv $ source .venv/bin/activate $ pip install -r backend/requirements.txt $ PYTHONPATH=. uvicorn backend.main:app --reload
$ cd frontend $ npm ci $ npm run dev → http://127.0.0.1:5173
Run every check before opening a pull request with make gate.
API keys & providers
Every key is optional, and the terminal runs on free sources out of the box. You can add keys from the browser or the terminal.
In the browser (admins)
Settings → Data Providers shows what each provider unlocks, along with its live status and last error. Set keys writes the same .env the installer uses, applies values live where possible and lists anything that needs a restart. Test runs the real probe. Placeholder values such as your-api-key don't count as configured.
From the terminal
$ make keys # or ./scripts/setup-keys.sh: guided, shows what each key unlocks
Provider waterfall
| PROVIDER | MARKET | PROVIDES |
|---|---|---|
| Zerodha Kite | India | Live ticks, OHLCV at all intervals, holdings and positions import |
| NSEPython | India | F&O chains, OI, PCR, corporate actions |
| FMP | US | OHLCV, fundamentals, earnings, news |
| Finnhub | US | WebSocket ticks, OHLCV, news |
| Alpaca | US | Quotes |
| Yahoo Finance | Global | Fallback OHLCV and quotes, no key needed |
| FRED · FXMacroData | Macro | Policy rates, CPI, GDP, release calendar; FXMacroData needs no key |
Environment
| VARIABLE | PURPOSE |
|---|---|
FMP_API_KEY | US equities, fundamentals, earnings |
FINNHUB_API_KEY | US real-time WebSocket ticks |
KITE_API_KEY · _SECRET · _ACCESS_TOKEN | Zerodha Kite for Indian live and historical data (the access token is daily) |
OPENROUTER_API_KEY | AI research agent; free :free models work |
LM_STUDIO_BASE_URL · _MODEL | Local model for news sentiment and the agent |
DATABASE_URL · REDIS_URL | SQLite by default; PostgreSQL and Redis are optional |
JWT_SECRET_KEY · CACHE_SIGNING_KEY | Generated by the installer |
BOOTSTRAP_ADMIN_EMAIL · _PASSWORD | First-run admin; skipped once any user exists |
The full list is in the README.
Provenance
Every security snapshot carries {source, quality, as_of, latency_ms, note}. The UI renders it as a chip next to the value:
| CHIP | MEANING |
|---|---|
| LIVE · KITE | Served by a live feed |
| DELAYED · YAHOO | Exchange-delayed |
| CACHED · FMP 4m | From cache, with its age |
| SYNTHETIC | Generated, and never used as a price basis |
| NO DATA | No provider could fill it |
Provider health comes from GET /api/providers/status, which runs real probes. An expired Kite token reads down.
MCP & agents
The agent's tool registry is exposed over the Model Context Protocol, so Claude Code, Claude Desktop or any other MCP client can use the terminal.
1 · Create an API key
In Settings → API Keys, create an otui_ key with read permission (38 read-only tools) or read_write permission (which adds 7 propose_* tools). The key maps to your user, so tools see your portfolio, watchlists and alerts.
2 · Connect
GET /api/mcp/manifest # tools, resources, prompts
GET /api/mcp/tools
POST /api/mcp/tools/{name}
X-API-Key: otui_…For a local stdio session, run python -m backend.mcp from the repo root. It's anonymous, so only market-data tools are available.
propose_* call creates a pending row that a human confirms in the app. Nothing places real orders, and trading is paper only.Every result is returned as {ok, data, provenance}. Five prompts ship with the server: morning_brief, position_review, screen_to_thesis, risk_check and idea_to_backtest.
Caching & failover
| LAYER | BEHAVIOUR |
|---|---|
| L1 SQLite | Per symbol and interval, default TTL 900 s |
| L2 Redis | Optional via REDIS_URL; also handles quote pub/sub fan-out |
| Live candles | WebSocket ticks update bars in memory without touching the cache |
| Prefetch | PARADOXLAB_PREFETCH_ENABLED=1 warms the cache |