Skip to content

Repository files navigation

hodlin

Market-context intelligence with a human approval gate. The recommend domain ingests price bars and news, flags statistically unusual moves with a rolling z-score, explains each one with a single LLM call over structured evidence, and delivers the alert to Telegram. It holds no keys and moves no money — a separate execute domain (M2) owns that authority, reachable only through a signed proposal. Not financial advice, but HODL.

Run it

You need Docker, and — for the live explanation and delivery — an Anthropic API key and a Telegram bot token (from @BotFather) plus your numeric chat id (from @userinfobot).

cp .env.example .env      # fill in ANTHROPIC_API_KEY and the TELEGRAM_* values
docker compose run --rm app python -m hodlin_recommend.demo

That one command starts Postgres, applies migrations, and runs the pipeline once end-to-end — backfill → explain → notify, printing each step — so the anomaly lands in your Telegram chat without waiting for a scheduler tick.

In the default demo mode, prices come from a committed seed CSV and news is skipped, so only Anthropic and Telegram need real credentials (the Finnhub/Massive placeholders can stay as-is). The first run downloads the FinBERT model (~440 MB, cached in a volume afterward), so give it a minute.

To run it as the real service instead — scheduler, API, and Telegram poller:

docker compose up         # builds the image, applies migrations, starts serving

It backfills the seed bars on startup, detects the demo anomaly (a sharp late-June BTC drop), then explains and delivers it on its own ticks — a few minutes, since explain runs every 5. Once http://localhost:8000/health/ready returns 200, the pipeline is live. Note that an anomaly notifies exactly once, so whichever of the two paths you run first is the one that delivers the alert.

Develop

The project is a uv workspace of three packages — contracts (shared frozen types), recommend, and execute (M2 stub). The full local gate — ruff, mypy (strict), import-linter, and pytest (unit + integration against a real Postgres via testcontainers) — runs with:

uv sync
./scripts/check.sh

Integration tests need Docker for Postgres; they skip cleanly if it is unavailable, or point them at an existing database with HODLIN_TEST_DATABASE_URL (use the admin account — the isolation test provisions roles, and the migration tests create tables wherever they are aimed).

Each domain has its own env file: .env for recommend, .env.execute for execute (cp .env.execute.example .env.execute). That split is the point rather than tidiness — an env file is injected into a container whole, so a shared one would hand the recommend process the execute domain's database credential and undo the per-role isolation the two databases exist to provide.

Health

  • GET /health/live — the process is up (no dependencies checked).
  • GET /health/ready — the database answers and the scheduler is running.
  • POST /v1/sentiment — score one text with the served FinBERT model.

About

Not a financial advice, but HODL.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages