William Toni ← Back to Portfolio
Applied AI · Quantitative Finance · 2026

Investment Copilot

An offline portfolio analytics workbench with a mean-variance rebalancing engine and a chat layer that is structurally incapable of inventing a number.

22
deterministic tools
0
numbers from the model
3.5%
hard volatility cap
155
tests passing

The problem

A portfolio manager runs performance attribution and rebalancing by hand in a spreadsheet: recomputing Sharpe and drawdown every month, re-solving allocations under a volatility ceiling, and answering "how did we do?" from memory. It is slow, easy to fumble, and there is no permanent record of what changed or why. The obvious modern fix, hand the numbers to a language model, is exactly the wrong one: an LLM will produce a fluent, confident, wrong Sharpe ratio, and on client money that is worse than no answer.

What it is

An offline analytics workbench with three parts: an attribution engine, a mean-variance rebalancing engine with a hard volatility cap, and a chat assistant. The defining constraint is that the language model cannot compute anything. It may only choose which deterministic tool to call; every figure in every answer comes out of plain, tested Python. The model decides what to ask and explains what came back.

How it works

tool resultselectsNAV + yieldsCSV / XLSXauto-detectedDeterministic engineattribution · optimizerhistory · analytics22 typed toolsone job eachLLM routerqwen3:14b · selects a toolnever computesAnswerfigure + theexact tool call

The chat runs on a local GPU model through a LangGraph loop: recall → model → tool → reason → remember. A test pins the invariant, if any number ever reaches the user without passing through a tool, the suite fails.

What it removed

Manual
Monthly attribution done by hand in Excel is now one upload. Total/annualised return, volatility, drawdown, Sharpe and Sortino compute in seconds.
Fabrication
The class of error where an AI invents a plausible financial figure is designed out, not merely discouraged. The model routes; it never calculates.
Silence
Bad input raises a typed error the UI shows and the agent returns as structured data. Nothing degrades quietly, which on financial data is the dangerous failure.

Honest limitations

Tools
PythonStreamlitLangGraphOllama / qwen3:14bNumPySciPy (SLSQP)SQLiteLangfusepytest
Methodology
Mean-variance optimisationHard volatility constraintDeterministic tool-callingRegression-gated correctnessAppend-only history