market-lab: every score has to show its doubt

A static research board scoring 1,600+ stocks on India’s National Stock Exchange

  • 12 min read
  • Case study, Design engineering, Data product

A stock screener is dangerous when it looks cleaner than the market it describes. Most screeners let you sort by a number, then scatter the caveats across other columns. I built market-lab to do the opposite. It ranks the NSE, and every row carries the reason to be skeptical.

What the public board shows

1,600+ NSE stocks scored every trading day, from all 2,078 listed. The public board publishes derived scores, ranks and plain-language reasons only. The raw price, volume and deal series stay private, and it gives no horizon or position-sizing advice. An independent project, not affiliated with or endorsed by NSE, BSE or NSE Indices. It is research and education, not investment advice. The screenshots below show the board as I first built it.

The light market-lab board ranking NSE stocks with opportunity score, risk band, horizon, turnover, order impact, and arguments for and against each row.
The light board is dense but readable: score, liquidity, horizon, and the counter-argument sit in the same scan.Kousik DuttaOriginal product screenshot

The data was fine. The reading wasn’t.

Screeners don’t lack data. They present it as if you already know how to weigh it. A high score can hide bad liquidity. A tempting small cap can be impossible to enter cleanly. A short-term signal can read like a long-term thesis. The interface had to make those mistakes harder to make.

A typical screener
  • Leads with a sortable number
  • Treats liquidity as secondary
  • Hides the counter-case in extra columns
  • Leaves you to guess the time horizon
market-lab
  • Pairs every score with its objection
  • Shows order impact beside turnover
  • Writes the argument in plain language
  • Puts the horizon in the row
I didn’t make the board simpler. I made the dangerous simplification visible.

Interactive preview

Try the score-with-doubt rule

Example ticker84 research score

Strong candidate, but the row still carries the strongest objection.

Research score84
A simplified model of the rule. A high research score only gets to lead while liquidity, risk, and the counter-argument stay visible beside it.

I built the whole stack

I designed and built it as a full-stack static data system: Python pipeline, scoring model, validation step, generated JSON, TypeScript board, light and dark visual system, SEO surface, and scheduled publishing. The interesting design question was what the interface was allowed to imply.

1,600+
NSE stocks scored every trading day, from all 2,078 listed
0
runtime servers needed for the public board
2
theme-specific portfolio screenshots
1
rule: every score shows its doubt
Operationally it is boring on purpose, so the product stays cheap, fast, and durable.

Static was a design decision

I went static because the product doesn’t need a live server to be useful. A scheduled job fetches the market data, computes scores, verifies the output, writes static files, and publishes to a CDN. That keeps it cheap, resilient, and easy to inspect.

  1. Official exchange data
  2. Scheduled fetch
  3. Clean and normalize
  4. Scoreopportunity, risk, horizon, liquidity
  5. Verification gates
  6. Static JSON
  7. TypeScript board
  8. CDN publish
You see a fresh research board. Underneath, it is a verified static artifact the pipeline regenerates.
These are not hidden implementation details. They are what let the product make honest promises.
LayerDesign concernEngineering choice
DataStale or malformed rows never become confident UINormalize the exchange feed before scoring and publish only generated data
ScoringNo single magic number pretending to be truthCalculate several signals and show the weakest next to the strongest
InterfaceFast comparison without hiding riskDense rows with score, risk, turnover, impact, horizon, and thesis together
DeliveryKeep the public product cheap and durableStatic files on a CDN, no always-on app server
These are not hidden implementation details. They are what let the product make honest promises.

The row is the product

market-lab Rankings view showing highest-ranked NSE stocks with score, risk, thesis, weakness, turnover, and order impact.
Rankings. The core research view: ranked stocks with thesis, weakness, risk, liquidity, and order impact in one row, so the score never travels alone.Kousik DuttaOriginal product screenshot

The hardest call was resisting the detail-page instinct. In a financial product, burying the caveat is the same as deleting it for most people. So the row carries the full minimum argument: ticker, score, risk, horizon, turnover, order impact, the case for, and the case against.

