Skip to main content

Time Scale

The time scale is the horizontal axis under the panes: date/time labels, viewport navigation, and the realtime right edge. Everything about the visible window is programmable: its range in bars or timestamps, how it scrolls and zooms, how labels are formatted, what happens across a data gap, and where a new bar leaves the viewport.

Common interactions

TaskAPI or gestureVisible result
Zoom around a pointWheel/pinch or zoomAtTimeBar spacing changes around the selected anchor
Scroll historyDrag/trackpad or scrollToTimeVisible range moves while data identity stays unchanged
Return to realtimeRealtime control or scrollToRealTimeLatest bar returns to the configured right margin
Apply presetsetTimeFrame(idOrDefinition)Range and interval adopt the preset definition
Reset after resizeResize with configured range policyRange stays locked or refits according to settings

Anatomy

TradeScript bottom time scale strip with hour labels around a day boundary where the date label thirty-one marks the new trading day

BTCUSDT · 15m · dark theme — bottom time scale captured from the live playground.

Three elements make up the axis strip:

  1. Time labels — hour labels (21:00, 03:00, 06:00, …) formatted through LocalizationSettings.formatters.time in the active chart timezone.
  2. Day boundary — the 31 label marks the date change, formatted through LocalizationSettings.formatters.date.
  3. The strip is shared by all stacked panes; navigation changes the visible range, it does not rewrite bar timestamps.
NeedTradeScript surface
ScrollscrollToTime, scrollToBar, scrollToRealTime
ZoomzoomAtTime, zoomAtBar, zoomAtCoordinate
Mouse-wheel zoom anchorinitialState.displaySettings.timeScaleMouseWheelZoomAnchor selects pointer or right-edge anchoring

Visible range

NeedTradeScript surface
Visible time rangesetVisibleRange, getVisibleRange
Visible bar rangegetVisibleBarRange
Minimum bar spacinginitialState.displaySettings.timeScaleMinBarSpacing
Maximum bar spacinginitialState.displaySettings.timeScaleMaxBarSpacing
Left edge lockinitialState.displaySettings.timeScaleFixLeftEdge
Resize visible-range lockinitialState.displaySettings.timeScaleLockVisibleTimeRangeOnResize
Right-margin unitinitialState.displaySettings.timeScaleRightOffsetUnit selects 'percent' or 'px'. The native Settings dialog exposes both units.
Percentage right margininitialState.displaySettings.timeScaleRightOffsetPercent. The native default is 25, so the latest bar lands at 75% from the left after an interval change.
Pixel right margininitialState.displaySettings.timeScaleRightOffsetPx
Preserve range while editinginitialState.displaySettings.timeScaleLockVisibleRangeOnRightOffsetChange

Realtime

NeedTradeScript surface
Return to realtimescrollToRealTime — the latest bar returns to the configured right margin
New-bar viewport policyinitialState.displaySettings.timeScaleShiftVisibleRangeOnNewBar
Right-side margininitialState.displaySettings.timeScaleRightOffsetPercent / timeScaleRightOffsetPx, or chart.setDisplaySettings(...) at runtime

Latest-bar position after interval changes

Interval changes replace the previous bars, indicator results, grid, and time-scale state as one committed chart frame. They do not reuse a historical right-edge anchor from the previous interval. The latest bar returns to the configured right margin on every transition, including lower-to-higher and higher-to-lower changes.

The built-in default is a 25% right margin, which places the latest bar at 75% of the drawable chart width from the left. The same setting is available under Settings → Time Scale → Right margin and is persisted as part of ChartDisplaySettings in saved chart layouts.

Set the SDK default during construction:

const mounted = sdk.chart.mount({
mount: '#chart',
initialState: {
displaySettings: {
timeScaleRightOffsetUnit: 'percent',
timeScaleRightOffsetPercent: 25,
},
},
})
const chart = (await mounted.ready()).chart()

Or change it at runtime:

