Skip to content

Time Series

Kind: module

Namespace: ts

Provides functions for creating and manipulating time series.

Overview

Time series are the core data structure for calculations. A series stores historical values with efficient lookback access, enabling stateful calculations across bars.

Series are continuous: every bar must have a value. If not explicitly set, it defaults to NaN. Internally, series use a ring buffer with constant memory usage regardless of how many bars are processed.

Index 0 is the current (most recent) value, 1 is the previous value, and so on:

Index:   [0]    [1]    [2]    [3]    [4]
          ↓      ↓      ↓      ↓      ↓
Value:   100    99     101    98     97

       current

The ts() function creates a series once on the first tick, then reuses the same instance on subsequent ticks. Each tick either shifts the series forward (new bar) or overwrites [0] (bar update):

Bar 0:  [10]         ← created
Bar 1:  [20, 10]     ← shifted, 10 is now [1]
Bar 1*: [25, 10]     ← update, [0] changed from 20 to 25
Bar 2:  [30, 25, 10] ← shifted

A series without an index is automatically converted to its current value in a numeric context (+, -, >, <, ==, !=, etc.). However, === and !== never trigger this conversion. Also, == and != between two series compare references, not values. See details below.

Built-in Series

Price and volume data are available as pre-defined read-only series:

SeriesDescription
timeCandle timestamp in millisecond
openOpening price
highHighest price
lowLowest price
closeClosing price
volumeTotal volume
bidVolumeBid volume
askVolumeAsk volume
countNumber of trades
vwapVolume-weighted average price
impVolatilityImplied volatility
openInterestOpen interest
hl2(high + low) / 2
hlc3(high + low + close) / 3
ohlc4(open + high + low + close) / 4

ts()

Kind: func

Creates a new time series for storing calculated values.

Syntax

ts() → series
ts(value) → series

Arguments

NameTypeDefaultDescription
value?doubleNaNValue for the current bar. Defaults to NaN if not provided

Returns

A mutable series object.

Caveats

  • Must be called inside onTick, not in the init section
  • Must be at the top level of onTick, not inside conditionals or loops. This ensures the call count and order remain constant across ticks

Pitfall

javascript
const y = ts(); // ❌ in init section

function onTick() {
    if (close > open) {
        const x = ts(close);  // ❌ inside conditional
    }
}

Example

javascript
function onTick() {
    // Create series at top level
    const diff = ts(close - open);
    const range = ts(high - low);
    const trend = ts();

    // Access history using [index]
    const prevDiff = diff[1];
    const avgRange = (range + range[1] + range[2]) / 3;

    // Update inside conditionals using .set()
    if (close > close[1]) {
        trend.set(1); // method syntax
    } else if (close < close[1]) {
        trend[0] = -1; // array syntax (equivalent)
    } else {
        trend.set(trend[1]);
    }

    spline(diff, {title: "diff"});
    spline(trend, {title: "trend"});
}

Series Object

The series object stores values and provides history access.


series[index]

Kind: operator

Reads historical values of a series.

Syntax

series[lookback] → double
series → double

Arguments

NameTypeDescription
lookbackintOffset from current bar. 0 = current, 1 = previous

Returns

The value at the specified offset, or NaN if the index exceeds available history.

Notes

  • series without index is equivalent to series[0]
  • Index 0 is the current bar, 1 is previous, etc.

Example

javascript
function onTick() {
    // Explicit index access
    const current = close[0];
    const prev = close[1];
    const old = close[10];

    // Implicit current value (numeric context)
    const sum = close + open; // same as close[0] + open[0]
    const change = close - close[1];

    // Comparisons (numeric context)
    const isUp = close > close[1];

    // ⚠️ Strict equality — use explicit [0]
    const equal = close[0] === high[0]; // ✅
    const noEqual = close[0] !== high[0]; // ✅
}

Equality operators with series

=== and !== never convert a series to its value, bevause they compare object references:

javascript
// ❌ always true — two different objects
if (high !== low) { ...
}
// ❌ always false — object vs number
if (close === 5) { ...
}

== and != convert a series to a number when compared with a primitive, but not when comparing two series:

javascript
// ✅ works — series is converted to number
if (close != 0) { ...
}
// ❌ compares references, not values
if (high != low) { ...
}

Use explicit [0] to avoid ambiguity:

javascript
// ✅ always correct
if (high[0] !== low[0]) { ...
}
if (close[0] === 5) { ...
}

series.set()

Kind: method

Sets the value for the current bar.

Syntax

javascript
series.set(value)
series[0] = value

Arguments

NameTypeDescription
valuedoubleValue to set

Returns

void

Notes

  • Only index 0 (current bar) can be written
  • Calling multiple times overwrites the same bar
  • Can be used inside conditionals (unlike ts())

Example

javascript
function onTick() {
    const signal = ts();

    if (close > close[1]) {
        signal.set(1); // method syntax
    } else {
        signal[0] = -1; // array syntax (equivalent)
    }

    spline(signal, {title: "signal"});
}