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.
When Joseph asks to analyse an Indian stock, review an existing analysis, or check quarterly/technical triggers. Only NSE/BSE stocks โ never international.
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:
Also extract key summary stats from the page snapshot:
If Screener.in is blocked or data is incomplete, fall back to https://www.screener.in/company/{TICKER}/ (standalone) and flag the gap.
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:
The script produces a JSON metrics dictionary. Use these computed values in the template โ never fabricate.
Assign to one strategy bucket:
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.
> 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:
> 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):
After DMA values are extracted:
browser_vision(question="Capture the Screener.in chart with 50 DMA, 200 DMA, and Volume visible")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)
In the stock's .md file, update the Technical picture section with:
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.
On follow-up reviews, before visiting Screener.in:
.webp with vision_analyze(image_url=<path>)/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)
Every #active-position stock MUST have:
Maintain /opt/data/wiki/wealth/invest/research/watchlist.md with:
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
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.
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.
Every stock gets Pass A (fundamentals) and Pass B (technical) as **two separate,
independent runs**. Never combine them into one subagent/context.
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.
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.
.md, a cohort can be finished one stockat a time, at different times. Do NOT require all 5 in one session.
task.md) listingPass A done / Pass B pending per slug so any later session can resume.
Pass B** on just that one stock and update only its file. No cohort context needed.
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).
capture_technical.py, compute_ratios.py,extract_dma_values.js) over interactive browser poking.
delegate_task for independent extraction; do NOT delegate thetechnical screenshot (script it instead).
/opt/data/wiki/wealth/invest/research/frameworks/_template.md