Finance Agent
5 LLM personas argue every trade idea. A deterministic judge decides what's worth telling me. The agent never places an order.
Why this exists
Every morning I want 1 honest answer for a stock I hold: is anything worth paying attention to today, and if so, what’s the actual evidence behind it. Reading every 10-K, earnings call, options chain, and price move myself doesn’t scale, and a single-model summary tends to agree with whatever it’s shown. Finance Agent is a long-running agent that reads the real data, argues both sides of the trade with 5 separate personas, gates the result through a deterministic rulebook and an LLM judge, and pushes a grounded briefing to my phone. It never places a trade. I do that by hand, at my actual brokerage, after reading the briefing.
Architecture
Four views: the always-on loop that decides when to think, the five-persona debate that does the thinking, the research pipeline that feeds it, and the safety-and-ops layer that makes an autonomous agent on a home server trustworthy.
The always-on loop: deciding when to act
The daemon’s core skill is restraint. systemd supervises it like a real service: watchdog pings from a task that never awaits LLM work, so a hung model call can’t fake liveness, plus API keys via LoadCredential, memory caps, and automatic restart. Every 15-minute tick decides whether now is worth spending money on. Sessions only open inside weekday research windows (with cooldowns) or when a fresh research signal lands outside them, and a daily $2 budget cap ends the day with a clean exit that a morning timer undoes. This loop is also where the “looks alive, does nothing” bug lived: the agent ran healthily for days while a stub trigger never fired, which is why the trigger now has deterministic tests for every window, weekday, cooldown, and daylight-saving edge.
The debate: five personas, two gates
Evidence flows one direction. The pool is fetched once per session (six parallel fetches, cache-first, with the safety-state fetch failing closed) and frozen into a typed structure whose serialization is byte-identical across personas, because timestamp drift between five prompts was quietly collapsing the prompt-cache hit rate. The five personas argue in parallel under a 120-second timeout, each slot independently routable to a different provider (that routing is how an Anthropic-specific outage got diagnosed in one run), with a drill-in tool that checks the pool first and only makes a capped live call for claims the pool can’t answer. Then two gates that don’t share a failure mode: a deterministic synthesizer that applies a quorum vote (three-plus strong opinions make a strong call, and the risk persona holds a veto that marks the result contested), and a temperature-zero LLM judge whose borderline scores get rerun three times and majority-voted. Only a briefing that clears both reaches the phone.
The research pipeline: RAG without a vector store
The retrieval layer is deliberately not a vector database. Every ingested document (filings, earnings-call transcripts, podcast episodes, analyst newsletters, 13F filings) gets read once by Claude and reduced to typed research signals: a fact, an inference, or a sentiment, with a confidence score, linked to the company and the source document. Personas then query structured signals instead of embedding-similarity chunks, which keeps retrieval explainable (every citation traces to a document) and keeps the whole thing runnable on a single SQLite file on a home server. Fresh signals landing outside research windows are also what the trigger’s signal-boost pass reacts to.
Safety and operations on a $2/day budget
The agent’s entire job is producing research briefings, never placing trades, and that boundary is enforced three ways that don’t share a failure mode: a static AST test that fails the build if order-placing SDK methods are even imported, a runtime allowlist that rejects any non-read-only tool name before dispatch, and a sandboxed document-read path hardened against symlinks, traversal, and oversized files. The ops side is sized to the machine it runs on: Langfuse is the source of truth for spend (adopted after a local token tracker under-counted a runaway day), the $2 daily budget prices every call against a per-provider table and exits cleanly at the cap, and everything the agent fetches and decides lands in an append-only audit log a single health command can summarize.
What shipped
By the final spec the agent had closed out the original “make it useful” backlog. Multi-provider routing means an Anthropic outage can’t take the whole system dark. The evidence pipeline replaced an earlier sprint’s stub data with real filings, prices, and options. The idle-poll trigger fixed an agent that ran forever without ever deciding to act. A daily portfolio snapshot rounds out the picture for trend review. 17 specs, each with its own spec, plan, tasks, and research trail, drove the build end to end with Claude Code.
The system has only ever run on paper-trading credentials. That’s a deliberate constraint, not a placeholder: the whole point was to prove the reasoning pipeline before ever touching a real order.