Open source · Python · MIT

FinancialAdvisor

A decision system for one household's money. It reads your own statements, pulls dated evidence from public sources, and puts every money decision through the same gates before it will say yes.

Everything below is a real run against the synthetic household in the repository's test fixtures. It is nobody's balance sheet, and no figure here belongs to a real person.

Most financial tools answer a question the moment you ask it. The answer sounds the same whether the evidence behind it was three independent sources from this morning or one blog post from 2019, and you cannot tell which you got.

This one refuses to hide that. A claim without three independent, fresh, successful sources is marked UNDETERMINED, and an undetermined claim caps the decision score at 55 no matter how good the idea looks. Missing evidence stays missing instead of quietly rounding up to a yes.

It also never executes anything. It drafts, you act. There is no broker connection to get wrong.

/setup reads the statements you drop into documents/ and asks one batch of questions. Anything it does not know stays _(unset)_ rather than being guessed at. Here is the synthetic one, and every derived figure underneath it is computed, not typed in.

€60,000Net worth€85,000 assets less €25,000 mortgage
6.0Months of runway€15,000 liquid against €2,500 essential spend
25%Savings rate€1,000 spare from €4,000 net income
52.1%Debt to incomeMortgage at 2.1%, 72 months left

One hard goal: €60,000 for a house deposit by September 2031. One soft goal: retirement, no target figure, 2051. The pension pot holds €30,000 with €2,400 a year going in and a 1.5% employer match.

/market pulls from 14 providers, each one a separate module with no shared state. Every request is recorded in market/queries.json with its arguments, so any claim on the page can be replayed later and checked. The coverage table names what failed, which matters more than what worked.

/market
COVERAGE  2026-09-20

claim                     sources  freshest     verdict
────────────────────────────────────────────────────────
policy rate, euro area          3  2026-09-18   SUPPORTED
euro area HICP                  3  2026-09-17   SUPPORTED
EUR/USD                         4  2026-09-19   SUPPORTED
Italian deposit rates           1  2026-08-02   UNDETERMINED
equity risk sentiment           2  2026-09-19   UNDETERMINED

2 failed:
  ecb        TLS certificate could not be verified against this
             machine's trust store. Local configuration, not a
             source failure.
  reddit     rate limited (429), no records

independence: mirrors of one feed share an operator and
count once. 4 sources, 3 operators.

Two things are deliberate here. A failed fetch raises rather than returning an empty list, because "the source said nothing" and "the source did not answer" are different facts and only one of them should count toward independence. And a provider never follows a URL it finds inside fetched content, so a feed cannot talk the system into going somewhere else.

Ask it something. /decide runs the gates in order, scores what survives, and writes the whole record to the tracker. This household is asking whether to put the €3,000 sitting in savings into the equity fund it already holds.

/decide move €3,000 savings into the world equity fund
GATES
1 liquidity      FLAG  runway falls 6.0 → 4.8 months, under the
                       6-month target set in the profile
2 policy         PASS  inside the IPS equity band (60-80%),
                       lands at 78.2%
3 tax            PASS  regime amministrato, no realisation
4 evidence       FLAG  equity risk sentiment UNDETERMINED,
                       2 sources, 3 required

SCORE
raw 71, capped to 55  (cap:60 liquidity=FLAG,
                          cap:55 coverage=UNDETERMINED)
band: hold

THE DECISIVE CHECK
Top up the emergency fund to 6 months first. That single
move clears gate 1 and the raw score stands on its own.

STRONGEST ALTERNATIVE
€3,000 against the mortgage. Guaranteed 2.1%, no market
risk, and it does not touch the runway.

Both numbers are always reported, 71 and 55. A capped score that showed only the 55 would lose the fact that the idea was reasonable and the evidence was not. And the cap that bit here, the liquidity one, is fixable in a week, which is why the report leads with that rather than with the verdict.

  • Nothing that quotes an amount is ever committed. A guard script checks the ignore rules are still in force and scans every tracked file before a commit lands. Your balance sheet never leaves the machine.
  • Nothing executes. No order, no broker login, no filing. A test exists whose only job is to keep that true.
  • Opinions are quarantined. Where it states a view about the future, that view is labelled, kept out of every gate, and scored against what actually happened later.
  • Material non-public information is refused. A tip that looks like MNPI is refused and logged. Published insider data, Form 4 filings and congressional disclosures, is fair game and moves only the evidence dimension.
  • The rulebook is versioned. Change a threshold and a check fails until you bump the version, so a silently changed weight cannot alter every verdict from then on.

It is Python with nothing but the standard library, on purpose, so no third-party package sits between a provider response and a verdict. 559 tests. Jurisdiction is Italy and EUR for now, and no tax rule is used until it carries an official source URL and a date it was checked.

It is not licensed financial advice. It is a structured second opinion that shows its working, and every report it writes says so.

Read the code