Skip to main content

Resolutions

Resolutions are the bar intervals users can select on the chart. TradeScript uses ChartInterval strings such as 1T, 100T, 1s, 6m, 2D, 3W, 6M, and 1Y.

Two lists control everything on this page: supportedIntervals (what users may select) and providedIntervals (what loadBars returns natively). The gap between them is closed — or not — by the resolution rebuild policy.

Exact, derived, or unsupported

Use this matrix to predict who serves a requested interval and how failure is surfaced. "Derived" requires wrapping the raw feed with createCachingDatafeed and an aggregate rebuild policy.

Requested intervalRebuild policyWho builds the barsWhat the user sees
In providedIntervalsanyYour feed — loadBars is called with that intervalExact vendor bars
In supportedIntervals onlyaggregateSDK — requests a smaller provided interval and aggregates (source-map rules below)Synthetic bars aligned to sessions when a calendar exists
In supportedIntervals onlyexact (default)Your feed — the SDK still requests the selected interval and returns the result unchangedWhatever the feed returns; no SDK aggregation, no empty-bar fill
Valid grammar, not in the resolved supported listNobodySelector entry (or typed entry) shown disabled
Text that fails the grammarNobodyTyped entry marked invalid

Two additional narrowing rules apply before the matrix:

  • The selector is the intersection of feed-level supportedIntervals and SymbolInfo.supportedIntervals for the active symbol.
  • Calendar intervals never derive from intraday data: daily, weekly, monthly, and yearly bars build only from day-or-larger sources (exact rules under Synthetic aggregation). A calendar interval whose allowed source is not provided stays feed-served or unsupported.

Defaults

If the datafeed does not declare intervals, the chart uses these defaults:

['1m', '2m', '3m', '5m', '10m', '15m', '30m', '1H', '2H', '4H', '1D', '1W', '1M']

The interval selector includes a typed custom-interval entry by default. Set features.customIntervals = false when you want a fixed-list selector. Typed entries still have to be present in the resolved supportedIntervals list; valid but unsupported entries are shown disabled.

The grammar is a positive integer plus unit:

`${number}${'T' | 's' | 'm' | 'H' | 'D' | 'W' | 'M' | 'Y'}`

Units are ticks, seconds, minutes, hours, days, weeks, months, and years. Examples: 1T, 25T, 30s, 6m, 2D, 3W, 6M, 12M, 1Y.

Global intervals

Declare the intervals the chart may show from onReady.

const datafeed: MarketDataFeed = {
onReady() {
return {
supportedIntervals: ['1T', '10T', '1s', '5s', '1m', '6m', '2D', '3W', '6M', '1Y'],
providedIntervals: ['1T', '10T', '1s', '5s', '1m', '2D', '1M'],
supportsRealTime: true,
};
},

async loadBars(symbol, interval, request) {
return fetchBars(symbol, interval, request);
},
};

supportedIntervals is the selector/request list. providedIntervals is the list your loadBars implementation can return directly.

Resolution rebuild policy

The SDK keeps vendor bars exact by default. In exact mode, createCachingDatafeed requests the selected interval from loadBars and returns those bars unchanged.

Use aggregate mode when the SDK may build requested intervals from smaller provided intervals:

const datafeed = createCachingDatafeed(rawFeed, {
policy: 'memory',
resolutionRebuildPolicy: 'aggregate',
});

The feed can declare the same policy itself:

onReady() {
return {
supportedIntervals: ['1m', '3m', '5m'],
providedIntervals: ['1m', '5m'],
resolutionRebuildPolicy: 'aggregate',
};
}

resolutionRebuildPolicy: 'exact' means vendor bars only. It disables SDK aggregation and empty-bar generation even if those options are present. resolutionRebuildPolicy: 'aggregate' allows SDK rebuilding, but providedIntervals still protects native intervals: if the requested interval is listed there, the SDK requests it directly.

Symbol intervals

Use SymbolInfo.supportedIntervals when a specific instrument has a smaller interval set than the feed globally supports.

async resolveSymbol(symbol) {
if (symbol === 'SPX') {
return {
ticker: 'SPX',
exchange: 'CBOE',
type: 'index',
supportedIntervals: ['1m', '5m', '15m', '1D'],
};
}

return {
ticker: String(symbol),
supportedIntervals: ['1T', '10T', '1s', '5s', '1m', '6m', '1D'],
};
}

The chart narrows the interval selector to the intersection of the feed-level supportedIntervals and the symbol-level supportedIntervals.

Typed interval entry

Users can type any interval that matches the ChartInterval grammar. The chart validates that text against the same SDK parser used by datafeeds:

  • If the typed interval is valid and supported, selecting it changes the chart interval.
  • If the typed interval is valid but not in the resolved feed/symbol list, it is shown disabled.
  • If the text does not match the grammar, it is shown invalid.

Disable the typed entry when the host product must expose only preconfigured choices:

