thetalens

Methodology

Every metric, threshold, and detection rule used by thetalens, spelled out. No proprietary scores. If you disagree with a definition, the filter controls let you set your own thresholds.

Data source and refresh cadence

All data is pulled from NSE end-of-day bhavcopy — the official closing snapshot published by the exchange. We use the UDiFF Common Bhavcopy format (CSV), which NSE adopted in mid-2024 after retiring the older daily-bhavcopy files.

  • Equity OHLC: archives.nseindia.com/content/cm/BhavCopy_NSE_CM_0_0_0_{YYYYMMDD}_F_0000.csv.zip
  • F&O chain: archives.nseindia.com/content/fo/BhavCopy_NSE_FO_0_0_0_{YYYYMMDD}_F_0000.csv.zip

The pipeline runs nightly at 18:30 IST Mon–Fri via GitHub Actions. If NSE is unavailable or publishes malformed data, yesterday's snapshot remains live until the next successful run. The last-updated timestamp is visible on every page; if it lags more than one trading day, treat the data as stale.

We do not use any broker API (Zerodha Kite or otherwise) for public data display — redistributing broker feeds is against terms of service. All prices on the site are from the public NSE archive.

Stock universe

The screener covers the 49 most actively-traded F&O stocks by options turnover. The list is hardcoded and refreshed manually on a roughly monthly cadence — if a stock's F&O trading volume collapses we remove it; if a newer name becomes highly liquid we add it. Index options (NIFTY, BANKNIFTY, etc.) are not in scope — they use different strategy mechanics.

Source list kept in src/lib/universe.ts and scripts/universe.py, which are kept in sync by hand. Tata Motors was dropped after its April 2025 CV/PV demerger — the old tradingsymbol no longer exists on NSE, the successor ticker has insufficient history for our detectors.

Annualized yield

yield = (premium / strike) × (365 / days_to_expiry) × 100

Premium is the contract's closing trade price (ClsPric) from the EOD bhavcopy. This is the single most-used number in the table, so note what it is and isn't:

  • It assumes you sell this contract today, keep the full premium at expiry (no assignment), and could roll the same yield repeatedly for 365 days. Most short-dated trades can't actually be rolled 30+ times at the same yield — it's a normalisation device, not a forecast.
  • It ignores assignment risk entirely. If you're assigned at a loss, the "yield" stops being the whole story.
  • It ignores STT, brokerage, and taxes. Net of those, realised return is lower.
  • Short-DTE contracts look dramatically better because of the 365/DTE multiplier. An 11-DTE contract at 3% yield annualises to 108%; a 45-DTE contract at the same 3% annualises to just 24%. That difference is mechanical, not predictive.

Capital and premium economics

Per-share premium in rupees is easy to underweight mentally. We show the total cash figures in the row detail modal so the decision is framed correctly:

  • Cash required= strike × lot size. This is the cash you'd need to reserve in case of assignment (for a cash-secured put) or the value of the shares you'd need to already hold (for a covered call).
  • Premium earned = closing premium × lot size. Total rupees you collect for writing one contract, before charges.
  • Return on capital (per trade) = premium earned / cash required = premium / strike.
  • Effective cost basis (if assigned, PE) = strike − premium. The breakeven spot below which the leg starts producing a mark-to-market loss.
  • Effective exit price (if assigned, CE)= strike + premium. Above this spot you're leaving upside on the table when shares are called away.

Implied volatility (strike and ATM)

NSE's EOD bhavcopy does not include IV, so we compute it ourselves. Two numbers surface in the UI:

"IV at this strike" (vol smile)

Per-strike IV uses Black-76 with an implied forward price derived from put-call parity, not plain Black-Scholes on spot. For each expiry we solve:

F = Catm − Patm + Katm × e−rT

