NeoCharts
Trading

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'
});
FieldTypeAccepted values
idStringrequired; unique symbol ID used for all data operations
nameStringrequired; display name shown on the chart
lotSizeintoptional; minimum tradeable unit, defaults to 1 if omitted
precisionintoptional; decimal places for price display, defaults to 2 if omitted
tickSizedoubleoptional; minimum price movement, defaults to 0 if omitted
exchangeStringoptional; exchange identifier (e.g. "NSE", "BSE") — see the note under marketTimings once your host serves more than one exchange
expiryStringoptional; expiry date for derivatives ("YYYY-MM-DD"), empty for equities
strikeStringoptional; strike price for options, empty otherwise
optTypeStringoptional; "CE", "PE", or empty for a non-option symbol
weeklyStringoptional; any non-empty value marks a weekly expiry, empty otherwise
undIDStringoptional; underlying symbol ID for derivatives, empty for equities
descriptionStringoptional; defaults to "" if omitted
trdUnitStringoptional; 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" }
]
FieldTypeAccepted values
optionSymbolsStringrequired; 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)
futureSymbolsString?optional; future contracts for the underlying symbol, null or "[]" when none exist — same per-entry undID requirement on web
indexSymbolsString?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'] },
  },
});
FieldTypeAccepted values
(map key)Stringexchange code (e.g. "NSE", "NFO"), matched case-insensitively against symbolInfo.exchange
timezoneStringrequired; IANA timezone name (e.g. "Asia/Kolkata")
sessionsList<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
holidaysList<String>required (may be []); dates in "YYYYMMDD" format, overrides the regular weekly schedule
specialMap<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,
});
ArgumentTypeMeaning
symbolIdStringwhich symbol's OHLCV to fetch
fromintwindow start, epoch milliseconds
tointwindow end, epoch milliseconds
intervalSecondsintbar size in seconds
requiredBarsintminimum 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 fresh subscribeMarketData call right after it in the common case (symbol switch), and treat it as "stop forever" only when no follow-up subscribeMarketData call arrives (chart torn down).
  • subscribeMarketData's symbols argument 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
  }
]
FieldTypeMeaning
symbolIdStringrequired; which symbol this tick belongs to, so the chart can route multi-symbol ticks
ltpdoublerequired; last traded price
lttintrequired; last trade time, epoch milliseconds — same unit as loadData's bar time, used to bucket the tick into the right candle
ltqintoptional; last traded quantity — shown in the web Market Depth panel, read from ticks that carry a book (below)
chngdoubleoptional; absolute change from previous close
chngPerdoubleoptional; percentage change from previous close
OIintoptional; open interest
oiChngintoptional; absolute change in open interest
oiChngPerdoubleoptional; percentage change in open interest
vwapdoubleoptional; volume-weighted average price
volintoptional; volume traded so far today
volDeltadoubleoptional; volume traded since the previous tick
lcldoubleoptional; lower circuit limit
ucldoubleoptional; upper circuit limit
dayClosedoubleoptional; 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 }
FieldTypeMeaning
itmbooloptional; whether the option is in-the-money
ivdoubleoptional; implied volatility
deltadoubleoptional; option delta
gammadoubleoptional; option gamma
thetadoubleoptional; option theta
rhodoubleoptional; option rho
vegadoubleoptional; option vega
highIVdoubleoptional; day's high implied volatility
lowIVdoubleoptional; 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
}
FieldTypeMeaning
bidsListbest 5 buy levels, highest price first; each {price, qty, orders}
asksListbest 5 sell levels, lowest price first; each {price, qty, orders}
totalBuyQtyinttotal quantity on the buy side
totalSellQtyinttotal quantity on the sell side
open / high / lowdoubleday's open, high and low
prevClosedoubleprevious day's close
avgTradePricedoubleday'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().

On this page