Welcome back!
Log in to your dashboard
Free forever. No card, no ads — and your data stays yours.
By continuing you agree to our and .
Track, value and stress-test stocks and mutual funds across the US and India — in one private dashboard that's free.
Your watchlist, scored and colour-coded — then one click into valuation, charts, AI summaries and backtests.
Research, valuation, screening, simulation and alerts — no spreadsheets, and free to start.
The free plan is free forever — no card, no ads. Continue with Google, or use an email and password.
Research and analysis only. VroomStocks never places trades and is not investment advice.
The Basic plan is free of charge, forever, and it covers the whole product — watchlists, valuation, charts, screeners, AI summaries, mutual funds, backtesting and screener alerts. Only the size of the numbers is capped: how many stocks you can track, how far back the financial history goes, how many AI summaries you can generate each month and which alerts you can set. Advanced raises every one of those. There are no ads, and nothing is charged unless you choose a paid plan.
Yes — that is what Alerts are for. Set one on a buy/sell signal, a price move on your watchlist, a saved screen gaining or losing a name, a shareholding or insider transaction, a new earnings call or an AI summary, and it arrives by email hourly, daily, weekly or monthly. Everything due in the same window is combined into a single digest, and nothing is ever reported to you twice. The two screener alerts are included on the free plan.
Prices and fundamentals come from Yahoo Finance, with SEC EDGAR and other public sources as fallbacks for US names and NSE for India. Mutual fund data comes from AMFI's official direct-growth catalogue. Earnings-call transcripts come from the exchange filings themselves.
Every account gets its own isolated store. Your watchlist, holdings, screens and settings are scoped to your sign-in and are never shared with or visible to other users.
No. VroomStocks is a research and analysis tool — it never connects to a broker and never places an order. Backtesting and paper-trading are simulated on historical and live prices.
US stocks (NYSE and Nasdaq) and Indian stocks (NSE), plus 2,500+ Indian direct-growth mutual funds. You can switch markets at any time from the sidebar.
An illustrated walkthrough of every feature — from adding your first stock to screening the market, reading AI summaries, tracking your mutual funds, backtesting a trading strategy and setting the alerts that tell you when something changes.
Log in to your dashboard
Free forever. No card, no ads — and your data stays yours.
By continuing you agree to our and .
Everything described here is free — no card, no ads, and your data stays yours.
Setting up your profile…
We're shipping a quick improvement — you'll be back in a few seconds. No need to refresh.
Safari is the only iPhone browser that can install web apps.
Name this metric selection so you can pick it again from the Template list.
| Ticker |
|---|
| Ticker |
|---|
| Ticker |
|---|
| Ticker |
|---|
| Ticker |
|---|
Which of the 10 backtest strategies wins most consistently — across your whole portfolio, and against the broad market.
No analysis yet for this region. Run it once to see which strategy performs best across your portfolio and vs the market. This fetches 5 years of history for every stock you track, so it may take a little while — after that, it loads instantly from your saved results until you hit Refresh again.
Fetching 5-year history and simulating 9 strategies…
Upload a Consolidated Account Statement (CAS) PDF from CAMS or KFintech. On the CAMS request page, use these settings:
Everything is parsed on the server — the file is never stored.
Get an email when something you follow actually moves. Everything due in the same window arrives as one message, so more alerts does not mean more email.
Your details and how you sign in. Nothing here is shown to anyone else.
Warm and refresh the shared screener and transcript caches, and schedule them to keep themselves up to date.
| Market | Universe | Loaded | Last updated | Actions |
|---|
Warm fetches only the stocks that are missing or stale. Refresh force-re-fetches every stock in that universe (slow for “All”). Warm missing (under a market) warms every not-fully-loaded universe in that market at once. The job now retries throttled fetches and only gives up on genuinely un-fetchable symbols (shown below). One job runs at a time. The auto-refresh cadence below force-refreshes the whole universe server-side on a schedule, even when no one is on the page.
Users, stocks added, and activity across the selected window. Activity is logged from this feature's deploy onward; stock counts and “last active” also reflect earlier data.
| User | Stocks added | Added in window | Activity in window | Top actions | Last active |
|---|
How the APIs readers actually call are behaving, and how the background sync jobs are doing. A failure is a 5xx or an unhandled exception — a 4xx is counted separately as a client error, so an endpoint that validates its input does not look broken. Reliability is (calls − failures) / calls. API timings are recorded into hourly buckets and flushed every minute, so the last minute may be missing.
Ordered by number of failures. Select a row to see why it failed.
| API | Method | Calls | Failures | Client errors | Reliability | Why |
|---|
Ordered by average server time, not by the worst single call — one cold start is not a slow endpoint.
| API | Method | Calls | Avg | Slowest | Reliability |
|---|
Every endpoint called in this window, least reliable first.
| API | Method | Calls | Failures | Client errors | Avg | Reliability |
|---|
Failed and partial runs with their message. A run that merely had failing tasks carries its reasons on the tasks, so those are shown underneath.
| Job | Outcome | When | Took | Failed tasks | Message |
|---|
| Job | Runs | Avg | Longest | Tasks done | Tasks failed |
|---|
A partial run counts against reliability; a cancelled one does not — a deploy stops the worker mid-run by design, and counting that would track how often we ship rather than how well the jobs work.
| Job | Runs judged | Succeeded | Partial | Failed | Cancelled | Reliability |
|---|
Warm and refresh the shared mutual-fund universe (AMFI Direct-Growth schemes, NAV history via mfapi.in). Warm fetches only missing or stale funds; Refresh re-fetches every fund in that category (slow for “All”). The auto-refresh cadence below warms every category on a schedule, server-side, even when no one is on the page. One fund job runs at a time.
| Category | Loaded | Last updated | Actions |
|---|
A stock's Q-Score is recalculated every time that stock is refreshed, but a fund's Avg Q-Score is a stored holding-weighted average of its constituents' scores, so nothing recomputes it on its own — this job does. It sweeps the whole cached universe, oldest-scored scheme first, and also refreshes Avg Technical Upside% and Avg Analyst Upside% alongside it. It costs one fund-detail request per scheme, so it is scheduled separately from the warm above; weekly is the sensible cadence. Recompute now runs the same sweep immediately.
Pre-fetch earnings-call transcripts for every stock in the covered indexes (membership read from the index_constituents table) into the shared earning_call store, so the Earnings Call page answers from the database instead of waiting on a provider. Sync due checks each ticker's next earnings date (cached, refreshed about once a quarter) and only fetches the ones that have reported since we last stored a report — the cheapest pass, and what the schedule runs; Sync new re-checks every ticker and stores only reports that became newly available since its last sync; Prefetch skips tickers fetched in the last 30 days; Refresh all re-fetches everything (slow). The auto-refresh cadence below runs Sync due on a schedule. One job runs at a time; each run's synced reports are listed under Recent syncs. Only the 4 most recent reports are kept per company — storing a newer one evicts the oldest.
| Market | Indexes | Stored | Actions |
|---|
Reads primary regulatory documents straight from the source — no model, no vendor, no API key. United States (SEC EDGAR): XBRL financials parse each 10-Q/10-K's tagged facts into sec_xbrl_financials (the source behind the Financials page, including the revenue/segment breakdowns); Form 4 records every insider trade into sec_form4, distinguishing an open-market purchase from a vest or a tax withholding; 13F-HR records what a curated set of active institutional managers holds, into sec_thirteenf. India (NSE/BSE): the exchange's XBRL result filings land in the same financials table, PIT insider disclosures are India's Form 4 analogue and share sec_form4, and the quarterly shareholding pattern — an issuer-side promoter / public / employee-trust split rather than a per-manager holdings list — lands in nse_shareholding. Only 13F is US-only: it has no Indian equivalent, and India's shareholding pattern has no US one.
Full sync seeds a market from scratch — a year of Form 4 / PIT history per ticker, two quarters per 13F manager, and as much XBRL history as that market's XBRL history selector asks for (1–10 years, or everything the source still serves) — and takes hours. Deeper history costs proportionally more time: each company files four periodic reports a year and every one is parsed individually, so ten years is roughly three times the work of the three-year default. Incremental sync is exactly what the schedule runs, and only asks for what can have changed: XBRL takes each company's newest filing (the history selector does not apply), Form 4 pulls the market-wide filing index for the last few days (one request for the whole universe, not one per ticker), and quarterly holdings do nothing at all outside their filing window, because a book is frozen between deadlines. Recommended cadence: daily. An insider must file within two business days of trading, so anything slower reports the news late; XBRL lands with the periodic report a median of one day after the earnings release; and the 13F window opens itself four times a year (14 Feb · 15 May · 14 Aug · 14 Nov) without costing anything on the other ~300 days.
| Dataset | Stored | Companies | Latest | Incremental strategy |
|---|
These checkboxes control the scheduled US run only — the ones beside the manual sync buttons below are separate. XBRL always runs and has no checkbox: this pass is the only thing that syncs structured financials — nothing else pulls a company's 10-Q/10-K — so switching it off would stop the Financials page updating entirely. Datasets run in order (XBRL → Form 4 → 13F) and each waits for the one before it.
These checkboxes control the scheduled India run only. XBRL always runs and has no checkbox, for the same reason as the US group. The two India boxes are separate documents: PIT insider disclosures are the Form 4 analogue (individual trades), while the shareholding pattern is the issuer's quarterly promoter / public / employee-trust split — it travels on the same dataset slot as 13F because it answers the same "who owns this" question, so switching it off leaves ownership stale even though insider trades keep flowing.
Keep the per-user stocks watchlist rows up to date server-side, even for tickers nobody has opened. A ticker is due when it has never been fetched, when it was last fetched on or before the company's latest earnings report (so the stored numbers predate results that already exist), or when it was last fetched more than 90 days ago. Report dates come from the earnings calendar the transcript sync already maintains. Refresh due fetches only those; Refresh all re-fetches every ticker (slow). Each ticker is fetched once and merged into every user's row that holds it. The auto-refresh cadence below runs Refresh due on a schedule. One job runs at a time.
How completely the structured filings store (sec_xbrl_financials) is populated, per metric and per region. The Financials page renders only what was stored, so a line item the extractor never populates is invisible — it simply does not appear, which reads as “the company does not report this” rather than as a gap. A metric at or near 0% filled is therefore a mapping bug, not a disclosure fact, and this is the only place it shows up. Percentages are of that region's own row count, so the two columns are comparable in share but not in absolute rows. Click any column heading to sort.
The section above asks whether a stored row is filled; this one asks whether the row exists at all. Every stored filing is folded onto the calendar quarter its period ends in — a 10-Q, an Indian quarterly result and a 10-K all land on a quarter boundary, and a 10-K's period end is its fiscal Q4 end, which is exactly the column the Financials page derives Q4 from — so this counts renderable quarters, not filings. The window ends at the newest quarter that market has actually reached, not at today, because a quarter that is simply not reported yet is not a gap. Each absence is then split by why it is absent: quarters before the stock's first filing, quarters still inside ordinary filing lag and quarters a delisted stock will never file are facts about the company, while a hole with filings on both sides — or a live index member that has fallen behind — is ours. Only the last two are worth chasing, and the Real gaps column counts only those.
Every third-party feed the app depends on, grouped by the functionality it serves, with whether its credential is actually set on this instance. Keyless is a deliberate design choice throughout — the exchange and regulator feeds need no account — so it is a healthy state, not a gap. Not configured means that source is currently unavailable and whatever depends on it is running on its fallback.
Every HTTP endpoint this app exposes, with what it is for and a worked request and response. The list is discovered from the running app's own routing table, not hand-maintained, so it cannot fall behind the code — a route added tomorrow appears here on the next load, marked undocumented until its entry is written in api_catalog.API_DOCS. Samples are illustrative literals: opening this page makes no upstream call and reads no user data.
Every background job now runs in a separate worker process, not inside the web app. A card's “Run now” only puts a run on the queue; the worker claims it, splits it into units of work and records each one. That is what makes a sync survive a deploy: whatever was mid-flight simply loses its lease and is picked up again, rather than vanishing with the process that was running it.
Each unit keeps its own attempt count, backoff and last error, so a failure is recorded rather than retried invisibly. A run that finished with some units still failing is marked partial — expand it to see exactly which ones and why, then retry just those. Only one run per job is ever in flight; that is enforced by the database, not by this page.
The four digest jobs are ordinary queue jobs, but they run as often as hourly, so the history above leaves them out by default — tick “Include alert digests” there to see the individual runs. This is the part the run rows cannot tell you anyway: what went out each day, by alert type, and what failed. A run that succeeded having found nothing is normal and common — only the sends table distinguishes it from one that mailed hundreds of findings.
Two questions about alerting, answered side by side: how many alerts we have actually sent, by type and by frequency, and how many readers have configured each type and frequency. Sends are counted from a ledger written at the moment an email goes out, so an alert someone has since edited or deleted is still counted — the configuration table alone could not tell you it ever fired.
A finding is one thing worth reporting; a digest is one email, which may carry several. Rules and readers are counted separately on purpose: one reader with twelve price alerts is a very different signal from twelve readers with one each.
Every constant that governs how often and how hard the background jobs run, in one place. These used to be reachable only as App Service app settings, which meant a restart to change a throttle. An edit here is stored in SQL and applied immediately — the other worker picks it up within a minute. Default is the app setting (or the built-in value) the app started with, so Reset always restores it. Values are range-checked; a blank field is ignored.
The Azure AI Foundry wiring behind AI Insight and the earnings-call metrics, plus every prompt the models are given. An edit is stored in SQL and applied immediately — the other worker picks it up within a minute. Default is the App Service app setting (or the built-in value) the app started with, so Reset always restores it. The API key is write-only: once saved it is never sent back to this page.
These are sent verbatim to the model. The system prompt is what forbids outside knowledge, buy/sell calls and price targets, and what tells the model to treat stock data as untrusted input — weakening it weakens those guarantees. Existing summaries are cached and are not regenerated by an edit; use a card's ↻ Refresh to rewrite one. For the extraction prompts, bump the prompt version so stored metrics are re-extracted.
The Q-Score puts every input on the same 0–100 scale before comparing them. Each metric gets a straight line between a low anchor, which scores 0, and a high anchor, which scores 100; anything beyond either end is clamped. The quality metrics are averaged into the Q-Green-Score, the valuation multiples into the Q-Black-Score, and Q-Score = Q-Green-Score − Q-Black-Score, so it always lands between −100 and +100.
The anchors are absolute, not peer-relative, on purpose: a score has to mean the same thing in every watchlist, and a percentile against neighbours would move a stock's score when the neighbours change. Widening a range makes that metric less decisive; narrowing it makes it more. A multiple that is missing because the company loses money is scored at the maximum expensive end rather than skipped, which is what stops losses from flattering a score. Edits are stored in SQL and apply within a minute on every worker; they do not rewrite already-stored scores — those refresh as each stock does.
Every number the two plans differ on: watchlist size and count, years of financial history, saved visuals, the two monthly AI allowances, how many alerts of each type a reader may create, the price and the free trial. Advanced is any admin — the owner or a delegated address — plus anyone with a live paid subscription (see Billing), and everyone else is Basic. So this card configures what each tier gets, not who is in it.
Edits are stored in SQL and apply within a minute on every worker, including the out-of-process sync worker. The same numbers are served to the public pricing cards through /api/plans, so a change here moves the marketing copy as well — which is the point: hand-written copy drifts from the cap the server enforces. Stocks treats 0 as unlimited; each Alerts: field is a per-kind cap where 0 means that alert type is not on the tier at all. ⚠ Watchlists is advertised but not yet enforced — there is no per-user watchlist cap. Prices are in INR. ⚠ Changing a price here does not change what an existing mandate debits — a provider plan’s amount is immutable, so mint a new one from the Billing card afterwards.
Connects the Advanced plan to a UPI Autopay aggregator — Cashfree (default) or Razorpay — so a customer can start it themselves: one UPI scan debits the trial charge immediately and registers an Autopay mandate that collects the Advanced price from day 8 onward. Until Accept payments is on and the selected provider’s credentials are set, the pricing card keeps its “Contact Us” button and /api/billing/subscribe refuses with 503 — so a half-configured install cannot take money. Each provider’s keys are stored separately, so switching back and forth loses nothing.
⚠ A secret is never shown again once saved — only whether one is set. The webhook signature is the security boundary of the whole feature: entitlement is granted only by a callback whose HMAC verifies, never by the browser. Cashfree issues no separate webhook secret — it signs with the key secret above, so leave that field blank; Razorpay does issue one and needs it filled in. Point the provider at /api/billing/webhook/cashfree and subscribe it to its subscription events. Create plan appears only for Razorpay, whose plan amounts cannot be edited — mint a new plan after any price change or customers keep paying the old one. Cashfree sends the price inline with every subscription, so it needs no plan step and cannot drift.
⚠ Test connection before switching payments on. It creates a real subscription in the provider’s sandbox with the stored keys, resolves the authorisation link and cancels it again — which is the only way to tell a key that is set from a key that is right. It refuses while Live mode is on, because against a live account it would register a genuine mandate.
Delegate the admin console to another address, and raise a single user's AI allowance above the plan default. Both lists are stored in SQL and picked up by every worker within a minute.
An address here gets the whole admin console — every sync job, every user's analytics and every setting on this page. Grant it sparingly. The owner is compiled into the app rather than stored as a row, so it can never be removed and no database edit can lock it out; for the same reason, only the owner can grant or revoke admin — a delegated admin that could delegate further would let one compromised account entrench itself. Delegated admins see this list but cannot change it.
Overrides for one address. Daily is the anti-burst ceiling on generations per UTC day; Summaries and Insights are the monthly plan caps for earnings-call summaries and the four insight kinds. Leave a box blank to keep that bucket on the default. Admins are already exempt from the monthly caps, so this is for normal users. Any admin can set these — it moves a spend cap, not access.
One-off checks that touch the real outside world. Use them to verify a template or a provider end-to-end without creating an account.
Sends the onboarding email — the same one a new user gets after verifying their address (email/password) or on their first page load (Google sign-in) — to any address you type here. This is a real send through Resend, and it deliberately does not touch the send-once ledger: testing against an address never uses up that person's actual welcome email, and you can repeat it as often as you like. Preview renders the same HTML in a new tab without sending anything.
Search for any listed company to open its analysis page.
| # | Side | Date | Price | Shares | P&L |
|---|