sdk.chart.mount({
mount: '#chart',
symbol: 'AAPL',
interval: '1m',
datafeed,
features: {
customIntervals: false,
},
});

Synthetic aggregation

Synthetic aggregation is off by default unless aggregate rebuild policy is enabled. Enable it when you want explicit control over how the SDK builds larger intervals from a smaller provided interval, for example 5m from 1m bars.

import { createCachingDatafeed } from '@tradescript/pro/sdk';

const datafeed = createCachingDatafeed(rawFeed, {
policy: 'memory',
syntheticAggregation: {
sourceIntervals: {
'1H': '1m',
'10m': '5m',
'100T': '10T',
},
},
});

Pair it with explicit capabilities:

const rawFeed: MarketDataFeed = {
onReady() {
return {
supportedIntervals: ['10T', '100T', '1m', '5m', '10m', '1H', '1D'],
providedIntervals: ['10T', '1m', '5m', '1D'],
};
},
async loadBars(symbol, interval, request) {
return fetchBars(symbol, interval, request);
},
};

With the explicit map above, 1H is built from 1m bars, 10m is built from 5m bars, and 100T is built by grouping ten 10T source bars.

Passing syntheticAggregation: true uses the SDK default source map instead:

Requested intervalSource interval
5T1T
10T5T
25T5T
50T25T
100T50T
2m1m
3m1m
10m5m
30m15m
2H1H
4H1H

Tick aggregation is count-based. The default map is one-hop, so the raw feed must be able to return the source interval listed above. Use an explicit source map entry such as '100T': '10T' when your feed has a different tick base tier.

For other intraday time intervals, syntheticAggregation: true uses the SDK base interval tier when possible, for example 6m -> 5m.

Calendar intervals use stricter source rules:

Requested intervalAllowed SDK source
multi-day such as 2Dsmaller day interval, normally 1D
weekly such as 1W, 3W1D, or a smaller weekly interval when available
monthly such as 1M, 6M1D, or a smaller monthly interval when available
yearly such as 1Y1D, 1M, or a smaller yearly interval when available

The SDK does not build daily, weekly, monthly, or yearly bars from intraday/hourly/tick data. If the vendor lists a requested calendar interval in providedIntervals, the SDK uses that vendor interval directly.

Session-aware aggregation

Synthetic aggregation uses fixed UTC buckets unless the feed supplies a session calendar. Add resolveSessionCalendar when synthetic bars must follow exchange sessions, holidays, half-days, or corrected trading hours.

const rawFeed: MarketDataFeed = {
onReady() {
return {
supportedIntervals: ['5T', '10T', '1m', '1H', '1D'],
providedIntervals: ['5T', '1m', '1D'],
supportsSessionCalendar: true,
};
},

async resolveSessionCalendar(request) {
return {
symbol: request.symbol,
timezone: 'America/New_York',
windows: [
{
opensAt: Date.parse('2024-01-02T14:30:00Z'),
closesAt: Date.parse('2024-01-02T21:00:00Z'),
tradingDay: '2024-01-02',
state: 'regular',
},
],
};
},

async loadBars(symbol, interval, request) {
return fetchBars(symbol, interval, request);
},
};

When wrapped with createCachingDatafeed, the SDK requests the calendar for the source/target interval pair before building synthetic bars. Intraday and hourly buckets align from opensAt. Bars outside active windows are ignored. Daily source bars match windows[].tradingDay, so a 1D bar timestamped at 00:00:00Z for the trading day can build weekly/monthly bars. Week/month/year buckets use the first trading day present in that calendar bucket. Tick aggregation remains count-based, but resets at session boundaries.

syntheticAggregation.sessionCalendar accepts a calendar or a provider function. A calendar-only object still uses the SDK default source map; include sourceIntervals for explicit source rules. Set sessionCalendar to false to force fixed UTC buckets even if the feed exposes resolveSessionCalendar.

Empty bars

Empty bars fill missing intraday slots inside active session windows. They are useful for symbols where the chart should show every expected session interval even when the vendor omits zero-trade bars.

const datafeed = createCachingDatafeed(rawFeed, {
policy: 'memory',
resolutionRebuildPolicy: 'aggregate',
emptyBars: true,
});

emptyBars: true uses rawFeed.resolveSessionCalendar. You can also pass emptyBars: { sessionCalendar } with a calendar object or provider function. Generated bars use the previous close for open, high, low, and close, set volume to 0, and carry isEmpty: true.

The SDK only fills seconds/minutes/hours slots inside active session windows and only after the first real bar in that same session. Tick and day/week/month/year intervals are left unchanged. Empty bars are skipped when the resolved rebuild policy is exact.

Empty bars do not choose a smaller source interval by themselves. Use resolutionRebuildPolicy: 'aggregate' or syntheticAggregation when the SDK should also build larger resolutions from smaller bars.

Leave synthetic aggregation off when your vendor provides native bars or when synthetic candles are not acceptable for your product.

Next steps