The dark market-lab board with the same NSE ranking, showing score chips, risk, horizon, turnover, and counter-arguments in a dense row layout.
Both themes keep the same hierarchy. The palette changes. The rule doesn’t: the objection stays visible.Kousik DuttaOriginal product screenshot

This row is where design and engineering meet. The pipeline has to produce the fields. The TypeScript model has to keep them. The UI has to keep them aligned. And the visual system has to make the warning readable without turning the board into an alarm panel.

The warning survives the phone

The mobile problem wasn’t fitting a desktop table on a phone. It was keeping the minimum responsible argument once side-by-side comparison was gone. On desktop, a row is for scanning across stocks. On mobile, each row becomes a research card: name and score first, the thesis and the weakest signal together, then the rest of the evidence in a deliberate order. Nothing that matters for safety hides in an overflow menu.

Mobile market-lab Rankings view at 390 pixels wide with market status cards, a responsive stock research card, and persistent bottom navigation.
Mobile Rankings at 390 × 844. The row becomes a vertical argument, and score, risk, thesis, and weakest signal all stay in the first scan.Kousik DuttaOriginal product screenshot
The breakpoint changes the layout, not the truth. The same generated fields and semantic tokens drive both.
System ruleDesktop expressionMobile expression
Score never travels aloneScore, thesis, weakness, horizon, and liquidity line up in one dense rowThe card keeps score, risk, thesis, and weakest signal above the fold
Navigation stays availableResearch destinations use the wide application shellFive primary destinations become a persistent bottom bar within thumb reach
Status stays in contextSession freshness and market breadth sit above the ranking tableStatus becomes stacked disclosure cards that keep their labels and tap targets
Themes keep the hierarchyColor tokens separate surface, data, and warning rolesThe same semantic tokens hold contrast without changing the information order
The breakpoint changes the layout, not the truth. The same generated fields and semantic tokens drive both.

Mobile is a set of decisions

Good responsive work isn’t a polished desktop plus a media query. It’s deciding what may collapse, what must stay visible, and how navigation changes with reach, then encoding those calls once so every generated row behaves the same.

How I kept the score honest

market-lab Screener view showing controls for filtering and comparing NSE stocks.
Screener. Narrow the market as much as you like. The safety model stays: filters work only while each row still shows its risk and doubt.Kousik DuttaOriginal product screenshot
The score is useful only because it has to compete with the reasons not to trust it.
SignalWhy it existsHow it appears
Opportunity scoreGives research a starting orderBig enough to scan, never alone
Risk bandStops a high score from feeling good across the boardA chip next to the stock name, not buried at the end
Liquidity and order impactShows whether a real position can get in without moving the price too muchShown as practical rupee-order context
Argument againstForces the product to say why the score might misleadWritten next to the positive thesis in plain language
The score is useful only because it has to compete with the reasons not to trust it.
See every screen, failure state, and accessibility checkScreens, failure states, build craft, and accessibility gates

Every screen keeps the same posture

market-lab Investors view showing stock research organized around investor needs and market context.
Investors. The board reframed around investor behavior: not just which stock ranks, but which investor, horizon, and risk appetite the signal fits.Kousik DuttaOriginal product screenshot
market-lab Portfolio view showing a portfolio-focused research surface.
Portfolio. From picking stocks to thinking in positions: what a set of choices means together, instead of each ticker as an isolated bet.Kousik DuttaOriginal product screenshot
market-lab Methodology view explaining how the scoring system works.
Methodology. The method is part of the product. It says what refreshes, where the data comes from, what the score means, and where the model is limited on purpose.Kousik DuttaOriginal product screenshot
market-lab AI assistant panel opened on top of the stock research board.
AI assistant. Same posture as the board. It helps you interpret and compare, and it keeps reminding you this is research, not advice.Kousik DuttaOriginal product screenshot

Missing data never looks like weak data

A research product turns dangerous when missing data looks like weak data, or when a failed refresh quietly leaves yesterday’s confidence on screen. I put reliability in the shared data layer: malformed numbers become unavailable, duplicate or damaged rows get counted, and a score is suppressed when its risk, liquidity, or horizon context can’t support it.

