Documentation

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.

BASH
$ 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.

SOFTWAREVERSIONNEEDED FOR
Docker20.10+ with Compose v2Recommended: the full stack in containers
Python3.11+Local backend
Node.js20+Local frontend
Hardware2 cores · 4 GB RAM · 2 GB diskMinimum; 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.

BASH
$ 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

BASH
$ 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

BACKEND
$ python3.11 -m venv .venv
$ source .venv/bin/activate
$ pip install -r backend/requirements.txt
$ PYTHONPATH=. uvicorn backend.main:app --reload
FRONTEND
$ 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

BASH
$ make keys
# or ./scripts/setup-keys.sh: guided, shows what each key unlocks

Provider waterfall

PROVIDERMARKETPROVIDES
Zerodha KiteIndiaLive ticks, OHLCV at all intervals, holdings and positions import
NSEPythonIndiaF&O chains, OI, PCR, corporate actions
FMPUSOHLCV, fundamentals, earnings, news
FinnhubUSWebSocket ticks, OHLCV, news
AlpacaUSQuotes
Yahoo FinanceGlobalFallback OHLCV and quotes, no key needed
FRED · FXMacroDataMacroPolicy rates, CPI, GDP, release calendar; FXMacroData needs no key

Environment

VARIABLEPURPOSE
FMP_API_KEYUS equities, fundamentals, earnings
FINNHUB_API_KEYUS real-time WebSocket ticks
KITE_API_KEY · _SECRET · _ACCESS_TOKENZerodha Kite for Indian live and historical data (the access token is daily)
OPENROUTER_API_KEYAI research agent; free :free models work
LM_STUDIO_BASE_URL · _MODELLocal model for news sentiment and the agent
DATABASE_URL · REDIS_URLSQLite by default; PostgreSQL and Redis are optional
JWT_SECRET_KEY · CACHE_SIGNING_KEYGenerated by the installer
BOOTSTRAP_ADMIN_EMAIL · _PASSWORDFirst-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:

CHIPMEANING
LIVE · KITEServed by a live feed
DELAYED · YAHOOExchange-delayed
CACHED · FMP 4mFrom cache, with its age
SYNTHETICGenerated, and never used as a price basis
NO DATANo 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

HTTP
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.

Writes are proposals. A 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

request→L1 SQLite→L2 Redis→primary provider→fallback→503
LAYERBEHAVIOUR
L1 SQLitePer symbol and interval, default TTL 900 s
L2 RedisOptional via REDIS_URL; also handles quote pub/sub fan-out
Live candlesWebSocket ticks update bars in memory without touching the cache
PrefetchPARADOXLAB_PREFETCH_ENABLED=1 warms the cache

Keyboard

GO barCtrl G
Command paletteCtrl K
AI agentCtrl J
WorkspacesF1–F9
Chart timeframes1–7
Close panelEsc