Then invert Black-76 at every strike using that forward to find IV. For strikes where both call and put legs have prices, we average the two side-derived IVs (mid-IV). If only one leg is liquid, we fall back to that leg. The forward-based approach cancels dividend distortions that break plain Black-Scholes put-call parity on dividend-paying stocks (validated against Sensibull at ±2 pp for HCLTECH / RELIANCE / HDFCBANK / INFY at near-ATM strikes).

"ATM mid-IV" (symbol reference)

Single-number representation of this stock's current volatility. Mean of call-IV and put-IV at the strike closest to spot. Useful for comparing stocks against each other or tracking a stock's vol over time.

Risk-free rate held constant at 7% (approximately India 10Y G-Sec yield). We do not model discrete dividends explicitly, but the parity-based forward absorbs their first-order effect. Some residual skew remains at deep-OTM strikes on stocks with very large dividends.

Breakeven buffer

How far the spot can move against the position before the trade starts losing money. Always positive for OTM contracts.

PE: buffer% = (spot − (strike − premium)) / spot × 100
CE: buffer% = ((strike + premium) − spot) / spot × 100

Larger buffer means more cushion before the trade turns red. It combines the OTM distance with the premium's safety margin. Two contracts with the same annualised yield but different buffers are different risk profiles.

Out-of-the-money default (important)

The screener defaults to showing only OTM contracts. In-the-money contracts carry large intrinsic value, which makes the annualized yield number misleading — a 1000% "yield" on a deep-ITM put is just the intrinsic value getting divided by the strike and multiplied by the DTE factor. You can't realise that yield because you'd be assigned immediately at a loss equal to the intrinsic value.

You can toggle OTM-only off in the filter panel, but we surface time value and intrinsic value separately in the row detail so you can see what you're looking at.

Support and resistance detection

Structural levels are detected from the last 180 trading days of equity OHLC — not from moving averages, which are dynamic and smooth over real price action. The pipeline runs nightly after each EOD pull.

Swing detection