market-lab recoverable market-data error showing the HTTP status, an explanation that scores are withheld without source data, and actions to retry or inspect the methodology.
Recoverable data failure. The app stays usable, explains why no scores are showing, and offers a retry plus a route to the method.Kousik DuttaOriginal product screenshot
Loading, empty, invalid, degraded, and failed are separate states, because each one needs its own way back.
Failure classUnsafe behaviorDesigned response
Initial data failureRender an empty board or fabricate zero-like valuesWithhold scores, show the failure reason, and offer retry plus methodology
Background refresh failureReplace useful context with a generic errorKeep the last-known screen, mark it stale, and let the user retry
Malformed or partial rowsLet missing risk or liquidity look neutralMark fields unavailable, count rejected rows, and suppress unsupported rankings
Restrictive filtersTreat zero results like a system outageName the active constraint and give a direct way to clear or widen filters
Invalid portfolio or assistant inputAccept a success-shaped answerAttach the error to the field or request and keep the user’s work recoverable
Loading, empty, invalid, degraded, and failed are separate states, because each one needs its own way back.

Missing means less confident

Missing context lowers confidence. It never turns into a reassuring zero, a complete-looking row, or a score the evidence can’t support.

The table is the last mile

The table is the last mile of a whole system: generated data, typed fields, sorting, responsive density, contrast in each theme, static routing, and public deployment. I worked every part of it: product judgment, interaction design, semantic visual tokens, TypeScript, pipeline verification, and running cost. A row that looks clean but drops a warning on mobile is a product failure, not a CSS bug.

Accessibility gated the release

The accessibility sweep covered every primary view, factsheets, assistant setup, both themes, and the loading, empty, degraded, and error states. I fixed landmarks and headings, linked labels and errors, made rows activate from the keyboard, kept focus visible and restored it, fixed disclosure semantics, announced live status, wrote text alternatives for financial signals, added reduced motion, communicated risk without relying on color, and made narrow screens reflow.

66
Playwright checks passed across product and failure states
0
automated WCAG 2.2 A/AA violations in tested surfaces
320–430
pixel-wide mobile viewports exercised
200%
equivalent zoom and reflow coverage at 640 pixels
Automated coverage is evidence, not a certificate, so I paired it with keyboard-flow and responsive browser checks.
The claim is narrow and it holds: automated WCAG 2.2 A/AA checks found no violations in the views and states I tested. Automated tools can’t prove full screen-reader or assistive-technology conformance, so I don’t claim it.
User needImplementation evidenceHow I checked it
Understand status without colorRisk and quality use text, icons, and semantic labels as well as colorLight and dark contrast checks plus non-color state inspection
Operate the board without a pointerRows, filters, disclosures, factsheets, and assistant controls have keyboard semanticsTab order, focus visibility, Enter and Space activation, and focus-return tests
Hear updates and errorsLoading, result counts, assistant responses, and failures use the right status or alert behaviorAutomated role and name checks plus inspection of the critical live regions
Zoom or use a narrow viewportCards reflow without two-way page scrolling and primary controls keep usable targets320, 390, and 430 pixel viewports plus 200%-equivalent reflow
The claim is narrow and it holds: automated WCAG 2.2 A/AA checks found no violations in the views and states I tested. Automated tools can’t prove full screen-reader or assistive-technology conformance, so I don’t claim it.

One rule, everywhere

No number travels without context. If a score can’t bring its risk, liquidity, and counter-argument along, it doesn’t get to be a primary decision point.

What this proves, and what it doesn’t

I built this alone, so the evidence is ownership end to end: model rules, data boundaries, responsive interaction, accessibility, recovery, tests, and deployment. It says nothing about team leadership, and the rankings say nothing about investment returns. Judge it on research quality: how fast you can compare a shortlist, whether you notice the counter-argument, how it recovers from stale or failed data, and whether confidence drops when the evidence is incomplete.

What I’d build next

Next, I’d make score movement easy to audit over time: a change log for why a score moved after each trading session, the raw fields behind every final judgment, and structured testing across several screen-reader and browser combinations.

The takeaway

market-lab’s quality lives in the seam between the model and the interface. The score, the pipeline, the themes, and the row layout all enforce one principle: help people research faster, and never let speed turn into false confidence.

Was this useful? Your pick is saved on this device and counted anonymously.