Exciting Update: Version 1.0.0 is now available, introducing high-performance technical indicators and custom drawing tools. Read more
Version: 1.0.0

Time scale

The time scale (or horizontal axis) maps timestamps to canvas coordinates, calculates index intervals, extrapolates future boundaries, and renders adaptive time tick gridlines.

Under the Hood: Coordinate Mapping

Backtestx Charts uses a slot-based layout model on the horizontal axis. Every candlestick or bar occupies an index slot of width candleWidth separated by candleGap:

Bar Index to X Pixel (barToX)

Translates a bar's integer index i into a horizontal canvas pixel coordinate. It adds half the candle's width to center the drawing shape on its vertical gridline:

slot = candleWidth + candleGap
x = offset + i * slot + candleWidth / 2

X Pixel to Bar Index (xToBar)

Translates a horizontal canvas offset pixel back to the nearest integer bar index. Used for mouse crosshair tracking and snap grids:

slot = candleWidth + candleGap
i = Math.round((x - offset - candleWidth / 2) / slot)

Future & Past Extrapolation

Unlike static plots, Backtestx Charts allows users to drag past the boundaries of the historical dataset. The time scale generates extrapolated ticks dynamically using the sample interval step:

  • Sample Step Discovery: Computes the smallest positive difference between historical bars to find the interval step (intervalMs) in milliseconds.
  • Past Index Extrapolation (i < 0): Ticks printed to the left of the initial historical candle are calculated as: time = bars[0].time + i * intervalMs.
  • Future Index Extrapolation (i >= len): Ticks printed inside the right-hand margin are calculated as: time = bars[len - 1].time + (i - (len - 1)) * intervalMs.

Adaptive Axis Labeling

To prevent grid labels from overlapping when zooming, the axis ticks adjust dynamically using window.TimeScale._getAdaptiveLabel(chart, date, prevDate):

Boundary Detection Engine

When a label crosses a temporal boundary (such as moving into a new day, month, or year), it is marked as a boundary label. Ticks that flag as boundaries are rendered in **bold** and a high-contrast theme color to establish clear visual context.

Timeframe Type Normal Label Format Boundary Label Format
Intraday (1m, 5m, 1h) HH:MM DD Mon (e.g. 12 Jun) / Year (bold)
Daily / Weekly / Monthly DD Mon (e.g. Jun) / Year (bold)

Horizontal Scroll Clamping

To prevent the user from dragging the chart completely off-screen, the engine applies structural clamping limits:

Past Boundary Limit

80% visible bars offset limit

Prevents dragging historical bars too far to the right. A minimum of 20% of the historical candles remain visible on screen.

Future Boundary Limit

90% visible bars offset limit

Limits how far to the left the user can drag the latest candles. A minimum buffer of 10% of visible slots (with at least 100 future empty slots) is maintained.

Pluggable Horizontal Scales

Backtestx Charts supports swap-in horizontal axis mappings via the ChartingAPI horizontal scale registry. This is essential for rendering non-time-series layouts:

Custom Scaling API

Register custom scales using the registry interface:

window.ChartingAPI.registerHorizontalScale('my-custom-scale', {
  barToX: (chart, i) => { ... },
  xToBar: (chart, x) => { ... },
  getVisibleRange: (chart) => { ... },
  drawTimeScale: (chart, ctx, chartH) => { ... },
  drawTimeBadge: (chart, ctx, chartH) => { ... }
});

Built-in Pluggable Scales

  • YieldScale (yield-curve): Spaces discrete maturity terms (1M, 3M, 2Y, 10Y, 30Y) evenly across the horizontal axis, mapping yield vertices to coordinates.
  • StrikeScale (strike-price): Maps option curves horizontally by numerical strike price points ($90.00, $100.00, etc.) rather than timestamps.