mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-20 19:48:05 +00:00
SIMD Refactor: Merge simd-dev into dev (#55)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) <aider@aider.chat> Co-authored-by: Warp <agent@warp.dev>
This commit is contained in:
co-authored by
Claude Opus 4.5
aider
Warp
parent
5bcdf8d614
commit
86fe32a682
@@ -0,0 +1,219 @@
|
||||
using Skender.Stock.Indicators;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Provides standardized test data for validation tests.
|
||||
/// Uses GBM (Geometric Brownian Motion) to generate realistic price data
|
||||
/// and converts it to formats required by external validation libraries.
|
||||
/// </summary>
|
||||
public sealed class ValidationTestData : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Default number of bars for validation tests.
|
||||
/// 5000 bars ensures sufficient convergence for most indicators.
|
||||
/// </summary>
|
||||
public const int DefaultCount = 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Default starting price for generated data.
|
||||
/// </summary>
|
||||
public const double DefaultStartPrice = 1000.0;
|
||||
|
||||
/// <summary>
|
||||
/// Default annual drift for GBM (5%).
|
||||
/// </summary>
|
||||
public const double DefaultMu = 0.05;
|
||||
|
||||
/// <summary>
|
||||
/// Default annual volatility for GBM (200%).
|
||||
/// High volatility ensures diverse price scenarios.
|
||||
/// </summary>
|
||||
public const double DefaultSigma = 2.0;
|
||||
|
||||
/// <summary>
|
||||
/// Default random seed for reproducibility.
|
||||
/// </summary>
|
||||
public const int DefaultSeed = 123;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the generated bar series.
|
||||
/// </summary>
|
||||
public TBarSeries Bars { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the close price series.
|
||||
/// </summary>
|
||||
public TSeries Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the quotes in Skender.Stock.Indicators format.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Quote> SkenderQuotes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw close price data as a ReadOnlyMemory for span-based APIs.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<double> RawData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw open prices as read-only memory.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<double> OpenPrices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw high prices as read-only memory.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<double> HighPrices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw low prices as read-only memory.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<double> LowPrices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw close prices as read-only memory.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<double> ClosePrices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw volume data as read-only memory.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<double> VolumeData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamps as read-only memory.
|
||||
/// </summary>
|
||||
public ReadOnlyMemory<long> Timestamps { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bars in the dataset.
|
||||
/// </summary>
|
||||
public int Count => Bars.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Creates validation test data with default parameters.
|
||||
/// </summary>
|
||||
public ValidationTestData()
|
||||
: this(DefaultCount, DefaultStartPrice, DefaultMu, DefaultSigma, DefaultSeed)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates validation test data with specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="count">Number of bars to generate</param>
|
||||
/// <param name="startPrice">Starting price</param>
|
||||
/// <param name="mu">Annual drift rate</param>
|
||||
/// <param name="sigma">Annual volatility</param>
|
||||
/// <param name="seed">Random seed for reproducibility</param>
|
||||
public ValidationTestData(
|
||||
int count,
|
||||
double startPrice = DefaultStartPrice,
|
||||
double mu = DefaultMu,
|
||||
double sigma = DefaultSigma,
|
||||
int seed = DefaultSeed)
|
||||
{
|
||||
var gbm = new GBM(startPrice, mu, sigma, seed: seed);
|
||||
Bars = gbm.Fetch(count, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
Data = Bars.Close;
|
||||
|
||||
// Extract raw arrays efficiently (avoid LINQ in hot path)
|
||||
int barCount = Bars.Count;
|
||||
var openPrices = new double[barCount];
|
||||
var highPrices = new double[barCount];
|
||||
var lowPrices = new double[barCount];
|
||||
var closePrices = new double[barCount];
|
||||
var volumeData = new double[barCount];
|
||||
var timestamps = new long[barCount];
|
||||
|
||||
// Use span-based access for efficiency
|
||||
var openSpan = Bars.OpenValues;
|
||||
var highSpan = Bars.HighValues;
|
||||
var lowSpan = Bars.LowValues;
|
||||
var closeSpan = Bars.CloseValues;
|
||||
var volumeSpan = Bars.VolumeValues;
|
||||
var timeSpan = Bars.Times;
|
||||
|
||||
openSpan.CopyTo(openPrices);
|
||||
highSpan.CopyTo(highPrices);
|
||||
lowSpan.CopyTo(lowPrices);
|
||||
closeSpan.CopyTo(closePrices);
|
||||
volumeSpan.CopyTo(volumeData);
|
||||
timeSpan.CopyTo(timestamps);
|
||||
|
||||
// Expose as ReadOnlyMemory to prevent external modification
|
||||
OpenPrices = openPrices;
|
||||
HighPrices = highPrices;
|
||||
LowPrices = lowPrices;
|
||||
ClosePrices = closePrices;
|
||||
VolumeData = volumeData;
|
||||
Timestamps = timestamps;
|
||||
RawData = closePrices;
|
||||
|
||||
// Build Skender quotes without LINQ
|
||||
var quotes = new Quote[barCount];
|
||||
for (int i = 0; i < barCount; i++)
|
||||
{
|
||||
quotes[i] = new Quote
|
||||
{
|
||||
Date = new DateTime(timestamps[i], DateTimeKind.Utc),
|
||||
Open = (decimal)openPrices[i],
|
||||
High = (decimal)highPrices[i],
|
||||
Low = (decimal)lowPrices[i],
|
||||
Close = (decimal)closePrices[i],
|
||||
Volume = (decimal)volumeData[i],
|
||||
};
|
||||
}
|
||||
SkenderQuotes = quotes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ValidationTestData instance with the specified bar count.
|
||||
/// Note: This regenerates data using the same seed rather than slicing existing data,
|
||||
/// ensuring deterministic results but not reusing the parent's generated bars.
|
||||
/// </summary>
|
||||
/// <param name="count">Number of bars to generate (must be between 1 and current Count)</param>
|
||||
/// <returns>A new ValidationTestData instance with freshly generated data</returns>
|
||||
public ValidationTestData CreateSubset(int count)
|
||||
{
|
||||
if (count <= 0 || count > Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), count, $"Count must be between 1 and {Count}");
|
||||
|
||||
return new ValidationTestData(count, DefaultStartPrice, DefaultMu, DefaultSigma, DefaultSeed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the close price span for SIMD operations.
|
||||
/// </summary>
|
||||
public ReadOnlySpan<double> GetCloseSpan() => ClosePrices.Span;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the high price span for SIMD operations.
|
||||
/// </summary>
|
||||
public ReadOnlySpan<double> GetHighSpan() => HighPrices.Span;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the low price span for SIMD operations.
|
||||
/// </summary>
|
||||
public ReadOnlySpan<double> GetLowSpan() => LowPrices.Span;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the open price span for SIMD operations.
|
||||
/// </summary>
|
||||
public ReadOnlySpan<double> GetOpenSpan() => OpenPrices.Span;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the volume span for SIMD operations.
|
||||
/// </summary>
|
||||
public ReadOnlySpan<double> GetVolumeSpan() => VolumeData.Span;
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources (no-op, but implements pattern for test fixtures).
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// No unmanaged resources to dispose
|
||||
// Implemented for IDisposable pattern compatibility with test fixtures
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user