Skip to content
Anurag Raina
Back to projects

Case study

OctaK Edge

A research tool for Indian stocks: code computes every rating, and AI only explains it.

Role
Architecture and implementation, two-person team
Year
2026
Status
In development
OctaK Edge landing page - a search box to analyse any NSE or BSE stock, under the headline 'Invest beyond noise. Decide with evidence.'

At a glance

Indian stocks covered
~300
holding timeframes
5
component scores
7
deployed apps
3

Overview

OctaK Edge is a research tool for people who trade or invest in Indian stocks. You look up a company and it returns a rating out of 100, the evidence behind that rating, and a trade plan with an entry price, a stop-loss and targets - or a refusal, when the setup is too weak to act on.

One rule governs the system: numbers come from code, words come from AI. Every score and price level is computed in TypeScript on the server, and the model only explains a verdict it did not produce.

The product concept and the scoring model are my collaborator's. I built the platform around them - the data layer, the analysis pipeline, the database, the access rules, the scheduled jobs and the three-app split. Two of us, pre-launch.

Why I Built It

Most retail stock tools publish a number with no working shown. Ask a chatbot instead and it improvises a confident recommendation. Neither can be checked, which is the one thing that matters when the answer costs money.

OctaK Edge takes the harder position: every number traces to a deterministic module, and the model may only explain values it did not compute. Writing that sentence is easy. Holding it true across free data sources, a serverless time budget, five timeframes and a second app on the same database is the engineering.

The Approach

My scope was everything between the scoring model and a product someone can use: fetching market data, running it through the engine, storing what it produced, deciding who sees what, and keeping it fresh on a schedule.

It shipped as a single Next.js app first - one deploy, and no service boundary to maintain while the surface was still moving. Once it stabilized I reviewed the whole codebase, wrote up twelve architecture decision records, and ran the split into three apps as a phased program rather than a rewrite.

Scope choices, recorded rather than rediscovered:

  • No monorepo, and no separate repo for the schema or the shared package.
  • No ORM - a typed client and per-table modules suit a dozen tables.
  • No HTTP between apps, so no service mesh and no cross-app coupling.
  • No API versioning with one first-party client - the seam is kept, not built.

Architecture

OctaK Edge architecture: the determinism boundaryData flows from fail-safe adapters over free public sources into a deterministic engine that computes every number in TypeScript on the server. A trust boundary separates that engine from the AI layer: the model reads the computed values read-only, explains the verdict with a deterministic fallback and proposes chart shapes from a closed capped list, but never writes a score, band or target. The response combines the computed numbers and the narration. Below, three independently deployed apps - a credential-free Landing site, the App that holds the product and engine, and an operator Admin behind MFA - share a single Supabase Postgres database as their only runtime surface, with no app-to-app HTTP.Browser requestData adapters · fail-safefree public sources: Yahoo · NSE/BSE · RSSa blocked source becomes a visible gapDeterministic coreDeterministic engine · TypeScriptcomputes every number:indicators · scoring · trade plan · projectionnumbers ← code  ·  words ← AIthe model never writes a numberAI layer · narration onlyAI narrationexplains the verdict (deterministic fallback)proposes chart shapes from a closed list, cappedResponseverdict · trade plan · narrative+ sources[] / unavailable[]deployment · 3 apps, 1 databaseLandingpublic · no keysAppproduct + engineAdminoperator · MFASupabase · Postgres · RLSone shared database - no app-to-app HTTP
Numbers come from code, words come from AI - across three apps over one database.

One Next.js app, no separate backend. A single per-symbol pipeline is the spine: parallel fetch across independent sources, indicators, pattern detection, then a deterministic chain - component scores, timeframe-weighted blend, confluence, recommendation, trade setup, projection. Both the analysis and position routes call it, so the two surfaces cannot diverge.

Every data adapter is wrapped in a fail-safe cache. A thrown error becomes a typed "unavailable" for that field, and the response lists what was missing. No adapter ever substitutes a default.

Screening ~300 names per request is too heavy, so a cron precomputes a universe snapshot and requests only read it; its timestamp anchors determinism. Picks are written to an append-only forward log before the market resolves them, each stamped with the engine version and a hash of the exact scoring code.

Today it runs as three apps over one Supabase project - a credential-free landing site, the product, and an operator console - sharing only the schema and one published package.

Decisions & Tradeoffs

Enforce the determinism boundary structurally, not with a system prompt

Tradeoff accepted

Every new signal must be modelled by hand, and the product cannot answer what the engine cannot compute. In return the model has no code path that writes a number - it explains computed values, and its pattern guesses are validated against a closed list at capped confidence.