A bar is a swing low if its low is ≤ the minimum of the 5 bars on each side, with strict inequality on at least one side (so flat-plateau interiors aren't counted, but a double-bottom where two bars tie at the minimum both register). Mirror for swing highs.

Additionally, a candidate swing requires ≥2% bounce confirmation: price has to move away from the swing level by at least 2% within 5 bars. This filters out wicks during trending moves that print a local minimum but never find buyers.

Clustering

Qualifying swings are clustered at 1% price tolerance. Two swings within 1% of each other merge into a single level centred on their mean price. We widened from 0.5% after observing that large-cap stocks regularly print legitimate touches of the same zone that differ by ~1% (INFY's 1542 and 1557 were the canonical example — same level, separate clusters under 0.5%).

Scoring

strength_score = touch_count × exp(−bars_since_last_touch / 90)

Recent multi-touch clusters score highest. Decay constant is 90 trading bars (half the lookback window), so a 4-touch cluster from 125 bars ago still scores ~1.0 — the "strong" bucket.

Strength tiers

  • Strong (solid amber badge): score ≥ 1.0 — multiple recent touches, or a single very recent touch
  • Moderate(outline badge): 0.4 ≤ score < 1.0
  • Weak(muted badge): score < 0.4 — single old touch

The row expansion shows every detected level for the stock — qualifying ones drive the filter, non-qualifying ones still appear for context with the reason they were excluded. Tier is visual weight, not a hard filter.

Polarity flip

A level with ≥2 touches that price has since crossed switches its role:

  • Former support that price broke below → emerges as resistance (price now has to reclaim it on the way up)
  • Former resistance that price broke above → emerges as support

These appear with a "polarity-flipped" indicator in the detail modal. Single-touch flips are ignored — not strong enough to carry the flipped role without adding noise.

Range extremes

The 180-day low is always surfaced as a support (the most recent capitulation low is structurally significant even at 1 touch). Same for the 180-day high as a resistance. These bypass the single-touch-min-age filter.

Strike selection: the at-support rule

Each row on the home list represents one stock with a strike sitting at a validated support (or resistance, when you toggle to calls). A level qualifies if it passes two gates:

  • strength_score ≥ 0.5 — moderate-tier or better
  • touch_count ≥ 2 — price has tested this level at least twice. Single-touch swings get rejected even when they score highly on recency alone, because one bar can bounce off coincidence; two is the minimum that looks like memory.

Given a qualifying support, a PE strike is eligible when all three hold:

strike < support_level
(spot − strike) / spot ≥ 0.03     (3% OTM gate)
(support − strike) / support ≤ 0.05   (5% cushion cap)

The band is one-sided by design: the support must sit above the strike so it provides cushion ahead of assignment, not absorb it on both sides. The anchor strike shown on the row is the highest eligible strike — closest to the level, maximum use of the cushion. Alternate strikes deeper in the same band are visible inside the row expansion.

For calls the rule flips: resistance sits below the strike,strike > resistance with the same 3% OTM gate and 5% cushion cap on the other side. Same structural idea, mirrored.

The 5% cap is deliberately generous — cheap stocks with sparse strike grids sometimes force the nearest eligible strike 2–3% below the support. That still qualifies; the home row just flips its "driven by" chip to an amber extra cushion visual so you notice the looser fit.

Liquidity

liquid = open_interest > 5,000 AND 5-day avg volume > 1,000

The original spec included a bid-ask-spread component ((ask − bid) / mid × 100 < 2) but the EOD bhavcopy doesn't publish bid or ask — those are only available via intra-day feeds we don't ingest. OI and volume together are a strong proxy for the same signal.

Earnings flag

Next-earnings dates are pulled daily from NSE's corporate events calendar (the same feed that powers the "board-meeting intimation" announcements). A contract is flagged earnings before expiryif the stock has an earnings announcement scheduled between today and the contract's expiry date.

The "Ex-Earnings" attribute filter passes only contracts where no earnings fall before expiry — useful if you want to avoid earnings IV crush on short-dated trades.

Row structure and sort

The home list shows one row per stock with a qualifying anchor support. The anchor strike on each row is the highest eligible strike in the anchor's band — the closest-to-support strike, not the highest-yield strike. Alternate strikes deeper in the same band, plus every other level detected for the stock (qualifying or excluded with reason), live inside the expansion when you click the row.

Sort cycle on any sortable column: first click descending, second click ascending, third click resets to the default (annualised-yield descending). Sorting re-orders the one-row-per-stock list by the anchor strike's value in that column.

Validation

IV computations were cross-checked against Sensibull's ATM IV column on 2026-04-17 across four liquid stocks:

StockSensibull ATM IVOur mid-IVDiff
HCLTECH33.9%35.81%+1.91 pp
RELIANCE21.4%22.37%+0.97 pp
HDFCBANK31.7%32.51%+0.81 pp
INFY34.3%34.24%−0.06 pp

Mean absolute difference: 0.94 pp. All within ±2 pp.

What this tool does not do

  • It does not produce a single composite "score" that sums attributes into a ranking. Filters stand side-by-side; you combine them.
  • It does not recommend trades, suggest specific strikes, or maintain a "today's candidates" list. Every row shown is there because it matches the filters you selected.
  • It does not handle US options, commodities, currency derivatives, or index options (NIFTY/BANKNIFTY/etc.). Indian F&O stocks only.
  • It does not include Greeks (delta, theta, gamma, vega). On the P1 roadmap if user feedback indicates demand.
  • It does not back-test or track historical performance of any strategy. Different product.
  • It does not model discrete dividends explicitly. The forward derivation from put-call parity absorbs dividend PV at the ATM strike, which is a good first-order correction for near-ATM IVs, but residual skew at deep-OTM strikes on heavily-dividend-paying stocks is not fully modeled.

Not investment advice

This is a research and filtering tool, not investment advice. The operator is not registered with SEBI as a Research Analyst or Investment Adviser. Options trading involves substantial risk of loss including loss of principal. Every decision and its outcome are yours. Consult a SEBI-registered adviser before placing any trade.

Full context on the About page.