← All case studies

Case study · Finance

Peer and transaction recommendation agents

Peers and precedent transactions are the two lists at the centre of any valuation, and analysts assembled both by hand across three licensed databases. Two systems now generate a ranked shortlist for each, with a written reason per candidate. The analyst still decides what enters the model, and that decision is the label the system learns from.

3 sourcesS&P Capital IQ, FactSet and CIQ, joined in Snowflake
5 stagesFilter, embed, gate, score, then re-rank
Reason per rowEvery candidate arrives with its rationale
LiveBoth systems in production in the platform
Transactions screen with AI suggested transactions
Recreated from the platform interface. All company and deal data is synthetic.
Client
A financial software company in the EU
My role
Built the recommendation pipeline and the MCP server.
Team
Worked alongside the client platform team.
Timeline
2025.
Stack
Python, PyTorch, Snowflake, AWS Bedrock, MCP, GraphQL

The problem

Peers and precedent transactions are the two lists at the centre of any valuation, and analysts built both by hand across three licensed databases.

The result depended on who was doing it and how long they had, and nothing recorded what had been considered and rejected.

Before and after workflow for the peer and transaction recommendation agents
How an analyst built a peer set before, and how the shortlist is produced now.

The solution

Similar text is not the same as comparable business. Two companies can describe themselves almost identically and be useless as comparables, because one is a brand owner with pricing power and the other a contract manufacturer. Cosine similarity scores the words.

Which makes the embedding step the weakest link, and the four stages around it load-bearing: hard filters in SQL, a maintained list of known non-comparables, heuristic scoring on size, margin and growth, and the language model last.

It re-ranks a pool that is already filtered and already scored, and writes the reason a reviewer reads. It is never asked to produce a company name, so it cannot invent one.

(inline svg)
Simplified. Steps are the same pattern for peers and transactions.

Components

Hard filters

Geography, SIC code and industry group cut the database to a relevant pool in SQL, before any model runs. Embedding the whole universe on every valuation would cost more and rank no better.

Embedding match

Company and deal descriptions, enriched in Snowflake, are embedded with an open-source text model and compared against the filtered pool by cosine similarity.

Negative gating

A maintained list removes candidates that pass the filters and are still clearly wrong: subsidiaries of the same group, companies in regulatory run-off. Some wrong answers are wrong for reasons no similarity score will catch.

Heuristic scoring

Size, margin and growth proximity produce the initial ranking. This is where comparability actually lives, and it is arithmetic rather than a model.

LLM re-ranking

The only generative step, and the last one. It re-orders a pool that is already filtered and already scored, and writes the short reason a reviewer reads before accepting a candidate.

MCP server

Every platform GraphQL operation is wrapped as a typed tool so an agent can call it directly. The analyst's token is passed through, so the platform's own authorization rules apply unchanged.

Analyst review

Accept, reject or replace, per candidate. Nothing enters the valuation model without an explicit human decision, and that decision is the label the system learns from.

Evaluation

Peer sets from closed valuations are ground truth: for a given target, this is the set a qualified person was willing to sign. Ranking against it answers how often the correct peer appears in the top twenty.

Tech stack

Data

S&P Capital IQFactSetCIQSnowflake

ML and embeddings

PythonPyTorchOpen-source text embedding modelCosine similarity

LLM re-ranking

AWS BedrockLangChainPython

MCP server

MCP protocolGraphQLLangChainPython

Product screens

Company and search entry

1 of 3

Recreated from the platform interface. All company and deal data is synthetic.

Results

Before

Hours, and inconsistent

Peers and transactions were assembled by hand across three licensed databases. The result depended on who was doing it and how long they had, and nothing recorded what had been rejected.

After

Shortlist in seconds

Ranked candidates, each with a written reason and a path back to the source record. The analyst still decides what enters the valuation model.

No baseline of manual analyst selections was captured before launch, so I will not claim a time saving. Acceptance rate is what the system is judged on instead, and capturing that baseline first is what I would insist on next time.