Back to catalog

indian-stock-analysis

Analyse Indian stocks using Joseph's framework. Pull Screener.in data, calculate ratios, populate the template, and track review schedules. Only for NSE/BSE-listed Indian stocks.

Category ๐Ÿ’ฐ Wealth

Indian Stock Analysis

When to use

When Joseph asks to analyse an Indian stock, review an existing analysis, or check quarterly/technical triggers. Only NSE/BSE stocks โ€” never international.

Workflow

1. Pull Screener.in data

Navigate to https://www.screener.in/company/{TICKER}/consolidated/ in the browser. Use the extraction script at scripts/extract_screener_data.js โ€” run it via browser_console(expression=<script content>) to pull ALL tables as structured JSON in one shot. This captures:

  • Profit & Loss: Revenue, PAT, EPS for last 10 years (if available)
  • Balance Sheet: Reserves, Debt, Equity Capital
  • Cash Flow: CFO (Cash from Operations), Free Cash Flow
  • Ratios table: ROCE %, Working Capital Days, Debtor/Inventory/Payable Days
  • CAGR tables: Sales, Profit, Stock Price, ROE growth rates
  • Shareholding pattern: Promoters, FIIs, DIIs
  • Peer comparison table
  • Quarterly results table

Also extract key summary stats from the page snapshot:

  • Current Price, Market Cap, 52W High/Low
  • Stock P/E, Book Value, Dividend Yield, ROCE, ROE, Face Value

If Screener.in is blocked or data is incomplete, fall back to https://www.screener.in/company/{TICKER}/ (standalone) and flag the gap.

2. Calculate key ratios (use scripts/compute_ratios.py)

