VivaTrades
Makes a stock decision take two minutes instead of an evening. Plain-English analysis of 500 NSE stocks, refreshed on a schedule, plus free strategy backtesting. No paywall, no signup.
Next.js · Neon Postgres · Prisma · LLM synthesis · Kite Connect · PM2 + launchd
The problem
An Indian retail investor deciding whether to buy a stock has two options, and neither answers the question. Screeners hand you forty columns and no opinion — you still have to know which of them matter this quarter. Research reports are paywalled PDFs written for institutions, arriving too late and too long.
So the actual work falls on the investor: open six tabs, reconcile numbers that disagree, read three months of news, and form a view. That is an evening's work per stock, and almost nobody does it — they buy on a tip instead.
What it does
VivaTrades answers the question directly. Every one of the 500 NSE-listed stocks has a written report — a snapshot, what is genuinely strong, what is weak, where it sits against its peers, and the risks worth knowing before buying. Plain English, two minutes, and a view rather than a table.
Reports are refreshed on a schedule rather than generated on request, so the analysis is already there when someone arrives, the same for everyone, and free to serve at 500 stocks.
The second half is the backtesting tool, also free: build a strategy, run it over 20 years of history with walk-forward and out-of-sample windows, and find out whether it actually worked before putting money behind it. That kind of tooling is normally either expensive or a spreadsheet, which is why most retail strategies are never tested at all.
Why the analysis can be trusted
An LLM handed a table of financials will produce a confident paragraph whether or not the numbers justify one. A fabricated risk flag on a real stock is worse than no report — it is the error that ends the product's credibility in a single screenshot.
The constraint that worked was structural rather than instructional. The model receives a pre-computed set of facts, rankings and risk flags — never raw tables. Peer comparison, scoring and flagging happen in code that can be tested. Its job is to write English about a decision already made, not to make the decision.
That ratio generalises well beyond this product: the LLM occupies one stage, and everything either side of it is ordinary, testable data engineering.
How a report actually gets made
The analysis runs as a two-phase pipeline, and the split is the whole cost model.
Phase A is deterministic and universal. Fundamentals, technicals, news and peer sets are fetched and reconciled for all ~500 stocks in roughly five to ten minutes, with no model involved at all. Phase B is the expensive half, and it runs for only about 100 stocks a night — chosen by a priority queue combining tier weight, recency of visits and age, with a three-day freshness skip so a recently-analysed stock drops out of contention. The full universe rotates in roughly five days on a fifth of the nightly spend.
That skip is load-bearing. Remove it and the same large caps get re-analysed every night while the tail is never touched — the system looks busy and quietly covers a tenth of its universe.
There is an honest tension in it, and the product choice matters more than the mechanism: more stocks become eligible each night than there are slots, so the freshness the skip implies is not one the throughput can actually deliver. Rather than paper over that, every read returns an age and a staleness flag and the interface surfaces per-section freshness. A reader can see the data is four days old and judge accordingly. Making the gap visible is a better product than a promise that quietly fails.
The consequence for a reader matters more than the architecture. The public page reads the raw data unconditionally, so fundamentals, technicals and news are always there immediately. Only the written narrative can be missing, and it says so rather than blocking the page. The expensive part is optional by design.
Within Phase B the work is split by how hard it is. News headlines are classified for sentiment and importance by a small, fast model doing one narrow job — no prose, just structured JSON. A larger model then reads the assembled draft and produces the risk classification and the published narrative in a single pass.
Seven model calls per stock became two
The interesting engineering here was subtraction. The pipeline started at seven model calls per stock. It is now two.
Most of that came from one move, repeated: when a stage turned out to be mechanical, it stopped being an agent. Draft assembly was originally a full model call; it is now a deterministic builder that reads the reconciled data and lays out the sections, which removed a call per stock and made that step testable at the same time. Two further stages doing adjacent work were merged into one pass.
The remaining two calls are the ones that genuinely need judgement — deciding what a headline means, and writing prose a person will read. Everything mechanical went back into code, which is cheaper, faster, and the only part you can write a test for.
Agents fail by doing less, not by erroring
Phase B is orchestrated by coding-agent sessions invoked from cron, with a sub-agent per analysis dimension and a synthesiser composing the result. Adding a dimension is a prompt and a schema rather than a service — which is the upside.
The cost is that an agent's failure mode is not a stack trace. It is a run that completes, reports success, and writes less than it should. One night's log records an agent concluding "Monitor timeout is fine" — and exiting 0.
So the wrapper does not trust the exit code. After each run it queries the database and asserts a minimum number of analyses were actually written that day; fewer rows than the floor is a failure regardless of what the process reported.
The second mitigation is splitting the run. Rather than one long session over ~100 stocks, it runs as two independent sessions of ~50 — long agent sessions die, a dropped API socket kills the whole session, and a fresh session gets a fresh socket. Measured, this took the probability of a zero-coverage night from ~30% to ~5-10%, turning an all-or-nothing outcome into ~100 stocks when both succeed and ~50 when one dies.
When a component can fail by doing less rather than by erroring, the health check has to measure output, not process status.
The timeout that quietly destroyed 95% of coverage
Phase A fans out with bounded concurrency and one retry. I set the per-stock timeout to 45 seconds, reasoning about the happy path.
The upstream's ordinary slow-night per-stock time is 50-90 seconds — sitting just above that cap. For five consecutive nights the timeout fired on 477-485 of ~500 stocks. The pipeline reported itself healthy, finished on time, and destroyed roughly 95% of nightly coverage while doing so.
It is now 180 seconds: worst case about 2.1 hours against a 3.5-hour window before Phase B, typical run ten minutes.
Set a timeout from the tail of the observed distribution, not the median — a cap tuned to the happy path is not a safety valve, it is a scheduled outage. And confirm what it actually fires on, because this failure was completely invisible from outside: the job completed, on time, every night.
A regulator in the prompt
VivaTrades is not a SEBI-registered Research Analyst, which puts a hard legal line through the middle of the product: it may describe what the data shows, and must never prescribe what an investor should do.
That constraint cannot live in a code review, because the text is written by a model overnight across hundreds of stocks. It lives in the agent itself — the compliance rule and an explicit forbidden-vocabulary list are part of the prompt that writes the narrative, and it holds even when the model is relaying somebody else's view, because repeating a broker's buy rating is still publishing a recommendation.
This is what a real production constraint on an LLM looks like: not a disclaimer in the footer, but a rule enforced at the point of generation, on every stock, every run.
The same numbers I use myself
The repo holds two products. The public platform is one; the other is the system I run my own money through against a 24% CAGR target — positions, orders, screening, weekend reviews. They are kept deliberately separate, tagged per commit, because mixing them up is the failure mode: research tooling leaking into the public product ships half-validated ideas to strangers.
But they share the data backbone, and that is the part worth stating plainly. The free public analysis and my own real-money decisions read the same reconciled data. There is no private dataset allowed to be better than the one anybody can use for nothing.
What it runs on
Next.js on Vercel for the reading surface, Postgres on Neon for reconciled data, and scheduled jobs under PM2 with launchd supervision, running after the market closes.
Deliberately unglamorous: a scheduled batch does not need a queue, a cluster, or a bill. Reports render statically, so a reader hits a file rather than an inference call — which is what keeps it free to run and free to use.
How a report gets written
Ingest
500 NSE stocks
Fundamentals, price history and news per stock
Normalise
Postgres
Reconcile corporate actions and survivorship gaps
Rank
Testable
Peer comparison and risk flags computed in code
Write
LLM
Plain-English report from settled facts
Serve
Free · no signup
Static render — already there when you arrive
Where the models sit
Phase A · raw data
No model · ~5-10 min
Fundamentals, technicals, news and peers for all 504 stocks
Draft builder
Used to be a model call
Assembles the sections deterministically
Classify news
Small model
Sentiment and importance only, as structured JSON
Risk + narrative
Large model
One pass, compliance rules inside the prompt
Publish
Static
Raw data always visible; narrative fills in behind it