Anand Mohan Singh

Software Engineer II at Microsoft

I build the backends that make AI agents dependable.

Case study, 2025. Kyndryl, open source.

A recommendation service for the Stock Trader demo

Stock Trader is a demo trading application. I built the service that answers “what’s the sentiment on this stock, and why?” from recent market news, and took it from a prototype to something the team used.

What
A FastAPI service and Streamlit dashboard that collect market news, retrieve the most relevant articles, and have Azure OpenAI explain the sentiment with sources.
My part
Designed and built the service at Kyndryl. It is published under Apache 2.0.
When
2025, as a software engineer co‑op at Kyndryl
Stack
Python, FastAPI, Azure OpenAI, Azure AI Search, Redis, Streamlit
Results
  • A 95% hit rate on the Redis cache.
  • Cached queries answer in under 100 ms at p99.
  • Went from prototype to a service the team used in the demo.

Repository

Context

The question is simple: given a ticker, what is the market saying about it, and why? Answering it well means pulling fresh articles from several sources, finding the ones that actually matter, and asking a model to reason over them. Done naively, every request pays for every upstream call and every model call, and is only as reliable as the slowest data source.

The goal was an answer that is grounded in sources, fast when it can be, and still works when a source is down.

Design

News comes from yfinance, Alpha Vantage and Finnhub. Articles are embedded and stored in Azure AI Search. A query runs two searches at once: a semantic vector search, and a keyword search over titles and summaries that catches exact ticker and company names the vector search can miss.

The two rankings are merged with reciprocal rank fusion. Each article scores 1 / (60 + rank) in every list it appears in, and the scores add up. An article that both searches rank highly rises to the top without having to compare a cosine similarity with a keyword score directly. The top three fused articles, above a minimum fused score, go to Azure OpenAI as context for the answer.

Redis sits in front of the expensive steps, with a different lifetime for each kind of data: prices for an hour, news for two hours, sentiment for a day, and stored article embeddings for a week. External calls go through a circuit breaker that opens after five failures and retries after a minute, so a failing data source degrades one panel instead of the whole response.

Decisions

OptionForAgainstChosen
Vector search onlySimple semantic matchingMisses exact tickers and namesNo
Hybrid search with RRFBalances meaning and exact matchesOne more ranking step to tuneYes
One cache lifetime for everythingEasy to reason aboutPrices go stale; embeddings get recomputedNo
A lifetime per kind of dataMatches how fast each one changesNeeds a clear cache policyYes

Results

The Redis cache ran at a 95% hit rate, and cached queries answer in under 100 ms at p99, because a repeat query never reaches retrieval or the model. The service went from prototype to a part of the Stock Trader demo the team used, and the code is public under Apache 2.0.

I don’t publish traffic numbers for it. The demo’s usage isn’t a meaningful production figure, and I’d rather show the design than a number I can’t stand behind.

What I’d change

I’d build an evaluation set before tuning the cache: a fast answer from the wrong articles is still wrong. With that in place I’d track groundedness, warm and cold p99, and model calls avoided, and use them to tune the fusion constant and the retrieval threshold instead of choosing them by hand.

All projects