Series Class (Series.ts)
The Series class is the bridge between JavaScript arrays and Pine Script semantics.
Storage Strategy: Forward vs. Reverse
Visual Representation
Pine Script Perspective (User View):
[Bar 0, Bar 1, Bar 2, Bar 3, ..., Bar N]
↑
Current bar (index 0)
PineTS Internal Storage (Forward Order):
[Bar 0, Bar 1, Bar 2, ..., Bar N-1, Bar N]
↑ ↑
Oldest bar Newest bar (current)
Series Access Layer:
Series.get(0) → Bar N (most recent)
Series.get(1) → Bar N-1 (previous)
Series.get(n) → Bar N-n (n bars ago)
Why Forward Storage with Series Wrapper?
- Pine Script Compatibility: The Series class provides
series[0]= current bar semantics - Efficient Appending: New bars are added with
.push()at the end - Natural Storage: Matches chronological order of market data
- Reverse Index Calculation:
Series.get(index)translates todata[length - 1 - index]
Internal Storage: Standard JavaScript array, forward chronological order (oldest at index 0, newest at end).
External Access: Pine Script reverse indexing (0 = current, 1 = previous).
The Series Wrapper
class Series {
constructor(public data: any[], public offset: number = 0) {}
// Get value at Pine Script index (0 = current, 1 = previous, etc.)
get(index: number): any {
const realIndex = this.data.length - 1 - (this.offset + index);
if (realIndex < 0 || realIndex >= this.data.length) {
return NaN;
}
return this.data[realIndex];
}
// Set value at Pine Script index
set(index: number, value: any): void {
const realIndex = this.data.length - 1 - (this.offset + index);
if (realIndex >= 0 && realIndex < this.data.length) {
this.data[realIndex] = value;
}
}
}
Key Features:
- Offset Support: Enables lookback operations like
close[1]by creating a new Series withoffset = 1 - Automatic NaN: Returns NaN for out-of-bounds access (Pine Script behavior)
- Forward Array: Wraps a standard forward-ordered array
Offset Handling
When you do close[1], PineTS doesn’t copy the array. It creates a lightweight Series wrapper with offset = 1.
close (offset = 0):
Series.get(0) → data[length - 1 - (0 + 0)] = data[length - 1] = last element
close[1] (offset = 1):
Series.get(0) → data[length - 1 - (1 + 0)] = data[length - 2] = second-to-last
close:offset = 0.get(0)-> last element.close[1]:offset = 1.get(0)-> second to last element.
This allows efficient passing of “historical views” to functions without data duplication.
Series Growth Pattern
At the end of each iteration, all series grow by pushing the current value:
Before iteration N+1: [val0, val1, ..., valN]
↑ current
index N-1
After processing N+1: [val0, val1, ..., valN, valN+1]
↑ ↑
[1] [0] current
index index N
N-1
Access via $.get():
$.get(arr, 0) → arr[N] (current)
$.get(arr, 1) → arr[N-1] (previous)
Series.from() Helper
Many TA functions use Series.from() to normalize inputs:
static from(source: any): Series {
if (source instanceof Series) return source;
if (Array.isArray(source)) return new Series(source);
return new Series([source]); // Wrap scalar in array
}
This allows functions to accept:
- Series objects (pass through)
- Arrays (wrap in Series)
- Scalar values (wrap in single-element array, then Series)