Pass the extracted JSON data through scripts/compute_ratios.py via execute_code (import and call compute_all()). The script computes:

  • P/E: Current Price รท TTM EPS (use Screener.in's reported P/E if available)
  • PEG: P/E รท lower of 3-year and 5-year PAT CAGR. Flag divergence >500 bps as growth deceleration.
  • P/B: Current Price รท Book Value
  • CFO/PAT: Cash from Operations รท PAT (sanity check: > 0.8 is strong)
  • CFO/OP %: Cash from Operations รท Operating Profit (quality check)
  • D/E (Debt/Equity): Borrowings รท (Equity Capital + Reserves)
  • Dividend payout ratio: Dividend % from annual P&L table
  • Median payout %: Excluding >60% outlier years โ€” to estimate sustainable dividend
  • Earnings yield: 100 รท P/E
  • Normalized dividend yield: (TTM PAT ร— median payout) รท Market Cap

The script produces a JSON metrics dictionary. Use these computed values in the template โ€” never fabricate.

3. Classify the stock

Assign to one strategy bucket:

  • Dividend income + capital compounding โ€” consistent dividends, growing earnings, low debt
  • Growth at reasonable price โ€” high earnings growth, moderate P/E
  • Cyclical / turnaround โ€” commodity/PSU, buy at cycle trough
  • Deep value โ€” P/B < 1, hidden assets, ignored by market

4. Populate the template

File: /opt/data/wiki/wealth/invest/research/{company-slug}/{company-slug}.md

Use the template at /opt/data/wiki/wealth/invest/research/frameworks/_template.md. Fill every section โ€” don't skip.

CRITICAL: Do NOT fabricate data. If Screener.in doesn't have a specific year's dividend or CFO, say so. Use ~ for approximate numbers and note them as estimates.

5. Technical analysis (if Joseph asks or for #active-position)

> WORKFLOW RULE (Joseph's preference, 13 Aug 2026): For deep-dives, run fundamental analysis and technical analysis as TWO SEPARATE passes, never in one shot.

> 1. Pass A (fundamental): Do steps 1โ€“4, WRITE the analysis file to wiki/wealth/invest/research/{slug}/{slug}.md (mark "#active-position" / technical section as pending), and STOP. This part takes ~2 min and completes reliably.

> 2. Pass B (technical): Separately (next session/turn), navigate back, run step 5 below, then APPEND the technical section + historical snapshot to the same file.

>

> Why: The technical pass (DMA extraction + ~100KB chart screenshot) is heavy. Doing it in the same context as the fundamentals routinely hits the 600s delegation/subagent timeout โ€” the file never gets written. Splitting them guarantees the fundamental file always lands. Also for numerous stocks (5+), a full fundamental+technical deep-dive per stock exceeds any single-run budget; split by phase.

>

> Known subagent stall (Aug 2026): Even on lightweight Pass A, subagents sometimes complete all the data gathering and ratio computation but then time out "while composing the long narrative" before the file-write tool call fires. Symptoms: few API calls (6โ€“8), a long gap after the last read, then status=timeout. The reliable fix: in any delegated deep-dive prompt, explicitly instruct "GATHER EFFICIENTLY THEN IMMEDIATELY WRITE the file โ€” do NOT stall composing a long narrative; write the file as your very next action once you have the numbers." Retry-stable.

If the status is #active-position or Joseph explicitly asks:

5a. Extract DMA data from the page โ€” USE THE DETERMINISTIC SCRIPT

> THE RELIABLE WAY (preferred, use this): Do the entire technical pass with the

> standalone script scripts/capture_technical.py โ€” it opens the chart, enables the

> 50/200 DMA overlays, reads the exact values from the Chart.js data layer, screenshots

> the chart, and writes the .webp โ€” all in one deterministic run. It returns

> {price, dma50, dma200, volume, png, webp}.

>

>
> # via execute_code (the terminal lifecycle guard blocks venv python):
> import subprocess, json
> r = subprocess.run(["/opt/data/.venv/bin/python",
>     "<skill_dir>/scripts/capture_technical.py", "TICKER", "slug",
>     "--date", "2026-08-13"], capture_output=True, text=True, timeout=120)
> print(r.stdout)   # JSON: price, dma50, dma200, volume, png, webp
> 

>

> This replaces the manual browser_console dance. It is idempotent (safe to re-run)

> and avoids dumping a ~100KB chart base64 into the model context (a major token-cost /

> timeout source). The script self-adds both site-packages paths, so it runs under either

> the /opt/data/.venv or /opt/hermes/.venv interpreter.

Manual fallback (only if the script can't run):

browser_navigate โ†’ https://www.screener.in/company/{TICKER}/
โ†’ Enable 50 DMA + 200 DMA checkboxes (they default to OFF after navigation)
โ†’ Set to "1Yr" view
โ†’ terminal(command="sleep 3") โ€” wait for canvas to render
โ†’ Extract DMA values via browser_console or snapshot

> IMPORTANT โ€” the chart is a Chart.js canvas. DMA values are NOT in the DOM text. The snapshot's checkbox states can be stale/misleading after you click them โ€” verify actual state via browser_console reading checked flags, and use scripts/extract_dma_values.js which reads the exact 50/200 DMA + price straight from the Chart.js data layer via Chart.getChart(canvas). Do NOT visually estimate DMA from the image, and do NOT dump the canvas base64 into context (it's ~100KB+ and floods the model / causes timeouts).

Key data to extract (from the chart data layer, NOT from visual estimation):

  • CMP (current market price)
  • 50 DMA value
  • 200 DMA value
  • Volume and % of average
CRITICAL: Never visually estimate DMA values from the chart. The vision model is unreliable at reading chart lines. Extract text values from the DOM or the chart overlay tooltip. If text extraction fails, note that DMA values are from visual estimation and flag as approximate.

5b. Capture chart screenshot

After DMA values are extracted:

  • Use browser_vision(question="Capture the Screener.in chart with 50 DMA, 200 DMA, and Volume visible")
  • ONLY Screener.in โ€” no TradingView needed. The Screener.in chart with DMAs + volume is sufficient.

Convert to WebP:

from PIL import Image
png = "/path/to/screenshot.png"
slug = "{company-slug}"
date = "{YYYY-MM-DD}"
img = Image.open(png)
img.save(f"/opt/data/wiki/wealth/invest/research/{slug}/charts/{slug}_screener_{date}.webp", "WEBP", quality=85)
os.remove(png)
File naming: {slug}_screener_{date}.webp (e.g., bpcl_screener_2026-08-02.webp)

5c. Write structured technical notes

In the stock's .md file, update the Technical picture section with:

  • Table of extracted values (CMP, 50 DMA, 200 DMA, Volume)
  • Summary of what the chart shows (price vs DMAs, trend, volume pattern)
  • Technical levels (resistance, support) with prices
  • Chart verdict โ€” does technical support the fundamental thesis?

5d. Append to Historical Snapshots

At the BOTTOM of the stock's .md file, add a new entry to the Historical snapshots section:

### {YYYY-MM-DD}

Metric Value
CMP โ‚นXXX
50 DMA โ‚นXXX
200 DMA โ‚นXXX
Volume XXX
Chart !chart
Assessment: One-line summary of what changed since last review.

The top Technical picture section is the "current" view โ€” overwrite it each review. The Historical snapshots at the bottom accumulate โ€” each review adds a new dated entry. This lets you scroll down to see the full history of what the chart looked like and what the numbers were at each review point.

5e. Reference prior snapshots for comparison

On follow-up reviews, before visiting Screener.in:

  • Read the most recent Historical snapshot entry
  • Load the prior .webp with vision_analyze(image_url=<path>)
  • Compare: what DMA crossovers happened? Did volume patterns shift? Is price at a different level vs DMAs?
  • Note specific changes in the new Assessment (e.g., "50 DMA has turned up; price now above 200 DMA vs. below last review")
Buy signal checklist โ€” reference /opt/data/wiki/wealth/invest/research/frameworks/technical-indicators-reference.md:

1. Golden Cross OR price holding above 200 DMA

2. MACD bullish crossover (line above signal)

3. Volume confirming (high on up moves, dry on pullbacks)

  • 2 of 3 โ†’ worth watching. All 3 โ†’ technicals aligned.

6. Define gates

Every #active-position stock MUST have:

  • Entry gates: What needs to be true before each tranche (fundamental + technical)
  • Hold triggers: What keeps you in the stock
  • Exit triggers: What makes you sell (be specific โ€” "dividend cut for 2 consecutive years", not "bad results")
  • Review schedule: Quarterly checks + annual deep review

7. Update watchlist

Maintain /opt/data/wiki/wealth/invest/research/watchlist.md with:

  • All stocks being tracked
  • Current status: #active-position | #watchlist | #closed
  • Next review date for each
  • Key triggers being waited on

Data sources

1. Primary: Screener.in consolidated financials (browser_navigate)

2. Fallback: Screener.in standalone financials

3. Supplementary: Company annual reports (investor relations page), NSE/BSE filings

Pitfalls

1. Screener.in is JS-heavy โ€” browser_navigate + browser_snapshot may not capture tables. Use browser_console with document.querySelector('.data-table')?.innerText to extract raw data.

2. NSE ticker vs BSE ticker โ€” Screener.in uses NSE tickers by default. If the stock is BSE-only, use the BSE code.

3. Quarterly data โ€” Screener.in consolidated view shows annual data. Check quarterly tab for recent quarters.

4. Do NOT fabricate โ€” if a specific data point (e.g., FY21 dividend, Q3 revenue) isn't available, note it as "Not available on Screener.in" rather than guessing.

5. Currency: All Indian stocks in โ‚น (Crores). Don't mix with $ or other currencies.

6. Scripts: When running extract_screener_data.js via browser_console, copy-paste the script content directly โ€” it's a self-contained IIFE. For compute_ratios.py, use execute_code to import and call compute_all() with the JSON data and page snapshot values (price, market_cap, pe, dividend_yield, roce, roe, book_value). The script handles parsing of Indian number formats (1,38,724), finding the right tables, and computing all ratios.


Repeatable batch deep-dive procedure (audited 13 Aug 2026)

Use this when Joseph asks to deep-dive several stocks of the same cohort (e.g. the 5

growth-compounder candidates). It is designed so a cohort can be spread across sessions

and so market opportunities at different times can each trigger a single-stock pass.

Two-pass split (MANDATORY โ€” non-negotiable)

Every stock gets Pass A (fundamentals) and Pass B (technical) as **two separate,

independent runs**. Never combine them into one subagent/context.

Pass A โ€” fundamentals (per stock)

1. Pull Screener.in consolidated data, compute ratios (compute_ratios.py), classify bucket.

2. Write the full analysis file wiki/wealth/invest/research/{slug}/{slug}.md immediately โ€”

end it with the marker line ## Technical picture - PENDING (Pass B).

3. Update growth-candidates.md / watchlist.md status for that slug.

4. Report one line: slug | path | status | verdict.

Pass B โ€” technical (per stock, OR all cohort in one run)

1. Use scripts/capture_technical.py via execute_code-subprocess โ†’ get

{price, dma50, dma200, volume, png, webp} deterministically. (No browser_console

base64 dance, no delegation for this step โ€” it's a script now.)

2. Append the ## Technical picture section (replace the PENDING marker) + add a

dated entry to ## Historical snapshots, embedding charts/{slug}_screener_{date}.webp.

3. If the fundamental file had an entry-plan table with PENDING (Pass B) cells, fill

them with the actual technical verdict.

Batching across sessions / opportunistic entry

  • Because each stock is its own self-contained .md, a cohort can be finished one stock

at a time, at different times. Do NOT require all 5 in one session.

  • Save an in-session TODO of remaining slugs (or a small task.md) listing
Pass A done / Pass B pending per slug so any later session can resume.
  • When a market opportunity appears (a stock dips, a result comes in), run a **single

Pass B** on just that one stock and update only its file. No cohort context needed.


Postmortem โ€” what inflated token cost / caused failures (13 Aug 2026)

These are the specific mistakes from the first cohort run, and how the above procedure

prevents each:

1. Delegating the technical (chart) pass to subagents โ†’ 6 timeouts ร— 600s.

The subagents solved the hard parts (DMA via Chart.js) but then burned the whole

budget fighting the screenshot toolchain (canvas-base64, CDP port hunting, clippped

boxes). Fix: the technical pass is now a deterministic python script, never delegated.

2. Dumping a ~100KB chart base64 into the model context. Repeatedly (multiple times)

pushed big strings into context = huge token cost. Fix: capture_technical.py writes

the .webp to disk directly; never returns base64.

3. Subagent "compose-stall": even lightweight Pass A sometimes gathered all data and

then timed out "while composing the narrative" before the file-write fired (few API

calls, long gap). Fix: prompt says **"gather efficiently then IMMEDIATELY write the

file โ€” do NOT stall composing a long narrative."** Retry-stable.

4. Loading huge context into every subagent (datapatterns.md, bpcl.md, framework,

candidates, watchlist all at once). Fix: each Pass-A subagent loads only the skill +

template + its own hint data; don't read every related file into context.

5. Running fundamental + technical in one shot. Combined job = too heavy for the

600s window. Fix: two-pass split guarantees the fundamental file always lands first.

6. Re-delegating the same stock. KPIT and Polycab each timed out once on Pass A and

got retried โ€” acceptable but wasteful. Fix: pass the write-promptly instruction and

split work; a task.md prevents redundant re-runs.

7. venv interpreter confusion. PIL and playwright live in different venvs

(/opt/hermes/.venv vs /opt/data/.venv). Fix: capture_technical.py self-adds both

site-packages paths. Run deterministic scripts via execute_code + subprocess, NOT

the terminal tool (the lifecycle guard blocks venv python).

Token-cost guardrails (summary)

  • Prefer deterministic scripts (capture_technical.py, compute_ratios.py,
extract_dma_values.js) over interactive browser poking.
  • Keep each stock's work isolated and small; report only one line back to the parent.
  • Never paste large base64/json blobs into context โ€” write to disk, read the small value.
  • Batch work: use delegate_task for independent extraction; do NOT delegate the

technical screenshot (script it instead).


Template location

/opt/data/wiki/wealth/invest/research/frameworks/_template.md