Refactor validation tests for various indicators to utilize shared test data structure

This commit is contained in:
Miha Kralj
2025-12-12 13:47:57 -08:00
parent e6033638ad
commit cea3e0c46d
29 changed files with 1167 additions and 1804 deletions
-68
View File
@@ -1,68 +0,0 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"name":"csharp","languageName":"csharp"},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"html","languageName":"HTML"},{"name":"http","languageName":"HTTP"},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"mermaid","languageName":"Mermaid"},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"value"}]}}
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using QuanTAlib;
using System.IO;
// 1. Setup: Use existing CSV file
// CsvFeed expects a CSV with header: timestamp,open,high,low,close,volume
// Timestamp format: YYYY-MM-DD
string csvPath = "daily_IBM.csv";
Console.WriteLine($"Using CSV file: {csvPath}");
#!csharp
// 2. Initialize CsvFeed
// The feed loads the data and prepares it for streaming
var feed = new CsvFeed(csvPath);
Console.WriteLine("CsvFeed initialized.");
#!csharp
// 3. Streaming Data
// Simulate processing historical data bar by bar
Console.WriteLine("\nStreaming data (first 5 bars):");
int count = 0;
bool isNew = true;
// Get first bar
var bar = feed.Next(isNew: true);
while (isNew && count < 5)
{
count++;
Console.WriteLine($" Bar {count}: {bar}");
// Get next bar
bar = feed.Next(ref isNew);
}
Console.WriteLine($"Streamed {count} bars.");
#!csharp
// 4. Batch Fetching
// Retrieve a specific range of data
Console.WriteLine("\nBatch fetching:");
// Using a date range present in daily_IBM.csv (July 2025)
long startTime = new DateTime(2025, 7, 8).Ticks;
var interval = TimeSpan.FromDays(1);
// Fetch 3 bars starting from July 8th, 2025
var batch = feed.Fetch(5, startTime, interval);
Console.WriteLine($"Fetched {batch.Count} bars:");
foreach (var b in batch)
{
Console.WriteLine($" {b}");
}
-69
View File
@@ -1,69 +0,0 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"name":"csharp","languageName":"csharp"},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"html","languageName":"HTML"},{"name":"http","languageName":"HTTP"},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"mermaid","languageName":"Mermaid"},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"value"}]}}
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using QuanTAlib;
// 1. Initialize GBM Generator
// GBM simulates price movements using Geometric Brownian Motion
// Parameters: Start Price, Drift (mu), Volatility (sigma)
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2);
Console.WriteLine("GBM Generator initialized (Start=100, Drift=5%, Vol=20%)");
#!csharp
// 2. Batch Generation
// Generate a sequence of bars at once
// Useful for backtesting or initializing indicators
long startTime = DateTime.UtcNow.Ticks;
var interval = TimeSpan.FromMinutes(1);
var history = gbm.Fetch(10, startTime, interval);
Console.WriteLine($"Generated {history.Count} bars:");
for (int i = 0; i < history.Count; i++)
{
Console.WriteLine($" Bar {i}: Time={history[i].AsDateTime:HH:mm}, Close={history[i].Close:F2}");
}
#!csharp
// 3. Streaming Generation
// Simulate real-time data feed bar by bar
Console.WriteLine("\nStreaming new bars:");
for (int i = 0; i < 3; i++)
{
var bar = gbm.Next(isNew: true);
Console.WriteLine($" New Bar: {bar.Close:F2}");
}
#!csharp
// 4. Intra-bar Updates
// Simulate real-time price ticks within a single bar
// The High/Low will expand, and Close will update
Console.WriteLine("\nSimulating intra-bar updates:");
// Start a new bar
var liveBar = gbm.Next(isNew: true);
Console.WriteLine($" Open: {liveBar.Open:F2}, Close: {liveBar.Close:F2}");
// Simulate 5 ticks
for (int i = 1; i <= 5; i++)
{
liveBar = gbm.Next(isNew: false);
Console.WriteLine($" Tick {i}: Close={liveBar.Close:F2}, High={liveBar.High:F2}, Low={liveBar.Low:F2}");
}
// Finalize bar
liveBar = gbm.Next(isNew: true);
Console.WriteLine($" Finalized Previous, Started New: {liveBar.Open:F2}");
+185
View File
@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using Xunit;
namespace QuanTAlib.Tests;
public static class ValidationHelper
{
public static void VerifyData<TResult>(TSeries qSeries, List<TResult> sSeries, Func<TResult, double?> selector, int skip = 100, double tolerance = 1e-6)
{
Assert.Equal(qSeries.Count, sSeries.Count);
int count = qSeries.Count;
int start = count - skip;
for (int i = start; i < count; i++)
{
double qValue = qSeries[i].Value;
double? sValue = selector(sSeries[i]);
if (!sValue.HasValue) continue;
Assert.Equal(sValue.Value, qValue, tolerance);
}
}
public static void VerifyData<TResult>(List<double> qResults, List<TResult> sSeries, Func<TResult, double?> selector, int skip = 100, double tolerance = 1e-6)
{
Assert.Equal(qResults.Count, sSeries.Count);
int count = qResults.Count;
int start = count - skip;
for (int i = start; i < count; i++)
{
double qValue = qResults[i];
double? sValue = selector(sSeries[i]);
if (!sValue.HasValue) continue;
Assert.Equal(sValue.Value, qValue, tolerance);
}
}
public static void VerifyData<TResult>(double[] qOutput, List<TResult> sSeries, Func<TResult, double?> selector, int skip = 100, double tolerance = 1e-6)
{
Assert.Equal(qOutput.Length, sSeries.Count);
int count = qOutput.Length;
int start = count - skip;
for (int i = start; i < count; i++)
{
double qValue = qOutput[i];
double? sValue = selector(sSeries[i]);
if (!sValue.HasValue) continue;
Assert.Equal(sValue.Value, qValue, tolerance);
}
}
public static void VerifyData(TSeries qSeries, double[] tOutput, int lookback, int skip = 100, double tolerance = 1e-6)
{
int count = qSeries.Count;
int start = count - skip;
for (int i = start; i < count; i++)
{
double qValue = qSeries[i].Value;
if (i < lookback) continue;
int tIndex = i - lookback;
if (tIndex >= tOutput.Length) continue;
double tValue = tOutput[tIndex];
Assert.Equal(tValue, qValue, tolerance);
}
}
public static void VerifyData(List<double> qResults, double[] tOutput, int lookback, int skip = 100, double tolerance = 1e-6)
{
int count = qResults.Count;
int start = count - skip;
for (int i = start; i < count; i++)
{
double qValue = qResults[i];
if (i < lookback) continue;
int tIndex = i - lookback;
if (tIndex >= tOutput.Length) continue;
double tValue = tOutput[tIndex];
Assert.Equal(tValue, qValue, tolerance);
}
}
public static void VerifyData(double[] qOutput, double[] tOutput, int lookback, int skip = 100, double tolerance = 1e-6)
{
int count = qOutput.Length;
int start = count - skip;
for (int i = start; i < count; i++)
{
double qValue = qOutput[i];
if (i < lookback) continue;
int tIndex = i - lookback;
if (tIndex >= tOutput.Length) continue;
double tValue = tOutput[tIndex];
Assert.Equal(tValue, qValue, tolerance);
}
}
public static void VerifyData(TSeries qSeries, double[] tOutput, Range outRange, int lookback, int skip = 100, double tolerance = 1e-6)
{
int count = qSeries.Count;
int start = count - skip;
int validCount = outRange.End.Value - outRange.Start.Value;
for (int i = start; i < count; i++)
{
double qValue = qSeries[i].Value;
if (i < lookback) continue;
int tIndex = i - lookback;
if (tIndex >= validCount) continue;
double tValue = tOutput[tIndex];
Assert.Equal(tValue, qValue, tolerance);
}
}
public static void VerifyData(List<double> qResults, double[] tOutput, Range outRange, int lookback, int skip = 100, double tolerance = 1e-6)
{
int count = qResults.Count;
int start = count - skip;
int validCount = outRange.End.Value - outRange.Start.Value;
for (int i = start; i < count; i++)
{
double qValue = qResults[i];
if (i < lookback) continue;
int tIndex = i - lookback;
if (tIndex >= validCount) continue;
double tValue = tOutput[tIndex];
Assert.Equal(tValue, qValue, tolerance);
}
}
public static void VerifyData(double[] qOutput, double[] tOutput, Range outRange, int lookback, int skip = 100, double tolerance = 1e-6)
{
int count = qOutput.Length;
int start = count - skip;
int validCount = outRange.End.Value - outRange.Start.Value;
for (int i = start; i < count; i++)
{
double qValue = qOutput[i];
if (i < lookback) continue;
int tIndex = i - lookback;
if (tIndex >= validCount) continue;
double tValue = tOutput[tIndex];
Assert.Equal(tValue, qValue, tolerance);
}
}
}
+58
View File
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Skender.Stock.Indicators;
namespace QuanTAlib.Tests;
public class ValidationTestData : IDisposable
{
public TBarSeries Bars { get; }
public TSeries Data { get; }
public IReadOnlyList<Quote> SkenderQuotes { get; }
public ReadOnlyMemory<double> RawData { get; }
public ValidationTestData(int count = 5000, double startPrice = 1000000.0, double mu = 0.05, double sigma = 2.0, int seed = 123)
{
var gbm = new GBM(startPrice, mu, sigma, seed: seed);
Bars = gbm.Fetch(count, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
Data = Bars.Close;
RawData = Data.Select(x => x.Value).ToArray();
var quotes = new List<Quote>();
for (int i = 0; i < Bars.Count; i++)
{
quotes.Add(new Quote
{
Date = new DateTime(Bars.Open.Times[i], DateTimeKind.Utc),
Open = (decimal)Bars.Open[i].Value,
High = (decimal)Bars.High[i].Value,
Low = (decimal)Bars.Low[i].Value,
Close = (decimal)Bars.Close[i].Value,
Volume = (decimal)Bars.Volume[i].Value
});
}
SkenderQuotes = quotes;
}
private bool _disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Dispose managed state (managed objects)
}
_disposed = true;
}
}
}