OPEN SOURCE · SELF-HOSTED · ENGLISH & ESPAÑOL

Portfolio analytics where the AI shows its work.

Faro computes risk metrics — Sharpe, Value-at-Risk, beta, drawdown — from their published formulas, then an AI copilot explains them in plain English or Spanish. A checker compares the numbers in each answer with the engine's results and flags the ones it can't match. It skips everyday values like 1 or 100, and a wrong number very close to a real one can pass. The methodology page lists its limits.

Get it on GitHub See how grounding works

MIT licensed · No bank logins · No cloud required · Educational tool, not investment advice

97
unit tests on the quant engine
24/60
answers fully grounded with chat history on · 59/60 asked fresh (local 7B model, 3 runs, Sept 24, 2026)
EN·ES
fully bilingual, one tap
$0
to run — free data, local AI
MIT
open source, self-hosted
See it work

Real screens, real numbers

Not mockups — the actual app running the seeded demo portfolio.

Faro dashboard showing portfolio value, risk metric cards, performance vs SPY, allocation and a correlation matrix
The dashboard — value & P/L, Sharpe · VaR · beta · drawdown, performance vs SPY, allocation, drawdown and a correlation matrix. The copilot calls the same engine, so the chat and the charts can never disagree.
Why Faro

Serious analytics. Honest AI. Your machine.

Most AI finance tools ask you to trust a chatbot. Faro is built the other way around: deterministic math first, language model second, verification always.

🧮

First-principles quant engine

Every metric implemented from the documented formula and unit-tested against independently computed references — Sharpe, Sortino, alpha/beta, historical & parametric VaR, CVaR, max drawdown, correlations, concentration, risk contributions.

🔍

A copilot that shows its work

Ask "how risky am I?" or "what if NVDA drops 20%?" The agent is instructed to answer by calling the quant engine's tools. Every figure in its reply is checked against those computations, and numbers it can't trace are flagged.

🌎

Truly bilingual

Dashboard, copilot, scenarios, digests — everything works in English and Spanish with one tap. Ask in either language; get answers, number formatting and legal docs in yours.

⚡

Scenario lab & daily digest

Stress-test instant price shocks with per-position impact, and generate a one-click daily brief: movers, risk contributors, upcoming earnings — narrated from computed facts only.

🔒

Private & replicable

Your data lives in a local database on your machine. Clone → docker compose up → running in 5 minutes. Claude-powered when you add a key; free local model otherwise.

⚖️

Compliance-aware by design

Educational analytics, not advice. Faro never executes trades, never touches your brokerage, and is instructed to refuse "should I buy?" (a behavior exercised in the public eval) — showing you how to evaluate it yourself instead.

The core idea

Invented numbers aren't prompted away. They're caught and shown.

The #1 failure mode of LLM finance apps is invented numbers. Faro's answer is architectural — and it's measured, not assumed.

1

Deterministic engine underneath

Pure numpy/pandas implementations of every formula, documented and cross-checked against independent libraries in 97 tests.

quant/risk.py historical_var(returns, 0.95) → 0.0162 # unit-tested
2

The engine is the only approved source

The copilot is instructed to take every number from five typed tools that call the same service layer the dashboard uses: one engine, two consumers. Nothing forces it to, which is why the next step exists.

tools: get_metric get_position_detail run_price_shock_scenario compare_to_benchmark
3

Every reply is checked

A grounding checker extracts each number from the answer and matches it to this turn's tool results. Anything it can't trace is flagged in the app. It has blind spots, listed on the methodology page, and it's measured by a public two-mode bilingual eval with raw logs in the repo.

grounding_check: every number in the reply must trace to a tool result → flagged if not
Faro copilot in Spanish with an amber warning that three figures could not be traced to a tool result
Caught in the act. Asked its beta again mid-conversation, the local model recited “1.35” from earlier in the chat instead of re-calling the tool — and Faro flagged it in-app: “3 figures … could not be traced to a tool result.” That's the grounding checker working — here, in Spanish.

Honest by two measures, not one flattering number

The eval runs two ways, and the repo publishes both with raw logs. Asked fresh, each answer's numbers trace to that turn's tools. In free-flowing multi-turn chat a small local model will recite earlier figures from memory — and every one is flagged in-app rather than hidden. Whether Claude does better is untested.

59/60
answers fully grounded, asked fresh — local 7B model, 3 runs, Sept 24, 2026
24/60
answers fully grounded, with chat history on as the shipped chat works — same model, 3 runs, Sept 24, 2026
Workflow

From holdings to insight in four steps

No accounts, no brokerage linking — enter what you own and Faro does the math.

📋

Enter your holdings

Tickers, shares, cost basis. Data stays in a local SQLite file on your machine.

📈

Faro computes your risk profile

Free daily market data, cached on disk, feeds the full metrics suite — with graceful offline degradation.

💬

Ask the copilot anything

Streaming answers with visible tool-call chips, in your language, with every number cited from computations.

📰

Read your daily digest

Drivers, risk contributors and upcoming earnings for your holdings — generated on demand.

Under the hood

One engine, two consumers

The dashboard and the copilot read the same service layer, so the charts and the chat can never disagree — and the copilot's replies are grounding-checked on top.

React dashboard

React 18 · TypeScript · Tailwind · Recharts · i18next EN/ES

AI copilot

Claude primary ⇄ local Ollama fallback, swapped by one environment variable. Its only source of numbers: five typed tools.grounding checker on every reply
↓  REST + SSE  ↓

FastAPI service layer — the single computation path

One set of services answers both the dashboard's REST calls and the agent's tools.
↓

Quant engine

Pure numpy/pandas, every formula documented and unit-tested — no black-box quant libraries.

Market data

Free daily bars: yfinance with a Stooq fallback, cached on disk — keeps working from cache when providers are unreachable.
For hiring managers

How to evaluate this in five minutes

Built as a work sample. Here's where to look, and what each choice is meant to demonstrate.

The quant is from first principles

Every metric implemented from its documented formula in pure numpy/pandas — no black-box libraries — tested against hand-computed references plus independent cross-checks.

Read the engine →

The guardrail is code, not vibes

The grounding checker that extracts every number and matches it to a tool result — the mechanism that makes “detected and surfaced” a testable claim.

Read the checker →

The eval is public and honest

A two-mode bilingual spot-check with the iteration history (28 → 4 → 2) and raw logs committed — including the fabricated $12,345.67 the checker caught.

See the results →

It runs from a clean clone

97 unit tests, mypy-strict, CI green on every push, and clone → docker compose up → running in five minutes. Replication as a design constraint.

Browse the tests →

Running in five minutes

git clone https://github.com/CarlosM787/faro && docker compose up

That's the whole install. No API keys required — add ANTHROPIC_API_KEY later to switch the copilot to Claude with zero code changes.