quantjobs
# QuantJobs
A terminal app for hunting **quant** roles and managing a tailored LaTeX CV — with a
Claude **MCP server** so you can drive the whole thing in conversation.
It does four things:
1. **Finds jobs** — scrapes stable public job boards (Greenhouse/Lever) for London quant
roles, *or* lets Claude web-search and ingest jobs for you via MCP.
2. **Scores & curates** — a fit score (0–100) per job, bucketed into **Strong match /
Stretch / Long shot**, plus star ★ and archive. The scorer is deliberately optimistic
about skills you're *learning*, so genuine stretch roles surface instead of being filtered.
3. **Builds your CV** — generates a LaTeX → PDF CV from a single source of truth
(`profile.yaml`), either as your base CV or **tailored to a specific job**. Tailoring only
*reorders and selects your real experience* — it never invents anything. If a job wants a
skill you don't have, it adds a **pending project** (with an honest time estimate) and
**flags the skill to learn** instead of fabricating a bullet.
4. **Spots trends** — aggregates skill demand across the jobs you're tracking and flags the
in-demand skills you're missing (e.g. "C++ in 20% of roles — learn it").

---
## Setup
Prereqs: [`uv`](https://docs.astral.sh/uv/) and a LaTeX engine (`latexmk`/`xelatex`, e.g. MacTeX
or BasicTeX — already on this machine).
```bash
uv sync
uv run quantjobs init # create the DB + seed your CV profile
uv run quantjobs scrape # pull London quant jobs from the configured firms
uv run quantjobs # launch the TUI
```
Data (DB, your editable `profile.yaml`, generated CVs) lives in `./data/` by default.
Override with `QUANTJOBS_HOME=/some/path`.
---
## The TUI
`uv run quantjobs` opens the dashboard. Left = job list, right = detail (fit %, the skills you
*have* vs *miss*, and the job description).
| Key | Action | Key | Action |
|-----|-------------------------------------|-----|---------------------------------|
| `/` | search (Enter apply, Esc clear) | `t` | skill **trends** screen |
| `s` | star / unstar | `p` | **pending projects** screen |
| `a` | archive | `k` | skills **gap** screen |
| `g` | generate tailored CV for this job | `y` | your skills profile |
| `o` | open the last generated CV (PDF) | `f` | cycle filter (active/★/strong/stretch/all) |
| `R` | run the scrapers | `r` | reload · `q` quit |
---
## CV management
Your CV is defined entirely by **`data/profile.yaml`** (seeded from your current CV). Edit it to
change anything. The LaTeX template is `quantjobs/cv/template.tex` — drop your own `.tex` into
`data/template.tex` to override it.
```bash
uv run quantjobs cv --base # your standard CV
uv run quantjobs cv --job 42 --stretch aggressive --open
```
`--stretch` (`conservative` | `balanced` | `aggressive`) controls how hard the tailoring leans
into reach roles: it reorders bullets/skills to surface what the job asks for, and in
`aggressive` mode selects the strongest few bullets per role. **It never adds experience you
don't have** — missing skills become pending projects + flags. Generated CVs land in
`data/output/`. The template is a dense one-page modern-sans design (Helvetica Neue via xelatex).
**Building a CV is best done as a conversation through the MCP** (below): Claude drafts a tailored
CV for a job, you refine the bullets/order/summary together, then render — see `cv_draft_*` tools.
---
## Adding job sources
Edit `quantjobs/scrapers/firms.yaml` (or `data/firms.yaml` to override). A firm's Greenhouse/Lever
token is the slug in its careers URL:
```yaml
location_filter: London # applied to every firm; set "" for all locations
firms:
- {name: Jane Street, type: greenhouse, token: janestreet}
- {name: DRW, type: greenhouse, token: drweng}
- {name: Some Fund, type: lever, token: somefund}
```
```bash
uv run quantjobs scrape --list # show configured sources
uv run quantjobs scrape --source "Jane Street" # one firm
uv run quantjobs trends # skill demand + flags to learn
uv run quantjobs gap # missing skills + suggested projects
```
eFinancialCareers HTML scraping is included as a best-effort fallback (`--source efc`) but the
robust paths are the JSON boards above and Claude-driven ingestion below.
---
## Talk to it: the Claude MCP server
Register the server so Claude can search, analyse and act on your job DB in conversation:
```bash
claude mcp add quantjobs -- uv run --directory "$(pwd)" quantjobs-mcp
```
<details><summary>Claude Desktop (JSON) config</summary>
```json
{
"mcpServers": {
"quantjobs": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/quantjobs", "quantjobs-mcp"]
}
}
}
```
Note: Claude Desktop may not inherit your shell `PATH`; if CV compilation fails, ensure
`/Library/TeX/texbin` is on PATH for the server process.
</details>
Then just ask Claude things like:
- *"Find London quant-developer roles and add them"* → web search → `add_jobs`
- *"What in-demand skills am I missing?"* → `analyze_trends` + `skills_gap_report`
- *"Which firms are hiring for C++?"* → `run_query` (read-only SQL over the DB)
- *"Star 42 and add a pending project to learn kdb+"* → `star_job` + `add_pending_project`
- *"I finished the order-book project — mark C++ as a skill I have"* → `add_or_update_skill`
**Build a CV conversationally** (the recommended way):
> *"Build my CV for job 107"* → `cv_draft_create` (Claude shows the proposed summary, the
> selected/reordered bullets, and the skills) → *"tighten the second UBS bullet and lead with the
> ML one; drop the Excel bullet"* → `cv_draft_update` (re-shows) → *"render it"* → `cv_draft_render`
> → PDF. Drafts persist between messages, so you can iterate. Claude only rephrases your **real**
> experience (same facts/metrics) — it never invents anything.
**29 tools** are exposed. `run_query` is restricted to a single read-only `SELECT`/`WITH` on a
read-only connection, so Claude can freely explore patterns without being able to mutate data
except through the explicit, audited tools.
Useful tables for `run_query`: `jobs`, `job_skills(job_id, skill)`, `skills_profile`,
`pending_projects`, `skill_flags`, `cv_versions`, `saved_searches`.
---
## Development
```bash
uv run pytest # test suite
uv run ruff check . # lint
```
Layout: `core` data layer (`db`, `repo`, `models`, `ingest`, `fit`), `cv/` (template +
generator + compile), `scrapers/`, `analysis/` (trends, skills gap), `tui/`, `mcp_server/`.
TDQS
Scored across 32 tools
Each tool has a clearly distinct purpose, with no overlapping functionality. For example, CV drafting tools are separated by action (create, update, get, list, render), and job tools cover distinct operations (add, search, get, update, archive, star). Even similar tools like analyze_trends and skills_gap_report target different aspects (demand vs. personal gaps).
All tool names follow a consistent verb_noun pattern (e.g., add_jobs, get_job, analyze_trends, cv_draft_create). The naming is predictable and uses underscores uniformly, with no mixing of conventions like camelCase. Even longer names like add_or_update_skill adhere to this pattern.
With 32 tools, the count is high for a typical MCP server, which often has 3-15 tools. However, the server covers a broad domain (quant job search, CV drafting, skill tracking, and analysis), and each tool seems justified. It borders on being too heavy but is still manageable given the comprehensive scope.
The tool set covers the full lifecycle of quant job hunting and CV management: job discovery (add, search, get, archive, star), skills tracking (add, analyze, gap report), CV drafting (create, update, get, list, render, reframe, notes), project management, settings, and ad-hoc analysis. No obvious gaps are apparent; even edge cases like job status updates and skill flags are handled.