Skip to content

Architecture

Pankaj Negi edited this page Aug 16, 2026 · 1 revision

Architecture

Two mechanisms do the actual work of making a recommendation trustworthy: the agent pipeline that produces it, and the privacy boundary that decides what any of them are allowed to see outside your machine. This page covers both at the level that matters for deciding whether to trust the output — for the full module-by-module breakdown, see docs/ARCHITECTURE.md in the repository.

The review pipeline

flowchart TD
    H["holdings.csv + investment_policy.md"] --> A1[market-analyst]
    H --> A2[portfolio-auditor]
    H --> A3[stock-researcher]
    H --> A4[mf-analyst]
    H --> A5[tax-strategist]
    H --> A6[risk-officer]
    A1 --> D["devil's-advocate<br/>adversarial review"]
    A2 --> D
    A3 --> D
    A4 --> D
    A5 --> D
    A6 --> D
    D --> G{"validator gate<br/>cites a real tool call?"}
    G -- yes --> R["reports/*.md"]
    G -- no --> X[rejected]
Loading
  • Six specialists run in parallel, each with its own tools and its own angle on the same portfolio — market-analyst (consensus, trend), portfolio-auditor (policy breaches), stock-researcher (fundamentals), mf-analyst (mutual funds), tax-strategist (STCG/LTCG impact), and risk-officer (concentration, correlation).
  • devil's-advocate reviews every finding adversarially before any of it is allowed near the report — this is the step that catches a specialist's own overconfidence, not just factual errors.
  • The validator gate is deterministic code, not a model. It reads the actual tool-call log from that run and rejects any claim citing a number that isn't in it. A recommendation that survives adversarial review can still be rejected here if it can't produce a receipt.
  • The LLM never computes a number. Weights, returns, tax, correlations, risk — all deterministic Python (mybroker/portfolio/), same result every time. Agents reason and call tools; they never do arithmetic.

What leaves your machine

flowchart LR
    subgraph M["Your machine"]
        HC["holdings.csv"]
        POL["investment_policy.md"]
        MEM["memory/ + reports/"]
        FF["factfolio process"]
        HC --> FF
        POL --> FF
        FF --> MEM
    end
    subgraph E["External services"]
        MD["NSE / BSE / yfinance"]
        CL["claude CLI<br/>(report / chat only)"]
    end
    FF -- "ticker symbol only" --> MD
    MD -- "prices, fundamentals" --> FF
    FF -- "question + evidence" --> CL
    CL -- "analysis" --> FF
Loading

Only two things ever cross the boundary:

  1. Ticker symbols out, prices/fundamentals back — to NSE/BSE/yfinance, for every command that touches market data.
  2. A question plus its supporting evidence out, analysis back — to the claude CLI you already control, and only when you explicitly run report or chat.

Your holdings, quantities, and ₹ values never appear in either of those. No packaging choice, install method, or command changes that — it's enforced by what the data provider clients are given (mybroker/data/*_provider.py), not a policy anyone has to remember to follow.

Why an executable can still need internet + a login

The standalone builds (factfolio-macos-arm64, factfolio-windows-x86_64.exe, factfolio-linux-x86_64) bundle the Python interpreter and every dependency — nothing else to install. They do not remove the need for the separate claude CLI (claude login or ANTHROPIC_API_KEY) for report/chat — no packaging choice changes that, since factfolio shells out to it rather than embedding it. See FAQ for what that CLI itself does and doesn't see.

Clone this wiki locally