5.2 KiB
TSeries: Time Series Data Container
| Property | Value |
|---|---|
| Category | Core |
| Inputs | Source (close) |
| Parameters | None |
| Outputs | Single series (TSeries) |
| Output range | Varies (see docs) |
| Warmup | 1 bar |
TSeriesis a high-performance, memory-efficient container for time-series data.- No configurable parameters; computation is stateless per bar.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
What It Does
TSeries is a high-performance, memory-efficient container for time-series data. Unlike standard collections (like List<TValue>), it uses a Structure of Arrays (SoA) layout internally. This means it stores timestamps and values in separate contiguous arrays, optimizing memory access patterns for numerical processing and SIMD vectorization.
Design Philosophy
Standard object-oriented collections (Array of Structures - AoS) are cache-inefficient for numerical algorithms. When calculating a moving average, the CPU only needs the values, but an AoS layout forces it to load interleaved timestamps into the cache, wasting bandwidth.
TSeries solves this by decoupling time and value storage:
- Cache Locality: Iterating over values loads only values.
- SIMD Readiness: The internal value array can be exposed directly as a
Span<double>for AVX/SSE processing. - Zero-Copy Views: Data is accessed without defensive copying, ensuring maximum throughput.
How It Works
TSeries maintains two parallel internal lists:
List<long> _t: Stores timestamps.List<double> _v: Stores values.
It implements IReadOnlyList<TValue>, allowing it to be treated as a standard collection of TValue structs when needed, but its true power lies in its column-oriented properties (Values, Times).
Structure
Definition
public class TSeries : IReadOnlyList<TValue>, ITValuePublisher
Core Properties
| Property | Type | Description |
|---|---|---|
Values |
ReadOnlySpan<double> |
Direct access to the value array (SIMD-ready). |
Times |
ReadOnlySpan<long> |
Direct access to the timestamp array. |
Last |
TValue |
The most recent time-value pair. |
Count |
int |
Number of elements in the series. |
Name |
string |
Optional identifier for the series. |
Events
| Event | Type | Description |
|---|---|---|
Pub |
Action<TValue> |
Fired whenever a new value is added or updated. |
Usage
Creating and Populating
var series = new TSeries();
// Add a new bar (isNew = true by default)
series.Add(DateTime.UtcNow, 100.0);
// Add multiple values
series.Add(new List<double> { 1.0, 2.0, 3.0 });
Streaming Updates (Real-time)
TSeries supports "bar updates" where the last value changes until the bar closes.
// New minute starts
series.Add(time, 100.0, isNew: true);
// Price updates within the same minute
series.Add(time, 101.0, isNew: false); // Overwrites last value
series.Add(time, 102.0, isNew: false); // Overwrites last value
SIMD Processing
// Calculate average using SIMD (via Span)
double sum = 0;
foreach (var v in series.Values) { sum += v; } // Compiler vectorizes this
Reactive Subscription
series.Pub += (item) => Console.WriteLine($"New value: {item}");
Performance Profile
Operation Count (Streaming Mode)
TSeries stores timestamps and values as parallel List + List (SoA). Pub/Sub event-driven streaming.
| Operation | Count | Cost (cycles) | Subtotal |
|---|---|---|---|
| Add TValue (2 List.Add calls) | 2 | 3 cy | ~6 cy |
| isNew check + rollback | 1 | 2 cy | ~2 cy |
| Pub event fire | 1 | 5 cy | ~5 cy |
| AsSpan (CollectionsMarshal) | 1 | 2 cy | ~2 cy |
| Total per bar | O(1) | — | ~15 cy |
The Pub/Sub dispatch dominates practical throughput when multiple subscribers are chained. Solo update without subscribers: ~8 cy.
- Memory Layout: SoA (Structure of Arrays).
- Access Speed: O(1) for random access.
- Iteration: Cache-friendly linear scan.
- SIMD: Fully supported via
Valuesspan.
Integration
TSeries is the standard output format for all indicators in QuanTAlib.
- Input: Can be fed into indicators via
Update(TSeries). - Output: Indicators return
TSeriesfrom theirCalculatemethods. - Visualization: Easily mappable to charting libraries due to separate Time/Value arrays.
Architecture Notes
- CollectionsMarshal: Uses
CollectionsMarshal.AsSpanto expose internal list storage as spans without copying. This is unsafe if the list is modified during span access, but provides maximum performance for single-threaded algorithms. - Virtual Methods:
Addis virtual to allow derived classes (likeTBarSeriescomponents) to intercept updates if necessary.