This runbook verifies the local EX3 stack: FastAPI API, Redis, worker, AI sidecar, SQLite persistence, tests, Schemathesis, and the Streamlit dashboard.
docker compose up starts:
- FastAPI API at
http://127.0.0.1:8000 - Redis at
localhost:6379 - one-shot seed service that initializes the demo account and sample watchlist
- background worker using the same SQLite volume as the API
- AI sidecar at
http://localhost:8010 - Streamlit UI at
http://localhost:8501
Install dependencies:
uv sync --extra devValidate the local source tree:
uv run scripts/local_ci.shThe local CI script runs Python syntax checks, pytest, compose config validation, and Schemathesis when an API is already running. If the API is not running, it skips Schemathesis and prints the exact command to run later.
Start API, Redis, seed, worker, AI, and UI:
docker compose up --buildThe seed service runs automatically after the API is healthy. It exits successfully after creating the demo account and sample stocks. It is idempotent, so repeated compose starts do not duplicate data.
Open the app:
- Streamlit UI:
http://localhost:8501 - API docs:
http://127.0.0.1:8000/docs - AI health:
http://127.0.0.1:8010/health
Demo login:
demo@alphawatch.local / password123
Stop the stack:
docker compose downRemove the local compose database volume:
docker compose down -vCheck OpenAPI:
curl -fsS http://127.0.0.1:8000/openapi.json >/dev/nullCheck docs in a browser:
http://127.0.0.1:8000/docs
Show response headers:
curl -i http://127.0.0.1:8000/openapi.json | sed -n '1,20p'AlphaWatch does not currently implement request rate limiting, so no X-RateLimit-* headers are expected. If rate limiting is added later, this header check is where those headers should be verified.
curl -fsS http://127.0.0.1:8010/healthExpected output:
{"status":"ok"}The base demo does not require AI secrets. The AI service returns deterministic mock stock briefs.
docker compose exec redis redis-cli pingExpected output:
PONG
Watch logs:
docker compose logs -f workerYou should see a cycle summary like:
refresh cycle complete: symbols=3 refreshed=3 skipped=0 errors=0
If no stocks exist yet, run:
docker compose run --rm seedYou can also run one manual refresh and then watch the worker continue on its interval:
uv run python scripts/refresh.py AAPL MSFT --concurrency 2 --retries 2Open:
http://localhost:8501
Use the demo login after seeding:
demo@alphawatch.local / password123
On Stock Details, verify:
- the chart range selector changes between 1D, 5D, 1M, YTD, and 1Y
- the AI Brief section shows summary, sentiment, takeaways, and risks
uv run pytestThe tests mock Yahoo Finance and fake Redis where needed. They do not require real market network calls.
Schemathesis is included in the dev dependencies. It checks the live OpenAPI schema against the running API.
Start the compose stack first:
docker compose up --buildThen run:
uv run scripts/schemathesis.shThe script runs a small GET-focused Schemathesis smoke test against http://127.0.0.1:8000/openapi.json with an intentionally invalid bearer token. This keeps the check local and stable: protected routes should reject the request before Yahoo-backed endpoint logic runs, and Schemathesis verifies that those requests do not produce server errors.
For a local CI-style check:
uv run scripts/local_ci.shRecommended full verification flow:
uv sync --extra dev
docker compose up --build
uv run scripts/local_ci.shIn hosted CI, the equivalent steps would be:
uv sync --extra dev
uv run python -m compileall app scripts tests
uv run pytest
docker compose config
docker compose up -d --build
uv run scripts/schemathesis.sh
docker compose downRefresh all saved stocks:
uv run python scripts/refresh.pyRefresh selected symbols:
uv run python scripts/refresh.py AAPL MSFT --concurrency 2 --retries 2When running against compose Redis from the host, the default REDIS_URL=redis://localhost:6379/0 works.
Log in:
curl -X POST \
-d "username=demo@alphawatch.local&password=password123" \
http://127.0.0.1:8000/auth/loginUse the returned token:
curl -H "Authorization: Bearer TOKEN_HERE" \
http://127.0.0.1:8000/reports/weekly