mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-16 01:28:05 +00:00
updates from mac
This commit is contained in:
@@ -1,82 +1,82 @@
|
||||
#!meta
|
||||
|
||||
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"}]}}
|
||||
|
||||
#!csharp
|
||||
|
||||
// Reference the library
|
||||
#r "..\..\bin\QuanTAlib.dll"
|
||||
|
||||
using QuanTAlib;
|
||||
|
||||
// 1. Creating a TBarSeries
|
||||
// TBarSeries is a collection of bars stored in Structure of Arrays (SoA) format
|
||||
// This layout is optimized for performance and SIMD operations
|
||||
|
||||
var bars = new TBarSeries();
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
|
||||
// Add a new bar
|
||||
var bar1 = new TBar(now, 100, 105, 95, 102, 1000);
|
||||
bars.Add(bar1, isNew: true);
|
||||
|
||||
Console.WriteLine($"Added Bar 1: Count={bars.Count}");
|
||||
Console.WriteLine($"Last Close: {bars.Last.Close}");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 2. Streaming Updates
|
||||
// TBarSeries supports updating the last bar in place
|
||||
// This is crucial for real-time feeds where the current bar changes until it closes
|
||||
|
||||
// Update the bar (e.g. price changed within the same minute)
|
||||
var bar1Update = new TBar(now, 100, 106, 95, 104, 1500);
|
||||
bars.Add(bar1Update, isNew: false);
|
||||
|
||||
Console.WriteLine($"Updated Bar 1: Count={bars.Count} (Count should not increase)");
|
||||
Console.WriteLine($"Last Close: {bars.Last.Close}");
|
||||
Console.WriteLine($"Last High: {bars.Last.High}");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 3. Zero-Copy Views
|
||||
// You can access individual components (Open, High, Low, Close, Volume) as TSeries
|
||||
// These views share the underlying memory, so no copying is involved
|
||||
|
||||
Console.WriteLine($"Bars Count: {bars.Count}");
|
||||
Console.WriteLine($"Close Series Count: {bars.Close.Count}");
|
||||
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value}");
|
||||
|
||||
// Verify view updates automatically
|
||||
Console.WriteLine("\nAdding new bar...");
|
||||
bars.Add(now + TimeSpan.TicksPerMinute, 104, 108, 103, 107, 2000, isNew: true);
|
||||
|
||||
Console.WriteLine($"Bars Count: {bars.Count}");
|
||||
Console.WriteLine($"Close Series Count: {bars.Close.Count} (Should match Bars Count)");
|
||||
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value} (Should be 107)");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 4. Aliases and Direct Access
|
||||
// TBarSeries provides short aliases (O, H, L, C, V) and direct access properties
|
||||
|
||||
Console.WriteLine($"Alias Access (C.Last): {bars.C.Last.Value}");
|
||||
Console.WriteLine($"Direct Last Access (LastClose): {bars.LastClose}");
|
||||
Console.WriteLine($"Direct Last Time (LastTime): {new DateTime(bars.LastTime)}");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 5. Iteration
|
||||
// You can iterate over the bars or individual series
|
||||
|
||||
Console.WriteLine("\nIterating over bars:");
|
||||
foreach (var bar in bars)
|
||||
{
|
||||
Console.WriteLine($" {bar}");
|
||||
}
|
||||
|
||||
Console.WriteLine("\nIterating over Close prices:");
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
Console.WriteLine($" Bar {i}: Close={bars.Close[i].Value}");
|
||||
}
|
||||
#!meta
|
||||
|
||||
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"}]}}
|
||||
|
||||
#!csharp
|
||||
|
||||
// Reference the library
|
||||
#r "..\..\bin\QuanTAlib.dll"
|
||||
|
||||
using QuanTAlib;
|
||||
|
||||
// 1. Creating a TBarSeries
|
||||
// TBarSeries is a collection of bars stored in Structure of Arrays (SoA) format
|
||||
// This layout is optimized for performance and SIMD operations
|
||||
|
||||
var bars = new TBarSeries();
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
|
||||
// Add a new bar
|
||||
var bar1 = new TBar(now, 100, 105, 95, 102, 1000);
|
||||
bars.Add(bar1, isNew: true);
|
||||
|
||||
Console.WriteLine($"Added Bar 1: Count={bars.Count}");
|
||||
Console.WriteLine($"Last Close: {bars.Last.Close}");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 2. Streaming Updates
|
||||
// TBarSeries supports updating the last bar in place
|
||||
// This is crucial for real-time feeds where the current bar changes until it closes
|
||||
|
||||
// Update the bar (e.g. price changed within the same minute)
|
||||
var bar1Update = new TBar(now, 100, 106, 95, 104, 1500);
|
||||
bars.Add(bar1Update, isNew: false);
|
||||
|
||||
Console.WriteLine($"Updated Bar 1: Count={bars.Count} (Count should not increase)");
|
||||
Console.WriteLine($"Last Close: {bars.Last.Close}");
|
||||
Console.WriteLine($"Last High: {bars.Last.High}");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 3. Zero-Copy Views
|
||||
// You can access individual components (Open, High, Low, Close, Volume) as TSeries
|
||||
// These views share the underlying memory, so no copying is involved
|
||||
|
||||
Console.WriteLine($"Bars Count: {bars.Count}");
|
||||
Console.WriteLine($"Close Series Count: {bars.Close.Count}");
|
||||
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value}");
|
||||
|
||||
// Verify view updates automatically
|
||||
Console.WriteLine("\nAdding new bar...");
|
||||
bars.Add(now + TimeSpan.TicksPerMinute, 104, 108, 103, 107, 2000, isNew: true);
|
||||
|
||||
Console.WriteLine($"Bars Count: {bars.Count}");
|
||||
Console.WriteLine($"Close Series Count: {bars.Close.Count} (Should match Bars Count)");
|
||||
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value} (Should be 107)");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 4. Aliases and Direct Access
|
||||
// TBarSeries provides short aliases (O, H, L, C, V) and direct access properties
|
||||
|
||||
Console.WriteLine($"Alias Access (C.Last): {bars.C.Last.Value}");
|
||||
Console.WriteLine($"Direct Last Access (LastClose): {bars.LastClose}");
|
||||
Console.WriteLine($"Direct Last Time (LastTime): {new DateTime(bars.LastTime)}");
|
||||
|
||||
#!csharp
|
||||
|
||||
// 5. Iteration
|
||||
// You can iterate over the bars or individual series
|
||||
|
||||
Console.WriteLine("\nIterating over bars:");
|
||||
foreach (var bar in bars)
|
||||
{
|
||||
Console.WriteLine($" {bar}");
|
||||
}
|
||||
|
||||
Console.WriteLine("\nIterating over Close prices:");
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
Console.WriteLine($" Bar {i}: Close={bars.Close[i].Value}");
|
||||
}
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
using QuanTAlib;
|
||||
|
||||
namespace QuanTAlib.Tests
|
||||
{
|
||||
public class TBarSeriesTests
|
||||
{
|
||||
[Fact]
|
||||
public void Add_NewBar_IncreasesCount()
|
||||
{
|
||||
var series = new TBarSeries();
|
||||
var bar = new TBar(DateTime.UtcNow.Ticks, 100, 110, 90, 105, 1000);
|
||||
|
||||
series.Add(bar, isNew: true);
|
||||
|
||||
Assert.Single(series);
|
||||
Assert.Equal(105.0, series.Last.Close);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_UpdateBar_DoesNotIncreaseCount()
|
||||
{
|
||||
var series = new TBarSeries();
|
||||
long time = DateTime.UtcNow.Ticks;
|
||||
var bar1 = new TBar(time, 100, 110, 90, 105, 1000);
|
||||
var bar2 = new TBar(time, 100, 112, 90, 108, 1200);
|
||||
|
||||
series.Add(bar1, isNew: true);
|
||||
series.Add(bar2, isNew: false);
|
||||
|
||||
Assert.Single(series);
|
||||
Assert.Equal(108.0, series.Last.Close);
|
||||
Assert.Equal(112.0, series.Last.High);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubSeries_AreUpdated()
|
||||
{
|
||||
var series = new TBarSeries();
|
||||
var bar = new TBar(DateTime.UtcNow.Ticks, 100, 110, 90, 105, 1000);
|
||||
|
||||
series.Add(bar, isNew: true);
|
||||
|
||||
Assert.Single(series.Open);
|
||||
Assert.Single(series.High);
|
||||
Assert.Single(series.Low);
|
||||
Assert.Single(series.Close);
|
||||
Assert.Single(series.Volume);
|
||||
|
||||
Assert.Equal(100.0, series.Open.Last.Value);
|
||||
Assert.Equal(110.0, series.High.Last.Value);
|
||||
Assert.Equal(90.0, series.Low.Last.Value);
|
||||
Assert.Equal(105.0, series.Close.Last.Value);
|
||||
Assert.Equal(1000.0, series.Volume.Last.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using Xunit;
|
||||
using QuanTAlib;
|
||||
|
||||
namespace QuanTAlib.Tests
|
||||
{
|
||||
public class TBarSeriesTests
|
||||
{
|
||||
[Fact]
|
||||
public void Add_NewBar_IncreasesCount()
|
||||
{
|
||||
var series = new TBarSeries();
|
||||
var bar = new TBar(DateTime.UtcNow.Ticks, 100, 110, 90, 105, 1000);
|
||||
|
||||
series.Add(bar, isNew: true);
|
||||
|
||||
Assert.Single(series);
|
||||
Assert.Equal(105.0, series.Last.Close);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_UpdateBar_DoesNotIncreaseCount()
|
||||
{
|
||||
var series = new TBarSeries();
|
||||
long time = DateTime.UtcNow.Ticks;
|
||||
var bar1 = new TBar(time, 100, 110, 90, 105, 1000);
|
||||
var bar2 = new TBar(time, 100, 112, 90, 108, 1200);
|
||||
|
||||
series.Add(bar1, isNew: true);
|
||||
series.Add(bar2, isNew: false);
|
||||
|
||||
Assert.Single(series);
|
||||
Assert.Equal(108.0, series.Last.Close);
|
||||
Assert.Equal(112.0, series.Last.High);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubSeries_AreUpdated()
|
||||
{
|
||||
var series = new TBarSeries();
|
||||
var bar = new TBar(DateTime.UtcNow.Ticks, 100, 110, 90, 105, 1000);
|
||||
|
||||
series.Add(bar, isNew: true);
|
||||
|
||||
Assert.Single(series.Open);
|
||||
Assert.Single(series.High);
|
||||
Assert.Single(series.Low);
|
||||
Assert.Single(series.Close);
|
||||
Assert.Single(series.Volume);
|
||||
|
||||
Assert.Equal(100.0, series.Open.Last.Value);
|
||||
Assert.Equal(110.0, series.High.Last.Value);
|
||||
Assert.Equal(90.0, series.Low.Last.Value);
|
||||
Assert.Equal(105.0, series.Close.Last.Value);
|
||||
Assert.Equal(1000.0, series.Volume.Last.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
# TBarSeries Class
|
||||
|
||||
`TBarSeries` is a high-performance collection of OHLCV bars implemented using a Structure of Arrays (SoA) layout. This design optimizes memory access patterns and enables efficient SIMD operations while providing convenient object-oriented views.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Structure of Arrays (SoA)**: Stores Time, Open, High, Low, Close, and Volume in separate contiguous arrays rather than an array of structs. This improves cache locality for operations that only need specific components (e.g., calculating SMA on Close prices).
|
||||
- **Zero-Copy Views**: Exposes `TSeries` properties (`Open`, `High`, `Low`, `Close`, `Volume`) that view the underlying data without copying.
|
||||
- **Streaming Support**: Efficiently handles real-time data updates with `Add(bar, isNew: false)`.
|
||||
- **Memory Efficient**: Minimizes object overhead by using shared internal lists.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```csharp
|
||||
public class TBarSeries : IReadOnlyList<TBar>
|
||||
{
|
||||
// Views
|
||||
public TSeries Open { get; }
|
||||
public TSeries High { get; }
|
||||
public TSeries Low { get; }
|
||||
public TSeries Close { get; }
|
||||
public TSeries Volume { get; }
|
||||
|
||||
// Aliases
|
||||
public TSeries O => Open;
|
||||
public TSeries H => High;
|
||||
public TSeries L => Low;
|
||||
public TSeries C => Close;
|
||||
public TSeries V => Volume;
|
||||
}
|
||||
```
|
||||
|
||||
## Core Methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `Add(TBar bar, bool isNew = true)` | Adds a new bar or updates the last one. |
|
||||
| `Add(DateTime time, double o, double h, double l, double c, double v, bool isNew)` | Adds raw values directly. |
|
||||
| `Count` | Returns the number of bars. |
|
||||
| `Last` | Returns the most recent `TBar`. |
|
||||
|
||||
## Usage
|
||||
|
||||
### Creating and Populating
|
||||
```csharp
|
||||
var bars = new TBarSeries();
|
||||
|
||||
// Add a new bar
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
bars.Add(new TBar(now, 100, 105, 95, 102, 1000), isNew: true);
|
||||
|
||||
// Update the last bar (e.g., real-time feed update)
|
||||
bars.Add(new TBar(now, 100, 106, 95, 104, 1500), isNew: false);
|
||||
```
|
||||
|
||||
### Accessing Data
|
||||
```csharp
|
||||
// Access entire bar
|
||||
TBar lastBar = bars.Last;
|
||||
|
||||
// Access specific component series (Zero-Copy)
|
||||
TSeries closes = bars.Close;
|
||||
double lastClose = closes.Last.Value;
|
||||
|
||||
// Access via indexer
|
||||
TBar firstBar = bars[0];
|
||||
```
|
||||
|
||||
### Performance Note
|
||||
Because `TBarSeries` uses SoA layout, iterating over a single component (like `Close` prices) is extremely cache-efficient. The CPU prefetcher can load contiguous doubles without loading the interleaved Open, High, Low, or Volume data.
|
||||
# TBarSeries Class
|
||||
|
||||
`TBarSeries` is a high-performance collection of OHLCV bars implemented using a Structure of Arrays (SoA) layout. This design optimizes memory access patterns and enables efficient SIMD operations while providing convenient object-oriented views.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Structure of Arrays (SoA)**: Stores Time, Open, High, Low, Close, and Volume in separate contiguous arrays rather than an array of structs. This improves cache locality for operations that only need specific components (e.g., calculating SMA on Close prices).
|
||||
- **Zero-Copy Views**: Exposes `TSeries` properties (`Open`, `High`, `Low`, `Close`, `Volume`) that view the underlying data without copying.
|
||||
- **Streaming Support**: Efficiently handles real-time data updates with `Add(bar, isNew: false)`.
|
||||
- **Memory Efficient**: Minimizes object overhead by using shared internal lists.
|
||||
|
||||
## Class Definition
|
||||
|
||||
```csharp
|
||||
public class TBarSeries : IReadOnlyList<TBar>
|
||||
{
|
||||
// Views
|
||||
public TSeries Open { get; }
|
||||
public TSeries High { get; }
|
||||
public TSeries Low { get; }
|
||||
public TSeries Close { get; }
|
||||
public TSeries Volume { get; }
|
||||
|
||||
// Aliases
|
||||
public TSeries O => Open;
|
||||
public TSeries H => High;
|
||||
public TSeries L => Low;
|
||||
public TSeries C => Close;
|
||||
public TSeries V => Volume;
|
||||
}
|
||||
```
|
||||
|
||||
## Core Methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `Add(TBar bar, bool isNew = true)` | Adds a new bar or updates the last one. |
|
||||
| `Add(DateTime time, double o, double h, double l, double c, double v, bool isNew)` | Adds raw values directly. |
|
||||
| `Count` | Returns the number of bars. |
|
||||
| `Last` | Returns the most recent `TBar`. |
|
||||
|
||||
## Usage
|
||||
|
||||
### Creating and Populating
|
||||
```csharp
|
||||
var bars = new TBarSeries();
|
||||
|
||||
// Add a new bar
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
bars.Add(new TBar(now, 100, 105, 95, 102, 1000), isNew: true);
|
||||
|
||||
// Update the last bar (e.g., real-time feed update)
|
||||
bars.Add(new TBar(now, 100, 106, 95, 104, 1500), isNew: false);
|
||||
```
|
||||
|
||||
### Accessing Data
|
||||
```csharp
|
||||
// Access entire bar
|
||||
TBar lastBar = bars.Last;
|
||||
|
||||
// Access specific component series (Zero-Copy)
|
||||
TSeries closes = bars.Close;
|
||||
double lastClose = closes.Last.Value;
|
||||
|
||||
// Access via indexer
|
||||
TBar firstBar = bars[0];
|
||||
```
|
||||
|
||||
### Performance Note
|
||||
Because `TBarSeries` uses SoA layout, iterating over a single component (like `Close` prices) is extremely cache-efficient. The CPU prefetcher can load contiguous doubles without loading the interleaved Open, High, Low, or Volume data.
|
||||
|
||||
+147
-147
@@ -1,147 +1,147 @@
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// A high-performance OHLCV time series implementation using Structure of Arrays (SoA) layout.
|
||||
/// Stores Time, Open, High, Low, Close, Volume in separate contiguous arrays for SIMD efficiency.
|
||||
/// Exposes TSeries views for each component that share the underlying Time array.
|
||||
/// </summary>
|
||||
public class TBarSeries : IReadOnlyList<TBar>
|
||||
{
|
||||
// Internal storage: SoA layout
|
||||
protected readonly List<long> _t = new();
|
||||
protected readonly List<double> _o = new();
|
||||
protected readonly List<double> _h = new();
|
||||
protected readonly List<double> _l = new();
|
||||
protected readonly List<double> _c = new();
|
||||
protected readonly List<double> _v = new();
|
||||
|
||||
public string Name { get; set; } = "Bar";
|
||||
public event Action<TBar>? Pub;
|
||||
|
||||
// Public properties are Views into the main data
|
||||
public TSeries Open { get; }
|
||||
public TSeries High { get; }
|
||||
public TSeries Low { get; }
|
||||
public TSeries Close { get; }
|
||||
public TSeries Volume { get; }
|
||||
|
||||
// Aliases for convenience
|
||||
public TSeries O => Open;
|
||||
public TSeries H => High;
|
||||
public TSeries L => Low;
|
||||
public TSeries C => Close;
|
||||
public TSeries V => Volume;
|
||||
|
||||
public TBarSeries()
|
||||
{
|
||||
// Initialize views sharing the same Time list but different Value lists
|
||||
Open = new TSeries(_t, _o) { Name = "Open" };
|
||||
High = new TSeries(_t, _h) { Name = "High" };
|
||||
Low = new TSeries(_t, _l) { Name = "Low" };
|
||||
Close = new TSeries(_t, _c) { Name = "Close" };
|
||||
Volume = new TSeries(_t, _v) { Name = "Volume" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with capacity hint to avoid List growth overhead.
|
||||
/// </summary>
|
||||
public TBarSeries(int capacity)
|
||||
{
|
||||
_t = new List<long>(capacity);
|
||||
_o = new List<double>(capacity);
|
||||
_h = new List<double>(capacity);
|
||||
_l = new List<double>(capacity);
|
||||
_c = new List<double>(capacity);
|
||||
_v = new List<double>(capacity);
|
||||
|
||||
// Initialize views sharing the same Time list but different Value lists
|
||||
Open = new TSeries(_t, _o) { Name = "Open" };
|
||||
High = new TSeries(_t, _h) { Name = "High" };
|
||||
Low = new TSeries(_t, _l) { Name = "Low" };
|
||||
Close = new TSeries(_t, _c) { Name = "Close" };
|
||||
Volume = new TSeries(_t, _v) { Name = "Volume" };
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _c.Count;
|
||||
}
|
||||
|
||||
public TBar this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(_t[index], _o[index], _h[index], _l[index], _c[index], _v[index]);
|
||||
}
|
||||
|
||||
public TBar Last
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _c.Count > 0 ? new(_t[^1], _o[^1], _h[^1], _l[^1], _c[^1], _v[^1]) : default;
|
||||
}
|
||||
|
||||
public long LastTime { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _t.Count > 0 ? _t[^1] : 0; }
|
||||
public double LastOpen { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _o.Count > 0 ? _o[^1] : double.NaN; }
|
||||
public double LastHigh { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _h.Count > 0 ? _h[^1] : double.NaN; }
|
||||
public double LastLow { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _l.Count > 0 ? _l[^1] : double.NaN; }
|
||||
public double LastClose { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _c.Count > 0 ? _c[^1] : double.NaN; }
|
||||
public double LastVolume { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _v.Count > 0 ? _v[^1] : double.NaN; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(TBar bar, bool isNew = true)
|
||||
{
|
||||
if (isNew || _c.Count == 0)
|
||||
{
|
||||
_t.Add(bar.Time);
|
||||
_o.Add(bar.Open);
|
||||
_h.Add(bar.High);
|
||||
_l.Add(bar.Low);
|
||||
_c.Add(bar.Close);
|
||||
_v.Add(bar.Volume);
|
||||
}
|
||||
else
|
||||
{
|
||||
int lastIdx = _c.Count - 1;
|
||||
_t[lastIdx] = bar.Time;
|
||||
_o[lastIdx] = bar.Open;
|
||||
_h[lastIdx] = bar.High;
|
||||
_l[lastIdx] = bar.Low;
|
||||
_c[lastIdx] = bar.Close;
|
||||
_v[lastIdx] = bar.Volume;
|
||||
}
|
||||
|
||||
Pub?.Invoke(bar);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(long time, double open, double high, double low, double close, double volume, bool isNew = true) =>
|
||||
Add(new TBar(time, open, high, low, close, volume), isNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(DateTime time, double open, double high, double low, double close, double volume, bool isNew = true) =>
|
||||
Add(new TBar(time.Ticks, open, high, low, close, volume), isNew);
|
||||
|
||||
public void Add(IEnumerable<long> t, IEnumerable<double> o, IEnumerable<double> h, IEnumerable<double> l, IEnumerable<double> c, IEnumerable<double> v)
|
||||
{
|
||||
_t.AddRange(t);
|
||||
_o.AddRange(o);
|
||||
_h.AddRange(h);
|
||||
_l.AddRange(l);
|
||||
_c.AddRange(c);
|
||||
_v.AddRange(v);
|
||||
}
|
||||
|
||||
public IEnumerator<TBar> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _c.Count; i++)
|
||||
{
|
||||
yield return new TBar(_t[i], _o[i], _h[i], _l[i], _c[i], _v[i]);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// A high-performance OHLCV time series implementation using Structure of Arrays (SoA) layout.
|
||||
/// Stores Time, Open, High, Low, Close, Volume in separate contiguous arrays for SIMD efficiency.
|
||||
/// Exposes TSeries views for each component that share the underlying Time array.
|
||||
/// </summary>
|
||||
public class TBarSeries : IReadOnlyList<TBar>
|
||||
{
|
||||
// Internal storage: SoA layout
|
||||
protected readonly List<long> _t = new();
|
||||
protected readonly List<double> _o = new();
|
||||
protected readonly List<double> _h = new();
|
||||
protected readonly List<double> _l = new();
|
||||
protected readonly List<double> _c = new();
|
||||
protected readonly List<double> _v = new();
|
||||
|
||||
public string Name { get; set; } = "Bar";
|
||||
public event Action<TBar>? Pub;
|
||||
|
||||
// Public properties are Views into the main data
|
||||
public TSeries Open { get; }
|
||||
public TSeries High { get; }
|
||||
public TSeries Low { get; }
|
||||
public TSeries Close { get; }
|
||||
public TSeries Volume { get; }
|
||||
|
||||
// Aliases for convenience
|
||||
public TSeries O => Open;
|
||||
public TSeries H => High;
|
||||
public TSeries L => Low;
|
||||
public TSeries C => Close;
|
||||
public TSeries V => Volume;
|
||||
|
||||
public TBarSeries()
|
||||
{
|
||||
// Initialize views sharing the same Time list but different Value lists
|
||||
Open = new TSeries(_t, _o) { Name = "Open" };
|
||||
High = new TSeries(_t, _h) { Name = "High" };
|
||||
Low = new TSeries(_t, _l) { Name = "Low" };
|
||||
Close = new TSeries(_t, _c) { Name = "Close" };
|
||||
Volume = new TSeries(_t, _v) { Name = "Volume" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with capacity hint to avoid List growth overhead.
|
||||
/// </summary>
|
||||
public TBarSeries(int capacity)
|
||||
{
|
||||
_t = new List<long>(capacity);
|
||||
_o = new List<double>(capacity);
|
||||
_h = new List<double>(capacity);
|
||||
_l = new List<double>(capacity);
|
||||
_c = new List<double>(capacity);
|
||||
_v = new List<double>(capacity);
|
||||
|
||||
// Initialize views sharing the same Time list but different Value lists
|
||||
Open = new TSeries(_t, _o) { Name = "Open" };
|
||||
High = new TSeries(_t, _h) { Name = "High" };
|
||||
Low = new TSeries(_t, _l) { Name = "Low" };
|
||||
Close = new TSeries(_t, _c) { Name = "Close" };
|
||||
Volume = new TSeries(_t, _v) { Name = "Volume" };
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _c.Count;
|
||||
}
|
||||
|
||||
public TBar this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(_t[index], _o[index], _h[index], _l[index], _c[index], _v[index]);
|
||||
}
|
||||
|
||||
public TBar Last
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _c.Count > 0 ? new(_t[^1], _o[^1], _h[^1], _l[^1], _c[^1], _v[^1]) : default;
|
||||
}
|
||||
|
||||
public long LastTime { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _t.Count > 0 ? _t[^1] : 0; }
|
||||
public double LastOpen { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _o.Count > 0 ? _o[^1] : double.NaN; }
|
||||
public double LastHigh { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _h.Count > 0 ? _h[^1] : double.NaN; }
|
||||
public double LastLow { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _l.Count > 0 ? _l[^1] : double.NaN; }
|
||||
public double LastClose { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _c.Count > 0 ? _c[^1] : double.NaN; }
|
||||
public double LastVolume { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _v.Count > 0 ? _v[^1] : double.NaN; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(TBar bar, bool isNew = true)
|
||||
{
|
||||
if (isNew || _c.Count == 0)
|
||||
{
|
||||
_t.Add(bar.Time);
|
||||
_o.Add(bar.Open);
|
||||
_h.Add(bar.High);
|
||||
_l.Add(bar.Low);
|
||||
_c.Add(bar.Close);
|
||||
_v.Add(bar.Volume);
|
||||
}
|
||||
else
|
||||
{
|
||||
int lastIdx = _c.Count - 1;
|
||||
_t[lastIdx] = bar.Time;
|
||||
_o[lastIdx] = bar.Open;
|
||||
_h[lastIdx] = bar.High;
|
||||
_l[lastIdx] = bar.Low;
|
||||
_c[lastIdx] = bar.Close;
|
||||
_v[lastIdx] = bar.Volume;
|
||||
}
|
||||
|
||||
Pub?.Invoke(bar);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(long time, double open, double high, double low, double close, double volume, bool isNew = true) =>
|
||||
Add(new TBar(time, open, high, low, close, volume), isNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(DateTime time, double open, double high, double low, double close, double volume, bool isNew = true) =>
|
||||
Add(new TBar(time.Ticks, open, high, low, close, volume), isNew);
|
||||
|
||||
public void Add(IEnumerable<long> t, IEnumerable<double> o, IEnumerable<double> h, IEnumerable<double> l, IEnumerable<double> c, IEnumerable<double> v)
|
||||
{
|
||||
_t.AddRange(t);
|
||||
_o.AddRange(o);
|
||||
_h.AddRange(h);
|
||||
_l.AddRange(l);
|
||||
_c.AddRange(c);
|
||||
_v.AddRange(v);
|
||||
}
|
||||
|
||||
public IEnumerator<TBar> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _c.Count; i++)
|
||||
{
|
||||
yield return new TBar(_t[i], _o[i], _h[i], _l[i], _c[i], _v[i]);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user