# Requirements — NSE Deep Research Engine (v1)

## Glossary
- **BTST**: Buy Today Sell Tomorrow — buy near close, exit next trading day.
- **Swing call**: Multi-day position (3–30 trading days) with defined entry zone, SL, target.
- **Universe**: NSE F&O stocks ∪ Nifty 500 constituents (deduped), refreshed monthly.
- **Hit**: BTST — next-day exit above entry after costs proxy (0.3%). Swing — target hit before SL.
- **Trading day**: Mon–Fri excluding NSE holidays (holiday table in DB).

## Requirement 1: Market Data Ingestion
1.1 WHEN the `job:eod-ingest` runs on a trading day (5:45 PM IST), THE **DataIngestor** SHALL persist OHLCV for every universe symbol for that day.
1.2 WHEN the NSE bhavcopy for the day is available, THE **DataIngestor** SHALL persist delivery quantity and delivery % per symbol.
1.3 WHEN bhavcopy is not yet available at run time, THE **DataIngestor** SHALL retry every 15 min up to 4 times before alerting failure.
1.4 WHEN `job:intraday-snapshot` runs at 2:55 PM IST, THE **DataIngestor** SHALL persist current price, day high/low, and cumulative volume for every universe symbol.
1.5 THE **DataIngestor** SHALL maintain an NSE holiday table and all jobs SHALL exit cleanly (no alerts, no output) on non-trading days.

## Requirement 2: Fundamentals
2.1 WHEN `job:fundamentals-refresh` runs (Sunday 10 AM + manual trigger), THE **FundamentalsStore** SHALL persist for each universe symbol: last 8 quarterly revenue/PAT figures, TTM EPS, TTM PE, ROE, debt-to-equity, promoter holding %, and market cap.
2.2 WHEN a data point is unavailable for a symbol, THE **FundamentalsStore** SHALL store NULL and continue (never abort the batch).
2.3 THE **FundamentalsStore** SHALL record `fetched_at` per symbol so staleness is queryable.

## Requirement 3: BTST Scanner
3.1 WHEN `job:btst-scan` runs at 3:00 PM IST on a trading day, THE **BtstScanner** SHALL score every universe symbol and select at most 5 candidates.
3.2 THE **BtstScanner** SHALL only consider symbols meeting ALL hard filters: (a) last price ≥ ₹50, (b) today's volume ≥ 1.5× its 20-day average, (c) price within 1.5% of day high at scan time, (d) day change between +1% and +8%, (e) 20-day avg delivery % ≥ 30, (f) no results announcement scheduled for today or the next trading day.
3.3 THE **BtstScanner** SHALL rank passing symbols by a composite score (volume surge × closing strength × 5-day momentum, weights configurable in `config/btst.json`).
3.4 FOR each selected candidate, THE **BtstScanner** SHALL compute entry (last price), SL (min of day-low and entry −1.5%), and indicative next-day target (+1.5% to +3% band).
3.5 THE **BtstScanner** SHALL persist all candidates (including score components) before ResearchWriter is invoked.

## Requirement 4: Swing Scanner
4.1 WHEN `job:swing-scan` runs at 6:30 PM IST on a trading day, THE **SwingScanner** SHALL select at most 8 candidates.
4.2 THE **SwingScanner** SHALL apply a fundamentals gate first: TTM PAT positive, latest-quarter YoY revenue growth ≥ 0%, debt/equity ≤ 1.5 (banks/NBFC exempt from D/E), promoter holding not down >2% QoQ.
4.3 ON the fundamentals-passing set, THE **SwingScanner** SHALL require a technical trigger, any of: (a) close above 20-day high on ≥1.5× avg volume, (b) reclaim of 50-DMA after ≥10 days below with RSI(14) 50–65, (c) tight 5-day range (<3%) resolving upward.
4.4 FOR each candidate, THE **SwingScanner** SHALL compute entry zone (trigger close ±1%), SL (swing low or −5%, whichever is nearer), target 1 (+8%), target 2 (+15%), horizon (30 trading days max).
4.5 THE **SwingScanner** SHALL not re-emit a symbol that already has an OPEN swing call.

## Requirement 5: AI Research Notes
5.1 WHEN candidates exist after a scan, THE **ResearchWriter** SHALL produce one structured note per candidate containing: 3-line thesis, key financials table (from FundamentalsStore — MUST match stored values), 3 identified risks, recent-news summary (via web search), and a conviction grade (A/B/C).
5.2 THE **ResearchWriter** SHALL request strict JSON output and validate it against a schema; on invalid JSON it SHALL retry once, then fall back to publishing the candidate without a note (never block the brief).
5.3 THE **ResearchWriter** SHALL complete all BTST notes within 8 minutes of scan completion (deadline guard: skip notes still pending at 3:10 PM).
5.4 Every note SHALL end with the fixed line: "Paper research — not investment advice."

## Requirement 6: Telegram Delivery
6.1 WHEN the BTST scan + notes complete, THE **TelegramPublisher** SHALL send one consolidated BTST brief by 3:12 PM IST (candidates, entry/SL/target, grade; full notes as a follow-up message per stock).
6.2 WHEN the swing scan + notes complete, THE **TelegramPublisher** SHALL send the Swing brief by 7:00 PM IST.
6.3 WHEN a scan produces zero candidates, THE **TelegramPublisher** SHALL send a one-line "No candidates today" message (so silence always means failure, never ambiguity).
6.4 THE **TelegramPublisher** SHALL be idempotent per (brief_type, date): re-runs MUST NOT duplicate messages.
6.5 WHEN any job throws, THE **Scheduler** SHALL send a Telegram error alert with job name and error summary.

## Requirement 7: Outcome Tracking
7.1 WHEN `job:evaluate-outcomes` runs at 5:50 PM IST, THE **OutcomeTracker** SHALL close every BTST call from the previous trading day using that day's data: exit at open if gap > +1%, else at day close; record P&L % net of 0.3% cost proxy and mark HIT/MISS.
7.2 FOR each OPEN swing call, THE **OutcomeTracker** SHALL check the day's high/low against SL and targets, close calls accordingly (SL_HIT / T1_HIT / T2_HIT / EXPIRED after horizon), and record P&L %.
7.3 EVERY Friday at 6:00 PM IST, THE **OutcomeTracker** SHALL send a weekly performance summary: BTST hit rate, swing open/closed counts, cumulative paper P&L %, best/worst call.
7.4 Recommendation rows SHALL be immutable after publication; outcomes stored in a separate table keyed to the recommendation.

## Requirement 8: Operations
8.1 THE system SHALL expose `GET /health` (DB + last-job status) and `POST /jobs/:name/run` (token-protected manual trigger) via Fastify on localhost.
8.2 ALL secrets SHALL come from `.env`; the repo SHALL contain `.env.example` with placeholder keys.
8.3 EACH job SHALL write a per-run log line (start, counts, duration, result) to `logs/<job>.log`.
