Option Chain & OI Analysis
Option contracts, ATM/top-options feeds, and OI profile/change/analysis, PCR, ATM straddle/IV.
fetchOptionDetails
Option contracts for the underlying symbol's chain — called when the user opens the option chain.
Future<String> fetchOptionDetails({required String underlyingSymbolId});The first element of the returned array must be the underlying symbol info (no optType
field) — the chart uses it to resolve the correct spot price. All subsequent elements are
option contracts:
[
{ "id": "NIFTY", "name": "NIFTY 50", "lotSize": 50, "precision": 2, "tickSize": 0.05 },
{ "id": "NIFTY25MAR23000CE", "name": "NIFTY 23000 CE Mar 27", "strike": "23000", "expiry": "2025-03-27", "optType": "CE", "lotSize": 50, "precision": 2, "tickSize": 0.05, "exchange": "NSE" }
]Return "[]" when no option chain is active.
Streaming & Greeks
fetchOptionDetails is a one-shot snapshot — there's no separate streaming method for the
option chain. Live updates (LTP, change, OI, and Greeks) arrive through the same
marketDataStreamer tick stream
every other price on the chart uses:
- When the option chain opens (or the user picks a different expiry/CE-PE side), the SDK adds
every visible contract's
id— theids from the arrayfetchOptionDetailsreturned, plus the underlying's ownid— to its one shared symbol subscription. See When subscribeMarketData/unsubscribeMarketData actually fire for exactly when that subscription changes. - Push a tick for each of those
ids the same way you push any other tick — for option contracts specifically, also include the Greeks flat on that same tick object (not nested under agreekskey):itm,iv,delta,gamma,theta,rho,vega,highIV,lowIV. Full field reference: Live ticks (marketDataStreamer). - There's no separate "Greeks changed" event — a tick with updated Greeks and unchanged LTP is a completely normal, valid update.
atmSymbols
[ceSymbolInfo, peSymbolInfo] for the ATM strike, nearest expiry — required for the
right panel of the Index + Call/Put or Call + Put layouts. Read
when the user switches to one of those layouts, so it's safe to resolve after background
data finishes loading. Resolve to "[]" to leave the right panel blank (null also works,
see Always return empty, never null).
Future<String?> get atmSymbols;Same symbolInfo shape as above, array of exactly two: [ceSymbolInfo, peSymbolInfo].
atmSymbolsFor
The parameterized replacement for atmSymbols above — scoped to an explicit underlyingId
instead of "whichever underlying is currently charted."
Future<String?> atmSymbolsFor(String underlyingId);Same shape as atmSymbols: [ceSymbolInfo, peSymbolInfo] for underlyingId's ATM strike,
nearest expiry. Resolve to null to leave the right panel blank for that underlying.
chartTopOptions
Top options ranked by active volume — powers the Index + Call/Put/Terminal "Top 5" panel. Called once when the panel opens, not re-fetched afterward.
Future<String> get chartTopOptions;[
{ "symId": "NIFTY25MAR23000CE", "name": "NIFTY 23000 CE Mar 27", "optType": "CE", "exchange": "NSE" },
{ "symId": "NIFTY25MAR22000PE", "name": "NIFTY 22000 PE Mar 27", "optType": "PE", "exchange": "NSE" }
]| Field | Type | Accepted values |
|---|---|---|
symId | String | required; note the key is symId, not id |
name | String | required; display name |
optType | String | required; "CE" or "PE" |
exchange | String | required; exchange identifier |
undID | String | optional; underlying symbol id. When non-empty, the SDK drops any candidate whose undID doesn't match the current underlying. Omit or leave empty to skip this filtering. |
The SDK takes the first 5 candidates per option type, in the order you return them, and only re-sorts within that set of 5 as live volume ticks arrive — it never looks past the first 5 of each type. Return your candidates already ordered by your own best-known priority (e.g. volume, open interest); anything beyond the first 5 per type is discarded and never reconsidered, even if it later turns out to have higher volume.
chartTopOptionsFor
The parameterized replacement for chartTopOptions above — scoped to an explicit
underlyingId. Same contract and shape, including the first-5-per-type rule.
Future<String> chartTopOptionsFor(String underlyingId);Same shape as chartTopOptions. undID on each candidate is no longer a filtering signal
here — you've already scoped the array to underlyingId yourself — but the field is still
accepted if you include it.
OI Profile / OI Change / OI Analysis
Three related methods — implement whichever your app's features need; each resolves to
"{}" (or is simply never called) when unused.
| Feature | Method | Scope |
|---|---|---|
| OI Profile (current OI) | fetchOI | point-in-time snapshot, summed across the expiries you pass |
| OI Profile (change) / OI Change Analysis | fetchOIChange | OI change over a time window, summed across expiries |
| OI Analysis screen | fetchOIAnalysis | OI, OI-change, and previous-OI per strike, for the strikes around ATM you return, for a single expiry over a time window |
Future<String?> fetchOIAnalysis({
required String underlyingSymbolId,
required String expiry,
required int timeFrom,
required int timeTo,
});
Future<String?> fetchOIChange({
required String underlyingSymbolId,
required List<String> expiries,
required int timeFrom,
required int timeTo,
});
Future<String?> fetchOI({
required String underlyingSymbolId,
required List<String> expiries,
});timeFrom/timeTo are unix seconds. fetchOIAnalysis does not receive a strike range —
your implementation derives the current ATM strike itself (the same way atmSymbols does)
and picks however many strikes around it to expose (e.g. ~43). The SDK does not trim this
for you — every strike you return renders, in a scrollable, ATM-centered chart. Return only
what you actually want shown.
fetchOIAnalysis resolves to:
{ "calls": { "23450": { "oi": 87425, "oiChg": 3770, "prevOi": 83655 } }, "puts": { "23450": { "oi": 0, "oiChg": 0, "prevOi": 0 } } }fetchOIChange resolves to:
{ "calls": { "23450": 3770 }, "puts": { "23450": 802750 } }fetchOI resolves to:
{ "calls": { "23450": 87425 }, "puts": { "23450": 3429205 } }All three resolve to "{}" when no data is available for the window/point (null also
accepted — see Always return empty, never
null). Strike keys are strings (parsed as double),
values are double/int per the field.
What the fields mean
oi— open interest: the number of currently outstanding contracts at that strike (not volume traded — a contract stays counted until closed, not just traded once). Rising OI with a rising price is read as bullish conviction (new money entering long); rising OI with a falling price is read as bearish (new shorts). Falling OI generally means positions are being closed/unwound, regardless of price direction.oiChg— the change inoiover the method's window:timeFrom..timeToforfetchOIAnalysis/fetchOIChange, "since a reference point you define" forfetchOI(it's a point-in-time snapshot, not itself a window). Positive = OI increased (contracts added over the window); negative = OI decreased (contracts closed).prevOi(fetchOIAnalysisonly) — the OI value immediately before this window started, i.e.oi - oiChgat the window's start. Lets the UI show a from/to comparison without a second request.
Which screen reads which method
| Screen | Method | Why that one |
|---|---|---|
| OI Profile bars (current OI) | fetchOI | Needs only "OI right now," no history — a single point-in-time snapshot, cheapest to serve |
| OI Profile bars (change) / OI Change Analysis | fetchOIChange | Needs "how much did OI move," not the absolute levels |
| OI Analysis screen (full detail) | fetchOIAnalysis | Needs all three together — absolute OI, its change, and the pre-change baseline — for a side-by-side reading |
Implement only the method(s) backing the screens you actually enable — the other(s) can resolve
to "{}"/never be called, per the table at the top of this section.
PCR / ATM straddle / ATM IV (Analysis screen)
Future<String> fetchPcrIntraday();
Future<String> fetchAtmStraddleIntraday();
Future<String> fetchAtmIvIntraday();No arguments.
fetchPcrIntraday — historical Put/Call OI ratio, pre-computed per strike range, oldest
first:
[{ "time": 1721460600000, "price": 24230.5, "pcr": { "3": 0.42, "5": 0.51, "10": 0.63, "20": 0.7, "all": 0.75 } }]fetchAtmStraddleIntraday — keyed by expiry date, one minute sampling:
{ "2025-03-27": [{ "time": 1721460600000, "atmStraddlePrice": 435.2 }] }fetchAtmIvIntraday — one minute sampling:
[{ "time": 1721460600000, "atmIv": 21.8 }]time is epoch milliseconds in all three. Return "[]" (or "{}" for the straddle map)
when no historical data is available.
fetchPcrIntradayFor / fetchAtmStraddleIntradayFor / fetchAtmIvIntradayFor
The parameterized replacements for the three methods above — each takes an explicit
underlyingId instead of implicitly answering for "whatever's currently charted." Same
contract, same shapes, same empty-value conventions as their ambient counterparts.
Future<String> fetchPcrIntradayFor(String underlyingId);
Future<String> fetchAtmStraddleIntradayFor(String underlyingId);
Future<String> fetchAtmIvIntradayFor(String underlyingId);If your data source only ever serves a single underlying's worth of this history, it's fine
to ignore underlyingId and return the same series regardless of its value.