Chart Data
symbolInfo, marketTimings, loadData, and the live tick stream — the baseline feed every screen needs.
symbolInfo
The symbol being charted (option, future, equity, or index).
String get symbolInfo => jsonEncode({
'id': 'RELIANCE',
'name': 'Reliance Industries',
'lotSize': 1,
'precision': 2,
'tickSize': 0.05,
'exchange': 'NSE',
'expiry': '', // e.g. '2025-03-27' for a derivative; empty for this equity
'strike': '', // e.g. '23000' for an option; empty for this equity
'optType': '', // e.g. 'CE' or 'PE' for an option; empty for this equity
'weekly': '', // e.g. 'Y' marks a weekly expiry; empty for this equity
'undID': '', // e.g. 'NIFTY' for a derivative; empty for this equity
'description': '', // e.g. 'Reliance Industries Limited'
'trdUnit': '', // e.g. 'EQ'
});| Field | Type | Accepted values |
|---|---|---|
id | String | required; unique symbol ID used for all data operations |
name | String | required; display name shown on the chart |
lotSize | int | optional; minimum tradeable unit, defaults to 1 if omitted |
precision | int | optional; decimal places for price display, defaults to 2 if omitted |
tickSize | double | optional; minimum price movement, defaults to 0 if omitted |
exchange | String | optional; exchange identifier (e.g. "NSE", "BSE") — see the note under marketTimings once your host serves more than one exchange |
expiry | String | optional; expiry date for derivatives ("YYYY-MM-DD"), empty for equities |
strike | String | optional; strike price for options, empty otherwise |
optType | String | optional; "CE", "PE", or empty for a non-option symbol |
weekly | String | optional; any non-empty value marks a weekly expiry, empty otherwise |
undID | String | optional; underlying symbol ID for derivatives, empty for equities |
description | String | optional; defaults to "" if omitted |
trdUnit | String | optional; defaults to "" if omitted |
underlyingSymbolInfo (same shape, String?) is the underlying when symbolInfo is itself a
derivative; return "" (empty) when symbolInfo is already the underlying (equity or index).
null is also accepted — see the callout below — but there's no reason to reach for it.
underlyingSymbolInfoFor
The parameterized replacement for underlyingSymbolInfo above — resolves the underlying for
an explicit symbolId instead of "whatever's currently charted." Called every time the SDK
needs a symbol's underlying, starting with the very first chart load, so implement it before
shipping against this SDK version — there's no partial-integration path for this one.
Future<String?> underlyingSymbolInfoFor(String symbolId);Same shape as symbolInfo, same null convention as underlyingSymbolInfo above — null when
symbolId is itself the underlying (equity or index).
A missing implementation fails loudly, not gracefully
Unlike the ambient members on this page, nothing sits in front of this call — no
client-side filtering, no cached fallback. A host that doesn't implement it gets an unhandled
platform-channel exception (Android/iOS) or a JS TypeError: ... is not a function (web) the
instant the chart tries to resolve any symbol's underlying, which happens on chart init —
so the whole chart fails to render, not just the underlying-dependent parts of it.
Symbol lists
optionSymbols, futureSymbols, and indexSymbols — resolved once at startup, alongside
symbolInfo and marketTimings.
String get optionSymbols => '[]'; // see shape below
String? get futureSymbols => null;
String? get indexSymbols => null;Each resolves to a JSON array of symbol objects, same shape as symbolInfo:
[
{ "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", "undID": "NIFTY" }
]| Field | Type | Accepted values |
|---|---|---|
optionSymbols | String | required; option contracts for the underlying symbol's chain, "[]" when no option chain is active — on web, every entry's undID must match the underlying's id (see callout below) |
futureSymbols | String? | optional; future contracts for the underlying symbol, null or "[]" when none exist — same per-entry undID requirement on web |
indexSymbols | String? | optional; every index symbol known to the app (not just the charted one), null or "[]" when not applicable |
Web: undID is required on every optionSymbols/futureSymbols entry
On web, the bridge scopes optionSymbols/futureSymbols to whichever underlying is currently
charted by filtering on each entry's undID — it drops any entry whose undID doesn't match the
current underlying's id. If your entries omit undID (it defaults to empty, which matches
nothing), every entry gets filtered out: the option chain renders blank, and OI Profile/OI
Change never even get called, since they require a resolved option chain first. There's no error
for this — it fails silently. Native (Android/iOS) does not do this filtering; undID there is
purely informational. This callout describes optionSymbols/futureSymbols only — see
optionSymbolsFor/futureSymbolsFor below for the parameterized replacements, which remove
this filtering step entirely.
optionSymbolsFor / futureSymbolsFor
The parameterized replacements for optionSymbols/futureSymbols above — scoped to an
explicit underlyingId instead of relying on client-side filtering by undID.
Future<String> optionSymbolsFor(String underlyingId); // non-nullable, "[]" when empty
Future<String?> futureSymbolsFor(String underlyingId);Same shape as optionSymbols/futureSymbols above. Unlike the ambient members, you are
responsible for scoping the result to underlyingId yourself — on every platform, not just
web, there is no filtering step left downstream of this call. Return "[]" (options) or
null/"[]" (futures) when nothing matches underlyingId.
Always return empty, never null
Every method that can be "empty" — futureSymbols, indexSymbols, underlyingSymbolInfo on
this page, plus atmSymbols, fetchOIAnalysis, fetchOIChange, and
fetchOI — accepts
null as an alias for empty, but you never need to use it: "[]" (arrays), ""
(underlyingSymbolInfo), or "{}" (the OI objects) all work identically. optionSymbols is
non-nullable and must always be "[]" when there's no option chain — it has no null option at
all. On web, the bridge also normalizes any null a host does return, so nothing breaks either
way — but there's no reason to write null when the empty form is exactly as simple.
indexSymbols hides the Volume indicator
Any symbol whose id appears in indexSymbols has its Volume indicator made unavailable — spot
indices (e.g. NIFTY, BANKNIFTY) have no traded volume of their own. An option or future on an
index still has real volume and stays unaffected. Leave indexSymbols empty/null if this
doesn't apply to your integration.
marketTimings
Exchange calendars, as a JSON map keyed by exchange code. This is a map (not a single
calendar) because a derivative and its underlying can trade on different exchanges with
different session hours — e.g. NSE and NFO, after SEBI's Call Auction Session diverged their
timing. Look up a specific exchange's calendar with marketTimingFor(exchange).
String get marketTimings => jsonEncode({
'NSE': {
'timezone': 'Asia/Kolkata',
'sessions': [
['0915-1530'], ['0915-1530'], ['0915-1530'], ['0915-1530'], ['0915-1530'], [], [],
],
'holidays': ['20260126'],
'special': { '20260201': ['0915-1530'] },
},
'NFO': {
'timezone': 'Asia/Kolkata',
'sessions': [
['0900-1530'], ['0900-1530'], ['0900-1530'], ['0900-1530'], ['0900-1530'], [], [],
],
'holidays': ['20260126'],
'special': { '20260201': ['0900-1530'] },
},
});| Field | Type | Accepted values |
|---|---|---|
| (map key) | String | exchange code (e.g. "NSE", "NFO"), matched case-insensitively against symbolInfo.exchange |
timezone | String | required; IANA timezone name (e.g. "Asia/Kolkata") |
sessions | List<List<String>> | required; 7 entries, index 0 = Monday … index 6 = Sunday; each entry is a list of "HHMM-HHMM" ranges (multiple sessions per day supported); empty list = non-trading day |
holidays | List<String> | required (may be []); dates in "YYYYMMDD" format, overrides the regular weekly schedule |
special | Map<String, List<String>> | required (may be {}); keyed by "YYYYMMDD", replaces that date's regular sessions with the given "HHMM-HHMM" ranges |
Required for live streaming — and now fails loudly, not silently
Live ticks only update the chart while the resolved exchange's calendar says the current time
is inside a trading session — this is what stops a stale reconnect tick from plotting outside
market hours. Include an entry for every exchange that ANY symbol reachable during the
session might resolve to — not just the main chart's own symbol and its underlying, but also
anything reachable via the option chain, scalper panels, or symbol search. A missing or empty
exchange key now throws — an assert in debug builds (with a message listing which keys the
host actually sent), a TypeError in release — the instant that exchange's calendar is
looked up. This is a deliberate change from the old single-calendar contract, where an
empty/default value silently stopped ticks reaching the candle series with no error or log:
the gap now surfaces immediately as a crash instead of a silent stall, so the fix is almost
always to add the missing exchange's entry to marketTimings, not to catch the error. Once
your integration spans more than one exchange, symbolInfo.exchange (and every other
symbol's exchange field — option-chain entries, search results, etc.) becomes effectively
required, since it's the lookup key into this map.
loadData
Called for historical OHLCV bars — initial load, on scroll (older bars), and on timeframe change.
Future<String> loadData({
required String symbolId,
required int from,
required int to,
required int intervalSeconds,
required int requiredBars,
});| Argument | Type | Meaning |
|---|---|---|
symbolId | String | which symbol's OHLCV to fetch |
from | int | window start, epoch milliseconds |
to | int | window end, epoch milliseconds |
intervalSeconds | int | bar size in seconds |
requiredBars | int | minimum bar count the chart needs for this window |
Returns a JSON-encoded array of arrays, oldest first, each bar
[time, open, high, low, close, volume]:
[
[1700000000000, 2400.0, 2420.0, 2390.0, 2410.0, 150000],
[1700000060000, 2410.0, 2430.0, 2405.0, 2425.0, 80000]
]time is the bar's open timestamp in epoch milliseconds — the same unit as from/to above,
so a bar's time falls directly within [from, to). The one argument in a different unit is
intervalSeconds, which is seconds as its name says.
Live ticks (marketDataStreamer)
Stream<String> marketDataStreamer(String symbols);symbols is a JSON-encoded list of symbol IDs to subscribe to. The SDK calls this again with
the complete new list every time the subscribed set changes (symbol switch, option chain
opened, scalper panel added), and cancels its listener on the previous stream. Resubscribe your feed to
exactly the ids passed, and return a broadcast stream — a single-subscription stream crashes on
the second call with Bad state: Stream has already been listened to.
When subscribeMarketData/unsubscribeMarketData actually fire
Every time the SDK's subscribed symbol set changes — a Terminal tile switches symbol, a tile
is added or closed, or the whole chart is disposed — it calls unsubscribeMarketData() first,
then subscribeMarketData() again with the complete new symbol list (not a diff). So:
unsubscribeMarketData()doesn't mean "stop forever" — expect a freshsubscribeMarketDatacall right after it in the common case (symbol switch), and treat it as "stop forever" only when no follow-upsubscribeMarketDatacall arrives (chart torn down).subscribeMarketData'ssymbolsargument always replaces your previous subscription — don't try to diff it against what you were already streaming, just resubscribe to exactly what's passed.
Each emitted event is a JSON-encoded array of tick objects:
[
{
"symbolId": "RELIANCE", "ltp": 2411.5, "ltq": 100, "chng": 11.5, "chngPer": 0.48,
"ltt": 1700001234000, "OI": 125000, "oiChng": 2500, "oiChngPer": 2.04,
"vwap": 2408.2, "vol": 1500000, "volDelta": 5000, "lcl": 2200.0, "ucl": 2600.0,
"dayClose": 2400.0
}
]| Field | Type | Meaning |
|---|---|---|
symbolId | String | required; which symbol this tick belongs to, so the chart can route multi-symbol ticks |
ltp | double | required; last traded price |
ltt | int | required; last trade time, epoch milliseconds — same unit as loadData's bar time, used to bucket the tick into the right candle |
ltq | int | optional; last traded quantity — shown in the web Market Depth panel, read from ticks that carry a book (below) |
chng | double | optional; absolute change from previous close |
chngPer | double | optional; percentage change from previous close |
OI | int | optional; open interest |
oiChng | int | optional; absolute change in open interest |
oiChngPer | double | optional; percentage change in open interest |
vwap | double | optional; volume-weighted average price |
vol | int | optional; volume traded so far today |
volDelta | double | optional; volume traded since the previous tick |
lcl | double | optional; lower circuit limit |
ucl | double | optional; upper circuit limit |
dayClose | double | optional; previous day's close price |
For option symbols, also include the greeks flat on the same tick object (not nested under a greeks key):
{ "symbolId": "NIFTY24SEP25000CE", "ltp": 142.3, "ltt": 1700001234000, "itm": "true", "iv": 18.4, "delta": 0.52, "gamma": 0.004, "theta": -12.1, "rho": 3.2, "vega": 9.8, "highIV": 21.0, "lowIV": 15.2 }| Field | Type | Meaning |
|---|---|---|
itm | bool | optional; whether the option is in-the-money |
iv | double | optional; implied volatility |
delta | double | optional; option delta |
gamma | double | optional; option gamma |
theta | double | optional; option theta |
rho | double | optional; option rho |
vega | double | optional; option vega |
highIV | double | optional; day's high implied volatility |
lowIV | double | optional; day's low implied volatility |
Order book (web only)
The web order pad's Market Depth panel reads the order book from the same tick objects. Add these fields to a symbol's tick to feed it; native hosts can leave them out.
{
"symbolId": "NIFTY24SEP25000CE", "ltp": 205.55, "ltt": 1700001234000, "vol": 7029373,
"bids": [{ "price": 205.5, "qty": 1300, "orders": 6 }],
"asks": [{ "price": 205.6, "qty": 910, "orders": 4 }],
"totalBuyQty": 3895, "totalSellQty": 3395, "open": 189.0, "high": 212.4, "low": 181.2,
"prevClose": 190.1, "avgTradePrice": 187.87, "ltq": 75
}| Field | Type | Meaning |
|---|---|---|
bids | List | best 5 buy levels, highest price first; each {price, qty, orders} |
asks | List | best 5 sell levels, lowest price first; each {price, qty, orders} |
totalBuyQty | int | total quantity on the buy side |
totalSellQty | int | total quantity on the sell side |
open / high / low | double | day's open, high and low |
prevClose | double | previous day's close |
avgTradePrice | double | day's average traded price |
Each book is a full snapshot, not a delta. A tick without bids/asks keeps the last book shown, so you
can send book updates and price-only ticks separately. The panel's volume and last trade time come
from vol and ltt. If no book arrives within 10 seconds of opening the panel, it shows "No data
available".
Symbol search & navigation
Stream<String> searchSymbolsStreamer(String query);searchSymbolsStreamer is called once per debounced keystroke; query is the raw search text
(e.g. "RELI"). Emits the same JSON shape as optionSymbols above (array of symbol objects) —
a single result (or "[]") is enough:
[
{ "id": "RELIANCE", "name": "Reliance Industries", "lotSize": 1, "precision": 2, "tickSize": 0.05, "exchange": "NSE" }
]Results are shown in the chart's own symbol search UI (the search box in the chart header) — there's no separate integration point for rendering them on your side.
chartInterfaceForSymbol
Called any time the chart needs to pivot to a different symbol without a full host-driven
reinit — an option chain cell tap, a symbol search result, the CE/PE or Spot/Fut swap toggle, or
(web terminal) a tile's layout role changing. symbolJson is the target symbol, same shape as
symbolInfo.
ChartInterface? chartInterfaceForSymbol(String symbolJson) {
try {
final symbol = jsonDecode(symbolJson);
final symbolId = symbol is Map<String, dynamic> ? symbol['id']?.toString() : null;
if (symbolId == null || symbolId.isEmpty) return null;
// A new instance scoped to the target symbol, reusing this host's own
// config (stream rate, storage key prefix, etc).
return YourChartInterface(symbolId: symbolId, storageKey: '$storageKey:$symbolId');
} catch (_) {
return null;
}
}Return a ChartInterface configured for that symbol, or null to disable navigation. null
is silent: tapping a symbol search result or an option-chain row simply does nothing, and the
search panel stays open.
The SDK never disposes the instances you return here — only the one you passed to
NxtChartPage. Track them and close their streams yourself, for example from your original
instance's dispose().