Alternatives considered (3)
  • Instruct the model not to invent numbers and trust it
  • Let the model adjust a computed score when it is confident
  • Generate the verdict with the model and compute a score afterwards to justify it

One shared database is the only contract between apps; no app-to-app HTTP

Tradeoff accepted

The schema becomes a coupling point two apps can break, so migrations are expand-contract, forward-only, and owned by one repo with a CI drift check. What it buys is zero runtime coupling - no internal auth, no availability dependency between apps.

Alternatives considered (3)
  • The product exposes internal endpoints the console calls
  • A second Supabase project for the console, with replication
  • A shared service layer in front of both

Three repos, one published package; duplicate everything else on purpose

Tradeoff accepted

Contract changes are two-step and some code is maintained twice. The rule that makes it bearable: promote to the shared package only when drift would be a security or billing bug - everything else is duplicated on purpose, with a comment naming its sibling.

Alternatives considered (3)
  • A monorepo with shared workspaces
  • A fourth repo for the schema, or for the shared package
  • Git submodules for shared code

Precompute the screening universe on a schedule; requests only read it

Tradeoff accepted

Results are only as fresh as the last snapshot, so every response carries its timestamp and a flagged live-fallback path. The worker tier stays designed-for, not built - the cut line is documented with its trigger conditions.

Alternatives considered (3)
  • Scan the universe on request
  • A job queue with workers
  • A long-lived worker outside the serverless runtime

Build the whole data layer on free, non-key-gated sources

Tradeoff accepted

Sources can block or change without notice, so the adapter layer contains the blast radius and a blocked source degrades to a visible gap. The licensing question is commercial, carried as a business gate before the product charges anyone.

Alternatives considered (2)
  • A paid market-data vendor from the start
  • Official exchange feeds

Engineering Challenges

Scanning ~300 stocks on demand costs more time than a single web request has.

How it was solved

A cron builds the feature vector for the whole universe and caches it; a request reads that snapshot, filters by per-timeframe eligibility, and runs full analysis only on a handful of finalists, batched ten at a time. The snapshot timestamp anchors determinism so two requests in the same window agree.

Free data sources fail without warning, and the product still has to answer honestly.

How it was solved

A component with no inputs is dropped from the blend, not scored zero, so weights renormalize and confidence reflects real coverage. The trade-setup generator refuses with a reason when the setup is weak, and the one value that could be faked - historical accuracy - is hardcoded null and flagged as the highest fabrication risk in the file.

Splitting one app into three, without ending up with three apps that can only ship together.

How it was solved

The trap is a distributed monolith - three deploys that still have to move together. The schema got an owner (the repo with write-gravity), a single writer per table, expand-contract forward-only migrations, and a CI drift check. Rolling back app code is always safe against the newest schema, so schema rollback stops existing as a concept.

Features

  • Deterministic Edge Score: seven component scores reweighted across five holding timeframes
  • Trade plan with entry, stop, targets and reward-to-risk - or an explicit refusal when the setup is weak
  • My Position: the same analysis adapted to a real entry price and quantity
  • Edge Insight of the Day: a timeframe-specific screen over ~300 names, precomputed on a cron
  • Append-only forward log: every pick recorded before the market resolves it

Tech Stack

  • Next.js 16 (App Router)
  • React 19
  • TypeScript
  • Tailwind CSS 4
  • shadcn/ui
  • Supabase (Postgres, Auth, RLS)
  • Vercel + Vercel Cron
  • Upstash Redis
  • Zod
  • TradingView Lightweight Charts
  • yahoo-finance2
  • technicalindicators
  • Gemini / Anthropic / OpenAI (pluggable)
  • Vitest
  • Sentry

Lessons Learned

  • A constraint that lives in a prompt is a suggestion. The determinism boundary held because the model was never given a code path that could write a number.
  • Honest degradation costs less and is worth more than it looks. "Unavailable" beats a plausible default and deletes a class of bug nobody can reproduce.
  • Deferring debt with an explicit revisit trigger turns rot into a decision.

Screenshots

How OctaK Edge works - technical, fundamental and sentiment data feed a deterministic engine that computes an Edge Score the AI only explains.
A stock verdict for HDFCBANK - a Strong Buy Edge Score with supporting factors, risk factors and a structure-defined trade setup.
Edge Insight of the Day - the day's strongest setups screened across liquid NSE names, each with an Edge Score, confidence and expected move.

Contact

If you need an engineer who can own it end to end, from the interface to the infrastructure, let's talk.

Email me