chart.setDisplaySettings({
timeScaleRightOffsetUnit: 'percent',
timeScaleRightOffsetPercent: 15,
})

A 15% right margin places the latest bar at approximately 85% from the left. Pixel mode is also supported:

chart.setDisplaySettings({
timeScaleRightOffsetUnit: 'px',
timeScaleRightOffsetPx: 120,
})

Labels, gaps, and formatting

NeedTradeScript surface
Time formattingLocalizationSettings.formatters.date and formatters.time
Inactivity gapsinitialState.displaySettings.inactivityGapsVisible and chart.getTimeScale().inactivityGaps() / setInactivityGaps() / inactivityGapsChanged(). Visible gaps require generated empty bars (emptyBars) or another explicit time-domain extension from the feed/cache layer.

Presets and programmatic control

TradeScript time range preset menu opened from the toolbar range control listing one day through five year presets, year to date, and all history over the dark chart

Time range menu open · BTCUSDT · dark theme — presets such as 1D, 1W, 1M, 3M, 6M, YTD, 1Y, 5Y, and All come from ChartWidgetOptions.timeFrames.

NeedTradeScript surface
Time-frame presetsChartWidgetOptions.timeFrames
Apply a presetchart.setTimeFrame(idOrDefinition)

Sessions

NeedTradeScript surface
SessionssessionSchedule, resolveSessionCalendar, session filtering
Extended-session displaycreateSessionFilterDatafeed(...), features.sessions.modeSelector, and features.sessions.extendedHoursPriceLine

Extended sessions

Extended-hours display uses the datafeed/session contract. A feed declares session windows through SymbolInfo.sessionSchedule or MarketDataFeed.resolveSessionCalendar; the chart uses those windows for intraday shading, break lines, session-aware aggregation, and filtering.

Use createSessionFilterDatafeed(...) for the regular/extended toggle. The wrapper keeps the full series in the feed/cache layer and filters the presentation:

  • regular mode keeps only ['regular'] bars.
  • extended mode keeps ['pre-market', 'regular', 'post-market'].
  • The chart surface detects the wrapper, shows the Regular Hours / Extended Hours control when features.sessions.modeSelector !== false, and refetches the visible series in place on session changes.
  • While regular-only bars are visible, features.sessions.extendedHoursPriceLine !== false draws the live pre/post-market quote as a dashed price line when the feed provides quotes.

See Session Filtering, Session Schedules, and Market Status.

Indicator time extension

Custom indicators can declare canExtendTimeScale, and the public authoring adapter forwards that declaration through indicator.metadata.canExtendTimeScale. The global gate is features.customIndicators.extendTimeScale. When both the global gate and indicator opt-in allow extension, the adapter records right-side extension timestamps from three source-owned places:

  • host-supplied secondary-series bars that occur after the primary series tail
  • per-plot PlotStyleOptions.timestamps
  • explicit IndicatorRunResult.timeScaleExtensionTimestamps

The Store owns those timestamps as indicator-scoped time-domain slots. Primary OHLC bars are not mutated, indicator inputs stay aligned to loaded main bars, and timestamps at or before the primary tail are ignored.

Indexed plot and marker series remain aligned to loaded bars. Projection plots that need non-primary times should pass timestamps with their value series.

Secondary series time extension

When features.comparisons.extendTimeScale is enabled, loaded comparison bars can contribute right-side timestamps after the primary series tail to the chart's horizontal time domain. The Store owns those extension slots separately from the primary OHLC list, so the main candle series, indicator inputs, high/low ranges, and datafeed cache state are not mutated by placeholder bars.

This covers main-pane comparison rendering, legend values, and comparison panes. The generated comparison-pane indicator uses the same runtime gate and publishes timestamped plot rows for comparison bars after the primary tail, so pane auto-scale and line/candle drawing can render against the extended time domain without synthetic main-series candles.

Use the native flag directly:

sdk.chart.mount({
mount: '#chart',
features: {
comparisons: {
extendTimeScale: true,
},
},
})