mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-23 13:08:04 +00:00
Refactor and expand unit tests for TBarSeries, TSeries, and TValue classes
- Enhanced TBarSeriesTests with additional constructors, methods, and assertions for better coverage. - Improved TSeriesTests to include new constructors, methods, and edge cases. - Expanded TValueTests to cover constructors, implicit conversions, equality checks, and hash codes. - Updated project file to target .NET 10.0 and include internal visibility for tests. - Added Codacy configuration for code quality checks.
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
runtimes:
|
||||||
|
- dart@3.7.2
|
||||||
|
- go@1.22.3
|
||||||
|
- java@17.0.10
|
||||||
|
- node@22.2.0
|
||||||
|
- python@3.11.11
|
||||||
|
tools:
|
||||||
|
- dartanalyzer@3.7.2
|
||||||
|
- eslint@8.57.0
|
||||||
|
- lizard@1.17.31
|
||||||
|
- pmd@7.11.0
|
||||||
|
- pylint@3.3.6
|
||||||
|
- revive@1.7.0
|
||||||
|
- semgrep@1.78.0
|
||||||
|
- trivy@0.66.0
|
||||||
@@ -405,3 +405,7 @@ memory-bank/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
.LSOverride
|
.LSOverride
|
||||||
|
|
||||||
|
|
||||||
|
#Ignore insiders AI rules
|
||||||
|
.github/instructions/codacy.instructions.md
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public class EmaValidationTests : IDisposable
|
|||||||
var qResult = ema.Update(_data);
|
var qResult = ema.Update(_data);
|
||||||
|
|
||||||
// Calculate TA-Lib EMA
|
// Calculate TA-Lib EMA
|
||||||
var retCode = TALib.Functions.Ema(tData, 0..^0, output, out var outRange, period);
|
var retCode = TALib.Functions.Ema<double>(tData, 0..^0, output, out var outRange, period);
|
||||||
|
|
||||||
// Check success
|
// Check success
|
||||||
Assert.Equal(Core.RetCode.Success, retCode);
|
Assert.Equal(Core.RetCode.Success, retCode);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,75 @@ public class EmaVectorTests
|
|||||||
int[] periods = { 10, 20 };
|
int[] periods = { 10, 20 };
|
||||||
var emaVector = new EmaVector(periods);
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
// We can't check private fields directly, but we can check results after 1 step
|
var res = emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
// Alpha = 2 / (P + 1)
|
|
||||||
// P=10 -> A=2/11
|
Assert.Equal(100.0, res[0].Value, 1e-9);
|
||||||
// P=20 -> A=2/21
|
Assert.Equal(100.0, res[1].Value, 1e-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Initialization_WithAlphas_Works()
|
||||||
|
{
|
||||||
|
double[] alphas = { 0.1, 0.2, 0.5 };
|
||||||
|
var emaVector = new EmaVector(alphas);
|
||||||
|
|
||||||
var res = emaVector.Update(new TValue(DateTime.Now, 100.0));
|
var res = emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
|
|
||||||
// First value should be 100.0 due to compensation
|
Assert.Equal(3, res.Length);
|
||||||
Assert.Equal(100.0, res[0].Value, 1e-9);
|
Assert.Equal(100.0, res[0].Value, 1e-9);
|
||||||
Assert.Equal(100.0, res[1].Value, 1e-9);
|
Assert.Equal(100.0, res[1].Value, 1e-9);
|
||||||
|
Assert.Equal(100.0, res[2].Value, 1e-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Initialization_WithZeroPeriod_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
int[] periods = { 10, 0, 20 };
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => new EmaVector(periods));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Initialization_WithNegativePeriod_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
int[] periods = { 10, -5, 20 };
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => new EmaVector(periods));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Initialization_WithZeroAlpha_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
double[] alphas = { 0.1, 0.0, 0.5 };
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => new EmaVector(alphas));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Initialization_WithNegativeAlpha_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
double[] alphas = { 0.1, -0.1, 0.5 };
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => new EmaVector(alphas));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Initialization_WithAlphaGreaterThanOne_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
double[] alphas = { 0.1, 1.5, 0.5 };
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentException>(() => new EmaVector(alphas));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Initialization_WithAlphaEqualToOne_Works()
|
||||||
|
{
|
||||||
|
double[] alphas = { 0.1, 1.0, 0.5 };
|
||||||
|
var emaVector = new EmaVector(alphas);
|
||||||
|
|
||||||
|
var res = emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
|
|
||||||
|
Assert.Equal(3, res.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -105,10 +164,8 @@ public class EmaVectorTests
|
|||||||
|
|
||||||
var series = new TSeries(t, v);
|
var series = new TSeries(t, v);
|
||||||
|
|
||||||
// Batch calculation
|
|
||||||
var batchRes = emaVectorBatch.Calculate(series);
|
var batchRes = emaVectorBatch.Calculate(series);
|
||||||
|
|
||||||
// Streaming calculation
|
|
||||||
for (int i = 0; i < len; i++)
|
for (int i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
var tVal = new TValue(new DateTime(t[i]), v[i]);
|
var tVal = new TValue(new DateTime(t[i]), v[i]);
|
||||||
@@ -121,6 +178,39 @@ public class EmaVectorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Calculate_Static_MatchesInstanceMethod()
|
||||||
|
{
|
||||||
|
int[] periods = { 5, 10, 20 };
|
||||||
|
|
||||||
|
int len = 50;
|
||||||
|
var t = new System.Collections.Generic.List<long>(len);
|
||||||
|
var v = new System.Collections.Generic.List<double>(len);
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
t.Add(now.AddMinutes(i).Ticks);
|
||||||
|
v.Add(Math.Sin(i * 0.1) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
var series = new TSeries(t, v);
|
||||||
|
|
||||||
|
var instanceEma = new EmaVector(periods);
|
||||||
|
var instanceRes = instanceEma.Calculate(series);
|
||||||
|
|
||||||
|
var staticRes = EmaVector.Calculate(series, periods);
|
||||||
|
|
||||||
|
for (int i = 0; i < periods.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.Equal(instanceRes[i].Count, staticRes[i].Count);
|
||||||
|
for (int j = 0; j < len; j++)
|
||||||
|
{
|
||||||
|
Assert.Equal(instanceRes[i].Values[j], staticRes[i].Values[j], 1e-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Reset_ClearsState()
|
public void Reset_ClearsState()
|
||||||
{
|
{
|
||||||
@@ -130,7 +220,6 @@ public class EmaVectorTests
|
|||||||
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
emaVector.Reset();
|
emaVector.Reset();
|
||||||
|
|
||||||
// After reset, next calculation should treat it as first value (warmup)
|
|
||||||
var res = emaVector.Update(new TValue(DateTime.Now, 200.0));
|
var res = emaVector.Update(new TValue(DateTime.Now, 200.0));
|
||||||
|
|
||||||
Assert.Equal(200.0, res[0].Value, 1e-9);
|
Assert.Equal(200.0, res[0].Value, 1e-9);
|
||||||
@@ -142,14 +231,11 @@ public class EmaVectorTests
|
|||||||
int[] periods = { 10, 20 };
|
int[] periods = { 10, 20 };
|
||||||
var emaVector = new EmaVector(periods);
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
// Feed some valid values
|
|
||||||
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
emaVector.Update(new TValue(DateTime.Now, 110.0));
|
emaVector.Update(new TValue(DateTime.Now, 110.0));
|
||||||
|
|
||||||
// Feed NaN - should use last valid value (110)
|
|
||||||
var resultAfterNaN = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
var resultAfterNaN = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
||||||
|
|
||||||
// All results should be finite (not NaN)
|
|
||||||
foreach (var result in resultAfterNaN)
|
foreach (var result in resultAfterNaN)
|
||||||
{
|
{
|
||||||
Assert.True(double.IsFinite(result.Value), $"Expected finite value but got {result.Value}");
|
Assert.True(double.IsFinite(result.Value), $"Expected finite value but got {result.Value}");
|
||||||
@@ -162,18 +248,15 @@ public class EmaVectorTests
|
|||||||
int[] periods = { 10, 20 };
|
int[] periods = { 10, 20 };
|
||||||
var emaVector = new EmaVector(periods);
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
// Feed some valid values
|
|
||||||
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
emaVector.Update(new TValue(DateTime.Now, 110.0));
|
emaVector.Update(new TValue(DateTime.Now, 110.0));
|
||||||
|
|
||||||
// Feed positive infinity - should use last valid value
|
|
||||||
var resultAfterPosInf = emaVector.Update(new TValue(DateTime.Now, double.PositiveInfinity));
|
var resultAfterPosInf = emaVector.Update(new TValue(DateTime.Now, double.PositiveInfinity));
|
||||||
foreach (var result in resultAfterPosInf)
|
foreach (var result in resultAfterPosInf)
|
||||||
{
|
{
|
||||||
Assert.True(double.IsFinite(result.Value));
|
Assert.True(double.IsFinite(result.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Feed negative infinity - should use last valid value
|
|
||||||
var resultAfterNegInf = emaVector.Update(new TValue(DateTime.Now, double.NegativeInfinity));
|
var resultAfterNegInf = emaVector.Update(new TValue(DateTime.Now, double.NegativeInfinity));
|
||||||
foreach (var result in resultAfterNegInf)
|
foreach (var result in resultAfterNegInf)
|
||||||
{
|
{
|
||||||
@@ -187,17 +270,14 @@ public class EmaVectorTests
|
|||||||
int[] periods = { 5, 10 };
|
int[] periods = { 5, 10 };
|
||||||
var emaVector = new EmaVector(periods);
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
// Feed valid values
|
|
||||||
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
emaVector.Update(new TValue(DateTime.Now, 110.0));
|
emaVector.Update(new TValue(DateTime.Now, 110.0));
|
||||||
emaVector.Update(new TValue(DateTime.Now, 120.0));
|
emaVector.Update(new TValue(DateTime.Now, 120.0));
|
||||||
|
|
||||||
// Feed multiple NaN values
|
|
||||||
var r1 = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
var r1 = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
||||||
var r2 = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
var r2 = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
||||||
var r3 = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
var r3 = emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
||||||
|
|
||||||
// All results should be finite
|
|
||||||
foreach (var result in r1) Assert.True(double.IsFinite(result.Value));
|
foreach (var result in r1) Assert.True(double.IsFinite(result.Value));
|
||||||
foreach (var result in r2) Assert.True(double.IsFinite(result.Value));
|
foreach (var result in r2) Assert.True(double.IsFinite(result.Value));
|
||||||
foreach (var result in r3) Assert.True(double.IsFinite(result.Value));
|
foreach (var result in r3) Assert.True(double.IsFinite(result.Value));
|
||||||
@@ -209,7 +289,6 @@ public class EmaVectorTests
|
|||||||
int[] periods = { 5, 10 };
|
int[] periods = { 5, 10 };
|
||||||
var emaVector = new EmaVector(periods);
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
// Create series with NaN values interspersed
|
|
||||||
var t = new System.Collections.Generic.List<long>();
|
var t = new System.Collections.Generic.List<long>();
|
||||||
var v = new System.Collections.Generic.List<double>();
|
var v = new System.Collections.Generic.List<double>();
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
@@ -224,7 +303,6 @@ public class EmaVectorTests
|
|||||||
var series = new TSeries(t, v);
|
var series = new TSeries(t, v);
|
||||||
var results = emaVector.Calculate(series);
|
var results = emaVector.Calculate(series);
|
||||||
|
|
||||||
// All results should be finite for all periods
|
|
||||||
foreach (var periodResults in results)
|
foreach (var periodResults in results)
|
||||||
{
|
{
|
||||||
foreach (var val in periodResults.Values)
|
foreach (var val in periodResults.Values)
|
||||||
@@ -240,14 +318,11 @@ public class EmaVectorTests
|
|||||||
int[] periods = { 10 };
|
int[] periods = { 10 };
|
||||||
var emaVector = new EmaVector(periods);
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
// Feed values including NaN
|
|
||||||
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
emaVector.Update(new TValue(DateTime.Now, double.NaN));
|
||||||
|
|
||||||
// Reset
|
|
||||||
emaVector.Reset();
|
emaVector.Reset();
|
||||||
|
|
||||||
// After reset, first valid value should establish new baseline
|
|
||||||
var result = emaVector.Update(new TValue(DateTime.Now, 50.0));
|
var result = emaVector.Update(new TValue(DateTime.Now, 50.0));
|
||||||
Assert.Equal(50.0, result[0].Value, 1e-9);
|
Assert.Equal(50.0, result[0].Value, 1e-9);
|
||||||
}
|
}
|
||||||
@@ -259,7 +334,6 @@ public class EmaVectorTests
|
|||||||
var emaVector = new EmaVector(periods);
|
var emaVector = new EmaVector(periods);
|
||||||
var emaSingles = periods.Select(p => new Ema(p)).ToArray();
|
var emaSingles = periods.Select(p => new Ema(p)).ToArray();
|
||||||
|
|
||||||
// Data with NaN values
|
|
||||||
var values = new double[] { 10, 20, double.NaN, 40, double.PositiveInfinity, 60, 70 };
|
var values = new double[] { 10, 20, double.NaN, 40, double.PositiveInfinity, 60, 70 };
|
||||||
var time = DateTime.Now;
|
var time = DateTime.Now;
|
||||||
|
|
||||||
@@ -277,4 +351,32 @@ public class EmaVectorTests
|
|||||||
time = time.AddMinutes(1);
|
time = time.AddMinutes(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Values_Property_UpdatesAfterUpdate()
|
||||||
|
{
|
||||||
|
int[] periods = { 5, 10 };
|
||||||
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
|
var result = emaVector.Update(new TValue(DateTime.Now, 100.0));
|
||||||
|
|
||||||
|
Assert.Equal(result[0].Value, emaVector.Values[0].Value);
|
||||||
|
Assert.Equal(result[1].Value, emaVector.Values[1].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Values_Property_UpdatesAfterCalculate()
|
||||||
|
{
|
||||||
|
int[] periods = { 5, 10 };
|
||||||
|
var emaVector = new EmaVector(periods);
|
||||||
|
|
||||||
|
var t = new System.Collections.Generic.List<long> { 100, 200, 300 };
|
||||||
|
var v = new System.Collections.Generic.List<double> { 10.0, 20.0, 30.0 };
|
||||||
|
var series = new TSeries(t, v);
|
||||||
|
|
||||||
|
var results = emaVector.Calculate(series);
|
||||||
|
|
||||||
|
Assert.Equal(results[0].Last.Value, emaVector.Values[0].Value, 1e-9);
|
||||||
|
Assert.Equal(results[1].Last.Value, emaVector.Values[1].Value, 1e-9);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,79 @@ namespace QuanTAlib.Tests;
|
|||||||
|
|
||||||
public class SimdExtensionsTests
|
public class SimdExtensionsTests
|
||||||
{
|
{
|
||||||
|
// ContainsNonFinite tests
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_EmptySpan_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var span = ReadOnlySpan<double>.Empty;
|
||||||
|
Assert.False(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_AllFinite_ReturnsFalse()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.False(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_ContainsNaN_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, double.NaN, 4.0, 5.0, 6.0, 7.0, 8.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_ContainsPositiveInfinity_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0, double.PositiveInfinity, 5.0, 6.0, 7.0, 8.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_ContainsNegativeInfinity_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0, 4.0, double.NegativeInfinity, 6.0, 7.0, 8.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_NonFiniteInRemainder_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, double.NaN];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_SingleNaN_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [double.NaN];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_TwoElements_AllFinite_ReturnsFalse()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.False(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFinite_TwoElements_OneNaN_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, double.NaN];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(span.ContainsNonFinite());
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumSIMD tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SumSIMD_EmptySpan_ReturnsZero()
|
public void SumSIMD_EmptySpan_ReturnsZero()
|
||||||
{
|
{
|
||||||
@@ -21,6 +94,14 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(42.5, span.SumSIMD());
|
Assert.Equal(42.5, span.SumSIMD());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SumSIMD_TwoElements_ReturnsSum()
|
||||||
|
{
|
||||||
|
double[] data = [1.5, 2.5];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(4.0, span.SumSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SumSIMD_MultipleElements_ReturnsCorrectSum()
|
public void SumSIMD_MultipleElements_ReturnsCorrectSum()
|
||||||
{
|
{
|
||||||
@@ -37,10 +118,35 @@ public class SimdExtensionsTests
|
|||||||
data[i] = i + 1.0;
|
data[i] = i + 1.0;
|
||||||
|
|
||||||
var span = new ReadOnlySpan<double>(data);
|
var span = new ReadOnlySpan<double>(data);
|
||||||
double expected = 1000.0 * 1001.0 / 2.0; // Sum of 1..1000
|
double expected = 1000.0 * 1001.0 / 2.0;
|
||||||
Assert.Equal(expected, span.SumSIMD(), precision: 8);
|
Assert.Equal(expected, span.SumSIMD(), precision: 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SumSIMD_ContainsNaN_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, double.NaN, 4.0, 5.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.SumSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SumSIMD_ContainsInfinity_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, double.PositiveInfinity, 4.0, 5.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.SumSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SumSIMD_NegativeValues_ReturnsCorrectSum()
|
||||||
|
{
|
||||||
|
double[] data = [-1.0, -2.0, -3.0, -4.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(-10.0, span.SumSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinSIMD tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MinSIMD_EmptySpan_ReturnsNaN()
|
public void MinSIMD_EmptySpan_ReturnsNaN()
|
||||||
{
|
{
|
||||||
@@ -56,6 +162,14 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(42.5, span.MinSIMD());
|
Assert.Equal(42.5, span.MinSIMD());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinSIMD_TwoElements_ReturnsMinimum()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(2.0, span.MinSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MinSIMD_MultipleElements_ReturnsMinimum()
|
public void MinSIMD_MultipleElements_ReturnsMinimum()
|
||||||
{
|
{
|
||||||
@@ -64,6 +178,31 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(1.0, span.MinSIMD());
|
Assert.Equal(1.0, span.MinSIMD());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinSIMD_ContainsNaN_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, double.NaN, 1.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.MinSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinSIMD_MinInRemainder_ReturnsCorrectMin()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, 8.0, 6.0, 9.0, 3.0, 7.0, 4.0, 0.5];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(0.5, span.MinSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinSIMD_NegativeValues_ReturnsMinimum()
|
||||||
|
{
|
||||||
|
double[] data = [-5.0, -2.0, -8.0, -1.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(-8.0, span.MinSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxSIMD tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MaxSIMD_EmptySpan_ReturnsNaN()
|
public void MaxSIMD_EmptySpan_ReturnsNaN()
|
||||||
{
|
{
|
||||||
@@ -79,6 +218,14 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(42.5, span.MaxSIMD());
|
Assert.Equal(42.5, span.MaxSIMD());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxSIMD_TwoElements_ReturnsMaximum()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(9.0, span.MaxSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MaxSIMD_MultipleElements_ReturnsMaximum()
|
public void MaxSIMD_MultipleElements_ReturnsMaximum()
|
||||||
{
|
{
|
||||||
@@ -87,6 +234,31 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(9.0, span.MaxSIMD());
|
Assert.Equal(9.0, span.MaxSIMD());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxSIMD_ContainsNaN_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, double.NaN, 1.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.MaxSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxSIMD_MaxInRemainder_ReturnsCorrectMax()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, 8.0, 6.0, 4.0, 3.0, 7.0, 1.0, 99.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(99.0, span.MaxSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxSIMD_NegativeValues_ReturnsMaximum()
|
||||||
|
{
|
||||||
|
double[] data = [-5.0, -2.0, -8.0, -1.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(-1.0, span.MaxSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
|
// AverageSIMD tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AverageSIMD_EmptySpan_ReturnsNaN()
|
public void AverageSIMD_EmptySpan_ReturnsNaN()
|
||||||
{
|
{
|
||||||
@@ -94,6 +266,14 @@ public class SimdExtensionsTests
|
|||||||
Assert.True(double.IsNaN(span.AverageSIMD()));
|
Assert.True(double.IsNaN(span.AverageSIMD()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AverageSIMD_TwoElements_ReturnsAverage()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, 4.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(3.0, span.AverageSIMD());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AverageSIMD_MultipleElements_ReturnsCorrectAverage()
|
public void AverageSIMD_MultipleElements_ReturnsCorrectAverage()
|
||||||
{
|
{
|
||||||
@@ -102,6 +282,15 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(3.0, span.AverageSIMD(), precision: 10);
|
Assert.Equal(3.0, span.AverageSIMD(), precision: 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AverageSIMD_ContainsNaN_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, double.NaN, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.AverageSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarianceSIMD tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VarianceSIMD_LessThanTwoElements_ReturnsNaN()
|
public void VarianceSIMD_LessThanTwoElements_ReturnsNaN()
|
||||||
{
|
{
|
||||||
@@ -110,28 +299,113 @@ public class SimdExtensionsTests
|
|||||||
Assert.True(double.IsNaN(span.VarianceSIMD()));
|
Assert.True(double.IsNaN(span.VarianceSIMD()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceSIMD_EmptySpan_ReturnsNaN()
|
||||||
|
{
|
||||||
|
var span = ReadOnlySpan<double>.Empty;
|
||||||
|
Assert.True(double.IsNaN(span.VarianceSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceSIMD_TwoElements_ReturnsCorrect()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(2.0, span.VarianceSIMD(), precision: 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceSIMD_ThreeElements_ReturnsCorrect()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(1.0, span.VarianceSIMD(), precision: 10);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VarianceSIMD_MultipleElements_ReturnsCorrectVariance()
|
public void VarianceSIMD_MultipleElements_ReturnsCorrectVariance()
|
||||||
{
|
{
|
||||||
double[] data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
|
double[] data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
|
||||||
var span = new ReadOnlySpan<double>(data);
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
|
||||||
// Expected variance: 4.571428... (sample variance)
|
|
||||||
double variance = span.VarianceSIMD();
|
double variance = span.VarianceSIMD();
|
||||||
Assert.True(Math.Abs(variance - 4.571428) < 0.0001);
|
Assert.True(Math.Abs(variance - 4.571428) < 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceSIMD_WithProvidedMean_UsesProvidedMean()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
|
||||||
|
double mean = 5.0;
|
||||||
|
double variance = span.VarianceSIMD(mean);
|
||||||
|
|
||||||
|
Assert.True(variance > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceSIMD_ContainsNaN_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, double.NaN, 4.0, 5.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.VarianceSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceSIMD_WithNaNMean_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, 4.0, 4.0, 5.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.VarianceSIMD(double.NaN)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceSIMD_WithInfinityMean_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, 4.0, 4.0, 5.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.VarianceSIMD(double.PositiveInfinity)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdDevSIMD tests
|
||||||
|
[Fact]
|
||||||
|
public void StdDevSIMD_TwoElements_ReturnsCorrect()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(Math.Abs(span.StdDevSIMD() - 1.414) < 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StdDevSIMD_MultipleElements_ReturnsCorrectStdDev()
|
public void StdDevSIMD_MultipleElements_ReturnsCorrectStdDev()
|
||||||
{
|
{
|
||||||
double[] data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
|
double[] data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
|
||||||
var span = new ReadOnlySpan<double>(data);
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
|
||||||
// Expected std dev: sqrt(4.571428) ≈ 2.138
|
|
||||||
double stdDev = span.StdDevSIMD();
|
double stdDev = span.StdDevSIMD();
|
||||||
Assert.True(Math.Abs(stdDev - 2.138) < 0.01);
|
Assert.True(Math.Abs(stdDev - 2.138) < 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StdDevSIMD_WithProvidedMean_Works()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
|
||||||
|
double stdDev = span.StdDevSIMD(5.0);
|
||||||
|
Assert.True(stdDev > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StdDevSIMD_ContainsNaN_ReturnsNaN()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, double.NaN, 4.0, 5.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(double.IsNaN(span.StdDevSIMD()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinMaxSIMD tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MinMaxSIMD_EmptySpan_ReturnsBothNaN()
|
public void MinMaxSIMD_EmptySpan_ReturnsBothNaN()
|
||||||
{
|
{
|
||||||
@@ -151,6 +425,16 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(42.5, max);
|
Assert.Equal(42.5, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinMaxSIMD_TwoElements_ReturnsCorrect()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
var (min, max) = span.MinMaxSIMD();
|
||||||
|
Assert.Equal(2.0, min);
|
||||||
|
Assert.Equal(5.0, max);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MinMaxSIMD_MultipleElements_ReturnsCorrectMinMax()
|
public void MinMaxSIMD_MultipleElements_ReturnsCorrectMinMax()
|
||||||
{
|
{
|
||||||
@@ -161,6 +445,37 @@ public class SimdExtensionsTests
|
|||||||
Assert.Equal(9.0, max);
|
Assert.Equal(9.0, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinMaxSIMD_ContainsNaN_ReturnsBothNaN()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, double.NaN, 1.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
var (min, max) = span.MinMaxSIMD();
|
||||||
|
Assert.True(double.IsNaN(min));
|
||||||
|
Assert.True(double.IsNaN(max));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinMaxSIMD_MinMaxInRemainder_ReturnsCorrect()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, 8.0, 6.0, 4.0, 3.0, 7.0, 5.0, 0.1, 99.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
var (min, max) = span.MinMaxSIMD();
|
||||||
|
Assert.Equal(0.1, min);
|
||||||
|
Assert.Equal(99.0, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinMaxSIMD_NegativeValues_ReturnsCorrect()
|
||||||
|
{
|
||||||
|
double[] data = [-5.0, -2.0, -8.0, -1.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
var (min, max) = span.MinMaxSIMD();
|
||||||
|
Assert.Equal(-8.0, min);
|
||||||
|
Assert.Equal(-1.0, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integration tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SIMD_WorksWithTSeriesValues()
|
public void SIMD_WorksWithTSeriesValues()
|
||||||
{
|
{
|
||||||
@@ -179,7 +494,7 @@ public class SimdExtensionsTests
|
|||||||
double max = values.MaxSIMD();
|
double max = values.MaxSIMD();
|
||||||
var (minAlt, maxAlt) = values.MinMaxSIMD();
|
var (minAlt, maxAlt) = values.MinMaxSIMD();
|
||||||
|
|
||||||
Assert.Equal(5050.0, sum, precision: 8); // Sum of 1..100
|
Assert.Equal(5050.0, sum, precision: 8);
|
||||||
Assert.Equal(50.5, avg, precision: 8);
|
Assert.Equal(50.5, avg, precision: 8);
|
||||||
Assert.Equal(1.0, min);
|
Assert.Equal(1.0, min);
|
||||||
Assert.Equal(100.0, max);
|
Assert.Equal(100.0, max);
|
||||||
@@ -211,17 +526,14 @@ public class SimdExtensionsTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void SIMD_PerformanceTest_LargeDataset()
|
public void SIMD_PerformanceTest_LargeDataset()
|
||||||
{
|
{
|
||||||
// Generate large dataset
|
|
||||||
var gbm = new GBM(startPrice: 100.0);
|
var gbm = new GBM(startPrice: 100.0);
|
||||||
long startTime = DateTime.UtcNow.Ticks;
|
long startTime = DateTime.UtcNow.Ticks;
|
||||||
var interval = TimeSpan.FromMinutes(1);
|
var interval = TimeSpan.FromMinutes(1);
|
||||||
var bars = gbm.Fetch(10000, startTime, interval);
|
var bars = gbm.Fetch(10000, startTime, interval);
|
||||||
var closeValues = bars.Close.Values;
|
var closeValues = bars.Close.Values;
|
||||||
|
|
||||||
// Warm up
|
|
||||||
_ = closeValues.SumSIMD();
|
_ = closeValues.SumSIMD();
|
||||||
|
|
||||||
// Test SIMD operations
|
|
||||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
|
||||||
double sum = closeValues.SumSIMD();
|
double sum = closeValues.SumSIMD();
|
||||||
@@ -234,7 +546,6 @@ public class SimdExtensionsTests
|
|||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
|
|
||||||
// Verify results are valid
|
|
||||||
Assert.True(sum > 0);
|
Assert.True(sum > 0);
|
||||||
Assert.True(avg > 0);
|
Assert.True(avg > 0);
|
||||||
Assert.True(min > 0);
|
Assert.True(min > 0);
|
||||||
@@ -242,8 +553,178 @@ public class SimdExtensionsTests
|
|||||||
Assert.True(variance > 0);
|
Assert.True(variance > 0);
|
||||||
Assert.True(stdDev > 0);
|
Assert.True(stdDev > 0);
|
||||||
|
|
||||||
// Performance should be sub-millisecond for 10k elements
|
|
||||||
Assert.True(sw.ElapsedMilliseconds < 10,
|
Assert.True(sw.ElapsedMilliseconds < 10,
|
||||||
$"SIMD operations took {sw.ElapsedMilliseconds}ms, expected < 10ms");
|
$"SIMD operations took {sw.ElapsedMilliseconds}ms, expected < 10ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SIMD_ScalarFallback_SmallArray()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
|
||||||
|
Assert.Equal(6.0, span.SumSIMD());
|
||||||
|
Assert.Equal(1.0, span.MinSIMD());
|
||||||
|
Assert.Equal(3.0, span.MaxSIMD());
|
||||||
|
Assert.Equal(2.0, span.AverageSIMD());
|
||||||
|
|
||||||
|
var (min, max) = span.MinMaxSIMD();
|
||||||
|
Assert.Equal(1.0, min);
|
||||||
|
Assert.Equal(3.0, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests for internal scalar implementations
|
||||||
|
public class SimdScalarFallbackTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFiniteScalar_AllFinite_ReturnsFalse()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0, 4.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.False(SimdExtensions.ContainsNonFiniteScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFiniteScalar_ContainsNaN_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, double.NaN, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(SimdExtensions.ContainsNonFiniteScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFiniteScalar_ContainsInfinity_ReturnsTrue()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, double.PositiveInfinity, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.True(SimdExtensions.ContainsNonFiniteScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ContainsNonFiniteScalar_Empty_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var span = ReadOnlySpan<double>.Empty;
|
||||||
|
Assert.False(SimdExtensions.ContainsNonFiniteScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SumScalar_MultipleElements_ReturnsCorrectSum()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 2.0, 3.0, 4.0, 5.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(15.0, SimdExtensions.SumScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SumScalar_NegativeValues_ReturnsCorrectSum()
|
||||||
|
{
|
||||||
|
double[] data = [-1.0, -2.0, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(0.0, SimdExtensions.SumScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SumScalar_Empty_ReturnsZero()
|
||||||
|
{
|
||||||
|
var span = ReadOnlySpan<double>.Empty;
|
||||||
|
Assert.Equal(0.0, SimdExtensions.SumScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinScalar_MultipleElements_ReturnsMinimum()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, 8.0, 1.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(1.0, SimdExtensions.MinScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinScalar_NegativeValues_ReturnsMinimum()
|
||||||
|
{
|
||||||
|
double[] data = [-5.0, -2.0, -8.0, -1.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(-8.0, SimdExtensions.MinScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinScalar_SingleElement_ReturnsElement()
|
||||||
|
{
|
||||||
|
double[] data = [42.5];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(42.5, SimdExtensions.MinScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxScalar_MultipleElements_ReturnsMaximum()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, 8.0, 1.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(9.0, SimdExtensions.MaxScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxScalar_NegativeValues_ReturnsMaximum()
|
||||||
|
{
|
||||||
|
double[] data = [-5.0, -2.0, -8.0, -1.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(-1.0, SimdExtensions.MaxScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MaxScalar_SingleElement_ReturnsElement()
|
||||||
|
{
|
||||||
|
double[] data = [42.5];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
Assert.Equal(42.5, SimdExtensions.MaxScalar(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceScalar_MultipleElements_ReturnsCorrectVariance()
|
||||||
|
{
|
||||||
|
double[] data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
double mean = 5.0;
|
||||||
|
double variance = SimdExtensions.VarianceScalar(span, mean);
|
||||||
|
Assert.True(Math.Abs(variance - 4.571428) < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VarianceScalar_TwoElements_ReturnsCorrectVariance()
|
||||||
|
{
|
||||||
|
double[] data = [1.0, 3.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
double mean = 2.0;
|
||||||
|
Assert.Equal(2.0, SimdExtensions.VarianceScalar(span, mean), precision: 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinMaxScalar_MultipleElements_ReturnsCorrectMinMax()
|
||||||
|
{
|
||||||
|
double[] data = [5.0, 2.0, 8.0, 1.0, 9.0, 3.0, 7.0, 4.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
var (min, max) = SimdExtensions.MinMaxScalar(span);
|
||||||
|
Assert.Equal(1.0, min);
|
||||||
|
Assert.Equal(9.0, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinMaxScalar_NegativeValues_ReturnsCorrectMinMax()
|
||||||
|
{
|
||||||
|
double[] data = [-5.0, -2.0, -8.0, -1.0];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
var (min, max) = SimdExtensions.MinMaxScalar(span);
|
||||||
|
Assert.Equal(-8.0, min);
|
||||||
|
Assert.Equal(-1.0, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MinMaxScalar_SingleElement_ReturnsSameValue()
|
||||||
|
{
|
||||||
|
double[] data = [42.5];
|
||||||
|
var span = new ReadOnlySpan<double>(data);
|
||||||
|
var (min, max) = SimdExtensions.MinMaxScalar(span);
|
||||||
|
Assert.Equal(42.5, min);
|
||||||
|
Assert.Equal(42.5, max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,76 @@ namespace QuanTAlib;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class SimdExtensions
|
public static class SimdExtensions
|
||||||
{
|
{
|
||||||
|
// Internal scalar implementations for testability
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool ContainsNonFiniteScalar(ReadOnlySpan<double> span)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
if (!double.IsFinite(span[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static double SumScalar(ReadOnlySpan<double> span)
|
||||||
|
{
|
||||||
|
double scalar = 0.0;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
scalar += span[i];
|
||||||
|
return scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static double MinScalar(ReadOnlySpan<double> span)
|
||||||
|
{
|
||||||
|
double min = span[0];
|
||||||
|
for (int i = 1; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
if (span[i] < min)
|
||||||
|
min = span[i];
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static double MaxScalar(ReadOnlySpan<double> span)
|
||||||
|
{
|
||||||
|
double max = span[0];
|
||||||
|
for (int i = 1; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
if (span[i] > max)
|
||||||
|
max = span[i];
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static double VarianceScalar(ReadOnlySpan<double> span, double mean)
|
||||||
|
{
|
||||||
|
double sumSquares = 0.0;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
double diff = span[i] - mean;
|
||||||
|
sumSquares += diff * diff;
|
||||||
|
}
|
||||||
|
return sumSquares / (span.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static (double Min, double Max) MinMaxScalar(ReadOnlySpan<double> span)
|
||||||
|
{
|
||||||
|
double scalarMin = span[0];
|
||||||
|
double scalarMax = span[0];
|
||||||
|
for (int i = 1; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
if (span[i] < scalarMin) scalarMin = span[i];
|
||||||
|
if (span[i] > scalarMax) scalarMax = span[i];
|
||||||
|
}
|
||||||
|
return (scalarMin, scalarMax);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if span contains any non-finite values (NaN or Infinity).
|
/// Checks if span contains any non-finite values (NaN or Infinity).
|
||||||
/// Returns true if any non-finite value is found.
|
/// Returns true if any non-finite value is found.
|
||||||
@@ -26,8 +96,6 @@ public static class SimdExtensions
|
|||||||
for (; i <= span.Length - vectorSize; i += vectorSize)
|
for (; i <= span.Length - vectorSize; i += vectorSize)
|
||||||
{
|
{
|
||||||
var vector = new Vector<double>(span.Slice(i, vectorSize));
|
var vector = new Vector<double>(span.Slice(i, vectorSize));
|
||||||
// Check for NaN: NaN != NaN is true
|
|
||||||
// Check for Infinity: IsInfinity
|
|
||||||
for (int j = 0; j < vectorSize; j++)
|
for (int j = 0; j < vectorSize; j++)
|
||||||
{
|
{
|
||||||
if (!double.IsFinite(vector[j]))
|
if (!double.IsFinite(vector[j]))
|
||||||
@@ -45,13 +113,7 @@ public static class SimdExtensions
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar fallback
|
return ContainsNonFiniteScalar(span);
|
||||||
for (int i = 0; i < span.Length; i++)
|
|
||||||
{
|
|
||||||
if (!double.IsFinite(span[i]))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -92,11 +154,7 @@ public static class SimdExtensions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar fallback
|
return SumScalar(span);
|
||||||
double scalar = 0.0;
|
|
||||||
for (int i = 0; i < span.Length; i++)
|
|
||||||
scalar += span[i];
|
|
||||||
return scalar;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -144,14 +202,7 @@ public static class SimdExtensions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar fallback
|
return MinScalar(span);
|
||||||
double min = span[0];
|
|
||||||
for (int i = 1; i < span.Length; i++)
|
|
||||||
{
|
|
||||||
if (span[i] < min)
|
|
||||||
min = span[i];
|
|
||||||
}
|
|
||||||
return min;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -199,14 +250,7 @@ public static class SimdExtensions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar fallback
|
return MaxScalar(span);
|
||||||
double max = span[0];
|
|
||||||
for (int i = 1; i < span.Length; i++)
|
|
||||||
{
|
|
||||||
if (span[i] > max)
|
|
||||||
max = span[i];
|
|
||||||
}
|
|
||||||
return max;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -270,14 +314,7 @@ public static class SimdExtensions
|
|||||||
return result / (span.Length - 1);
|
return result / (span.Length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar fallback
|
return VarianceScalar(span, m);
|
||||||
double sumSquares = 0.0;
|
|
||||||
for (int i = 0; i < span.Length; i++)
|
|
||||||
{
|
|
||||||
double diff = span[i] - m;
|
|
||||||
sumSquares += diff * diff;
|
|
||||||
}
|
|
||||||
return sumSquares / (span.Length - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -339,14 +376,6 @@ public static class SimdExtensions
|
|||||||
return (min, max);
|
return (min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar fallback
|
return MinMaxScalar(span);
|
||||||
double scalarMin = span[0];
|
|
||||||
double scalarMax = span[0];
|
|
||||||
for (int i = 1; i < span.Length; i++)
|
|
||||||
{
|
|
||||||
if (span[i] < scalarMin) scalarMin = span[i];
|
|
||||||
if (span[i] > scalarMax) scalarMax = span[i];
|
|
||||||
}
|
|
||||||
return (scalarMin, scalarMax);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+282
-5
@@ -26,39 +26,144 @@ namespace QuanTAlib.Tests
|
|||||||
Assert.Equal(volume, bar.Volume);
|
Assert.Equal(volume, bar.Volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithDateTime_SetsPropertiesCorrectly()
|
||||||
|
{
|
||||||
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
double open = 100;
|
||||||
|
double high = 110;
|
||||||
|
double low = 90;
|
||||||
|
double close = 105;
|
||||||
|
double volume = 1000;
|
||||||
|
|
||||||
|
var bar = new TBar(dateTime, open, high, low, close, volume);
|
||||||
|
|
||||||
|
Assert.Equal(dateTime.Ticks, bar.Time);
|
||||||
|
Assert.Equal(open, bar.Open);
|
||||||
|
Assert.Equal(high, bar.High);
|
||||||
|
Assert.Equal(low, bar.Low);
|
||||||
|
Assert.Equal(close, bar.Close);
|
||||||
|
Assert.Equal(volume, bar.Volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AsDateTime_ReturnsCorrectDateTime()
|
||||||
|
{
|
||||||
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
var bar = new TBar(dateTime, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.Equal(dateTime, bar.AsDateTime);
|
||||||
|
Assert.Equal(DateTimeKind.Utc, bar.AsDateTime.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void O_Property_ReturnsTValueWithOpenPrice()
|
||||||
|
{
|
||||||
|
long time = DateTime.UtcNow.Ticks;
|
||||||
|
var bar = new TBar(time, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
TValue o = bar.O;
|
||||||
|
|
||||||
|
Assert.Equal(time, o.Time);
|
||||||
|
Assert.Equal(100.0, o.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void H_Property_ReturnsTValueWithHighPrice()
|
||||||
|
{
|
||||||
|
long time = DateTime.UtcNow.Ticks;
|
||||||
|
var bar = new TBar(time, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
TValue h = bar.H;
|
||||||
|
|
||||||
|
Assert.Equal(time, h.Time);
|
||||||
|
Assert.Equal(110.0, h.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void L_Property_ReturnsTValueWithLowPrice()
|
||||||
|
{
|
||||||
|
long time = DateTime.UtcNow.Ticks;
|
||||||
|
var bar = new TBar(time, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
TValue l = bar.L;
|
||||||
|
|
||||||
|
Assert.Equal(time, l.Time);
|
||||||
|
Assert.Equal(90.0, l.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void C_Property_ReturnsTValueWithClosePrice()
|
||||||
|
{
|
||||||
|
long time = DateTime.UtcNow.Ticks;
|
||||||
|
var bar = new TBar(time, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
TValue c = bar.C;
|
||||||
|
|
||||||
|
Assert.Equal(time, c.Time);
|
||||||
|
Assert.Equal(105.0, c.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void V_Property_ReturnsTValueWithVolume()
|
||||||
|
{
|
||||||
|
long time = DateTime.UtcNow.Ticks;
|
||||||
|
var bar = new TBar(time, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
TValue v = bar.V;
|
||||||
|
|
||||||
|
Assert.Equal(time, v.Time);
|
||||||
|
Assert.Equal(1000.0, v.Value);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HL2_CalculatesCorrectly()
|
public void HL2_CalculatesCorrectly()
|
||||||
{
|
{
|
||||||
var bar = new TBar(0, 100, 110, 90, 105, 1000);
|
var bar = new TBar(0, 100, 110, 90, 105, 1000);
|
||||||
Assert.Equal(100.0, bar.HL2); // (110 + 90) / 2
|
Assert.Equal(100.0, bar.HL2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OC2_CalculatesCorrectly()
|
||||||
|
{
|
||||||
|
var bar = new TBar(0, 100, 110, 90, 104, 1000);
|
||||||
|
Assert.Equal(102.0, bar.OC2);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OHL3_CalculatesCorrectly()
|
public void OHL3_CalculatesCorrectly()
|
||||||
{
|
{
|
||||||
var bar = new TBar(0, 100, 110, 90, 105, 1000);
|
var bar = new TBar(0, 100, 110, 90, 105, 1000);
|
||||||
Assert.Equal(100.0, bar.OHL3); // (100 + 110 + 90) / 3
|
Assert.Equal(100.0, bar.OHL3);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HLC3_CalculatesCorrectly()
|
public void HLC3_CalculatesCorrectly()
|
||||||
{
|
{
|
||||||
var bar = new TBar(0, 100, 110, 90, 100, 1000);
|
var bar = new TBar(0, 100, 110, 90, 100, 1000);
|
||||||
Assert.Equal(100.0, bar.HLC3); // (110 + 90 + 100) / 3
|
Assert.Equal(100.0, bar.HLC3);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OHLC4_CalculatesCorrectly()
|
public void OHLC4_CalculatesCorrectly()
|
||||||
{
|
{
|
||||||
var bar = new TBar(0, 100, 110, 90, 100, 1000);
|
var bar = new TBar(0, 100, 110, 90, 100, 1000);
|
||||||
Assert.Equal(100.0, bar.OHLC4); // (100 + 110 + 90 + 100) / 4
|
Assert.Equal(100.0, bar.OHLC4);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HLCC4_CalculatesCorrectly()
|
public void HLCC4_CalculatesCorrectly()
|
||||||
{
|
{
|
||||||
var bar = new TBar(0, 100, 110, 90, 100, 1000);
|
var bar = new TBar(0, 100, 110, 90, 100, 1000);
|
||||||
Assert.Equal(100.0, bar.HLCC4); // (110 + 90 + 100 + 100) / 4
|
Assert.Equal(100.0, bar.HLCC4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitConversion_ToDouble_ReturnsClosePrice()
|
||||||
|
{
|
||||||
|
var bar = new TBar(0, 100, 110, 90, 105, 1000);
|
||||||
|
double closePrice = bar;
|
||||||
|
Assert.Equal(105.0, closePrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -72,5 +177,177 @@ namespace QuanTAlib.Tests
|
|||||||
Assert.Equal(time, tv.Time);
|
Assert.Equal(time, tv.Time);
|
||||||
Assert.Equal(105.0, tv.Value);
|
Assert.Equal(105.0, tv.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitConversion_ToDateTime_ReturnsCorrectDateTime()
|
||||||
|
{
|
||||||
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
var bar = new TBar(dateTime, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
DateTime result = bar;
|
||||||
|
|
||||||
|
Assert.Equal(dateTime, result);
|
||||||
|
Assert.Equal(DateTimeKind.Utc, result.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToString_ReturnsFormattedString()
|
||||||
|
{
|
||||||
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
var bar = new TBar(dateTime, 100.5, 110.25, 90.75, 105.0, 1000.0);
|
||||||
|
|
||||||
|
string result = bar.ToString();
|
||||||
|
|
||||||
|
Assert.Contains("2024-06-15", result);
|
||||||
|
Assert.Contains("10:30:00", result);
|
||||||
|
Assert.Contains("O=100.50", result);
|
||||||
|
Assert.Contains("H=110.25", result);
|
||||||
|
Assert.Contains("L=90.75", result);
|
||||||
|
Assert.Contains("C=105.00", result);
|
||||||
|
Assert.Contains("V=1000.00", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TBar_SameBars_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.True(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TBar_DifferentTime_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12346, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TBar_DifferentOpen_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 101, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TBar_DifferentHigh_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 111, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TBar_DifferentLow_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 110, 91, 105, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TBar_DifferentClose_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 110, 90, 106, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TBar_DifferentVolume_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 110, 90, 105, 1001);
|
||||||
|
|
||||||
|
Assert.False(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_Object_SameTBar_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
object bar2 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.True(bar1.Equals(bar2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_Object_DifferentType_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
object other = "not a TBar";
|
||||||
|
|
||||||
|
Assert.False(bar.Equals(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_Object_Null_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar.Equals(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetHashCode_SameBars_ReturnsSameHashCode()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.Equal(bar1.GetHashCode(), bar2.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetHashCode_DifferentBars_ReturnsDifferentHashCode()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12346, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.NotEqual(bar1.GetHashCode(), bar2.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EqualityOperator_SameBars_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.True(bar1 == bar2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EqualityOperator_DifferentBars_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12346, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar1 == bar2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InequalityOperator_SameBars_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.False(bar1 != bar2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InequalityOperator_DifferentBars_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var bar1 = new TBar(12345, 100, 110, 90, 105, 1000);
|
||||||
|
var bar2 = new TBar(12346, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
Assert.True(bar1 != bar2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using QuanTAlib;
|
using QuanTAlib;
|
||||||
|
|
||||||
@@ -6,6 +9,38 @@ namespace QuanTAlib.Tests
|
|||||||
{
|
{
|
||||||
public class TBarSeriesTests
|
public class TBarSeriesTests
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_Default_CreatesEmptySeries()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
|
||||||
|
Assert.Empty(series);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithCapacity_CreatesEmptySeries()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries(100);
|
||||||
|
|
||||||
|
Assert.Empty(series);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Name_DefaultValue_IsBar()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
|
||||||
|
Assert.Equal("Bar", series.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Name_CanBeSet()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries { Name = "TestBars" };
|
||||||
|
|
||||||
|
Assert.Equal("TestBars", series.Name);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Add_NewBar_IncreasesCount()
|
public void Add_NewBar_IncreasesCount()
|
||||||
{
|
{
|
||||||
@@ -34,6 +69,59 @@ namespace QuanTAlib.Tests
|
|||||||
Assert.Equal(112.0, series.Last.High);
|
Assert.Equal(112.0, series.Last.High);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_UpdateOnEmptySeries_AddsNewBar()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
var bar = new TBar(DateTime.UtcNow.Ticks, 100, 110, 90, 105, 1000);
|
||||||
|
|
||||||
|
series.Add(bar, isNew: false);
|
||||||
|
|
||||||
|
Assert.Single(series);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_WithLongTime_AddsBar()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
long time = DateTime.UtcNow.Ticks;
|
||||||
|
|
||||||
|
series.Add(time, 100, 110, 90, 105, 1000, isNew: true);
|
||||||
|
|
||||||
|
Assert.Single(series);
|
||||||
|
Assert.Equal(time, series.Last.Time);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_WithDateTime_AddsBar()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
var dt = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
series.Add(dt, 100, 110, 90, 105, 1000, isNew: true);
|
||||||
|
|
||||||
|
Assert.Single(series);
|
||||||
|
Assert.Equal(dt.Ticks, series.Last.Time);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_WithEnumerables_AddsMultipleBars()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
var times = new long[] { 100, 200, 300 };
|
||||||
|
var opens = new double[] { 10, 20, 30 };
|
||||||
|
var highs = new double[] { 15, 25, 35 };
|
||||||
|
var lows = new double[] { 5, 15, 25 };
|
||||||
|
var closes = new double[] { 12, 22, 32 };
|
||||||
|
var volumes = new double[] { 100, 200, 300 };
|
||||||
|
|
||||||
|
series.Add(times, opens, highs, lows, closes, volumes);
|
||||||
|
|
||||||
|
Assert.Equal(3, series.Count);
|
||||||
|
Assert.Equal(10, series[0].Open);
|
||||||
|
Assert.Equal(32, series[2].Close);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SubSeries_AreUpdated()
|
public void SubSeries_AreUpdated()
|
||||||
{
|
{
|
||||||
@@ -54,5 +142,277 @@ namespace QuanTAlib.Tests
|
|||||||
Assert.Equal(105.0, series.Close.Last.Value);
|
Assert.Equal(105.0, series.Close.Last.Value);
|
||||||
Assert.Equal(1000.0, series.Volume.Last.Value);
|
Assert.Equal(1000.0, series.Volume.Last.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubSeries_Aliases_Work()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
var bar = new TBar(DateTime.UtcNow.Ticks, 100, 110, 90, 105, 1000);
|
||||||
|
series.Add(bar, isNew: true);
|
||||||
|
|
||||||
|
Assert.Same(series.Open, series.O);
|
||||||
|
Assert.Same(series.High, series.H);
|
||||||
|
Assert.Same(series.Low, series.L);
|
||||||
|
Assert.Same(series.Close, series.C);
|
||||||
|
Assert.Same(series.Volume, series.V);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubSeries_HaveCorrectNames()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
|
||||||
|
Assert.Equal("Open", series.Open.Name);
|
||||||
|
Assert.Equal("High", series.High.Name);
|
||||||
|
Assert.Equal("Low", series.Low.Name);
|
||||||
|
Assert.Equal("Close", series.Close.Name);
|
||||||
|
Assert.Equal("Volume", series.Volume.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Last_EmptySeries_ReturnsDefault()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
|
||||||
|
var last = series.Last;
|
||||||
|
|
||||||
|
Assert.Equal(0, last.Time);
|
||||||
|
Assert.Equal(0.0, last.Open);
|
||||||
|
Assert.Equal(0.0, last.Close);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Last_NonEmptySeries_ReturnsLastBar()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
var last = series.Last;
|
||||||
|
|
||||||
|
Assert.Equal(200, last.Time);
|
||||||
|
Assert.Equal(22.0, last.Close);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastTime_EmptySeries_ReturnsZero()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
Assert.Equal(0, series.LastTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastTime_NonEmptySeries_ReturnsLastTime()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
Assert.Equal(200, series.LastTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastOpen_EmptySeries_ReturnsNaN()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
Assert.True(double.IsNaN(series.LastOpen));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastOpen_NonEmptySeries_ReturnsLastOpen()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
Assert.Equal(20.0, series.LastOpen);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastHigh_EmptySeries_ReturnsNaN()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
Assert.True(double.IsNaN(series.LastHigh));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastHigh_NonEmptySeries_ReturnsLastHigh()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
Assert.Equal(25.0, series.LastHigh);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastLow_EmptySeries_ReturnsNaN()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
Assert.True(double.IsNaN(series.LastLow));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastLow_NonEmptySeries_ReturnsLastLow()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
Assert.Equal(15.0, series.LastLow);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastClose_EmptySeries_ReturnsNaN()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
Assert.True(double.IsNaN(series.LastClose));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastClose_NonEmptySeries_ReturnsLastClose()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
Assert.Equal(22.0, series.LastClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastVolume_EmptySeries_ReturnsNaN()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
Assert.True(double.IsNaN(series.LastVolume));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastVolume_NonEmptySeries_ReturnsLastVolume()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
Assert.Equal(200.0, series.LastVolume);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Indexer_ReturnsCorrectBar()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
series.Add(300, 30, 35, 25, 32, 300);
|
||||||
|
|
||||||
|
Assert.Equal(100, series[0].Time);
|
||||||
|
Assert.Equal(10.0, series[0].Open);
|
||||||
|
Assert.Equal(200, series[1].Time);
|
||||||
|
Assert.Equal(22.0, series[1].Close);
|
||||||
|
Assert.Equal(300, series[2].Time);
|
||||||
|
Assert.Equal(32.0, series[2].Close);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Count_ReturnsCorrectValue()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
|
||||||
|
Assert.Empty(series);
|
||||||
|
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
Assert.Single(series);
|
||||||
|
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
Assert.Equal(2, series.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetEnumerator_IteratesAllBars()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
series.Add(300, 30, 35, 25, 32, 300);
|
||||||
|
|
||||||
|
var list = series.ToList();
|
||||||
|
|
||||||
|
Assert.Equal(3, list.Count);
|
||||||
|
Assert.Equal(10.0, list[0].Open);
|
||||||
|
Assert.Equal(22.0, list[1].Close);
|
||||||
|
Assert.Equal(32.0, list[2].Close);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetEnumerator_NonGeneric_Works()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
IEnumerable enumerable = series;
|
||||||
|
var list = new List<object>();
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
list.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(2, list.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Pub_Event_IsRaisedOnAdd()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
TBar? received = null;
|
||||||
|
series.Pub += bar => received = bar;
|
||||||
|
|
||||||
|
var barToAdd = new TBar(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(barToAdd, isNew: true);
|
||||||
|
|
||||||
|
Assert.NotNull(received);
|
||||||
|
Assert.Equal(100, received.Value.Time);
|
||||||
|
Assert.Equal(12.0, received.Value.Close);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Pub_Event_IsRaisedOnUpdate()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
TBar? received = null;
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Pub += bar => received = bar;
|
||||||
|
|
||||||
|
series.Add(100, 10, 18, 5, 15, 150, isNew: false);
|
||||||
|
|
||||||
|
Assert.NotNull(received);
|
||||||
|
Assert.Equal(15.0, received.Value.Close);
|
||||||
|
Assert.Equal(18.0, received.Value.High);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SubSeries_ShareSameTimeArray()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
|
||||||
|
Assert.Equal(series.Open.Times[0], series.Close.Times[0]);
|
||||||
|
Assert.Equal(series.High.Times[1], series.Volume.Times[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_MultipleBars_MaintainsOrder()
|
||||||
|
{
|
||||||
|
var series = new TBarSeries();
|
||||||
|
|
||||||
|
series.Add(100, 10, 15, 5, 12, 100);
|
||||||
|
series.Add(200, 20, 25, 15, 22, 200);
|
||||||
|
series.Add(300, 30, 35, 25, 32, 300);
|
||||||
|
|
||||||
|
Assert.Equal(3, series.Count);
|
||||||
|
Assert.Equal(100, series[0].Time);
|
||||||
|
Assert.Equal(200, series[1].Time);
|
||||||
|
Assert.Equal(300, series[2].Time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using QuanTAlib;
|
using QuanTAlib;
|
||||||
|
|
||||||
@@ -6,6 +9,52 @@ namespace QuanTAlib.Tests
|
|||||||
{
|
{
|
||||||
public class TSeriesTests
|
public class TSeriesTests
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_Default_CreatesEmptySeries()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
|
||||||
|
Assert.Empty(series);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithCapacity_CreatesEmptySeries()
|
||||||
|
{
|
||||||
|
var series = new TSeries(100);
|
||||||
|
|
||||||
|
Assert.Empty(series);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithLists_WrapsExistingData()
|
||||||
|
{
|
||||||
|
var times = new List<long> { 100, 200, 300 };
|
||||||
|
var values = new List<double> { 1.0, 2.0, 3.0 };
|
||||||
|
|
||||||
|
var series = new TSeries(times, values);
|
||||||
|
|
||||||
|
Assert.Equal(3, series.Count);
|
||||||
|
Assert.Equal(1.0, series[0].Value);
|
||||||
|
Assert.Equal(2.0, series[1].Value);
|
||||||
|
Assert.Equal(3.0, series[2].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Name_DefaultValue_IsData()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
|
||||||
|
Assert.Equal("Data", series.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Name_CanBeSet()
|
||||||
|
{
|
||||||
|
var series = new TSeries { Name = "TestSeries" };
|
||||||
|
|
||||||
|
Assert.Equal("TestSeries", series.Name);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Add_NewValue_IncreasesCount()
|
public void Add_NewValue_IncreasesCount()
|
||||||
{
|
{
|
||||||
@@ -45,5 +94,252 @@ namespace QuanTAlib.Tests
|
|||||||
Assert.Equal(10.0, series[0].Value);
|
Assert.Equal(10.0, series[0].Value);
|
||||||
Assert.Equal(20.0, series[1].Value);
|
Assert.Equal(20.0, series[1].Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_TValue_AddsNewItem()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
var tv = new TValue(DateTime.UtcNow.Ticks, 42.0);
|
||||||
|
|
||||||
|
series.Add(tv);
|
||||||
|
|
||||||
|
Assert.Single(series);
|
||||||
|
Assert.Equal(42.0, series.Last.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_TValueWithIsNew_AddsOrUpdates()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
var tv1 = new TValue(DateTime.UtcNow.Ticks, 42.0);
|
||||||
|
var tv2 = new TValue(DateTime.UtcNow.Ticks, 43.0);
|
||||||
|
|
||||||
|
series.Add(tv1, isNew: true);
|
||||||
|
series.Add(tv2, isNew: false);
|
||||||
|
|
||||||
|
Assert.Single(series);
|
||||||
|
Assert.Equal(43.0, series.Last.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_WithDateTime_AddsValue()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
var dt = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
series.Add(dt, 100.0, isNew: true);
|
||||||
|
|
||||||
|
Assert.Single(series);
|
||||||
|
Assert.Equal(dt.Ticks, series.Last.Time);
|
||||||
|
Assert.Equal(100.0, series.Last.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_EnumerableDoubles_AddsAllValues()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
var values = new[] { 1.0, 2.0, 3.0, 4.0, 5.0 };
|
||||||
|
|
||||||
|
series.Add(values);
|
||||||
|
|
||||||
|
Assert.Equal(5, series.Count);
|
||||||
|
Assert.Equal(1.0, series[0].Value);
|
||||||
|
Assert.Equal(5.0, series[4].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Add_UpdateOnEmptySeries_AddsNewItem()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
var tv = new TValue(DateTime.UtcNow.Ticks, 42.0);
|
||||||
|
|
||||||
|
series.Add(tv, isNew: false);
|
||||||
|
|
||||||
|
Assert.Single(series);
|
||||||
|
Assert.Equal(42.0, series.Last.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Last_EmptySeries_ReturnsDefault()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
|
||||||
|
var last = series.Last;
|
||||||
|
|
||||||
|
Assert.Equal(0, last.Time);
|
||||||
|
Assert.Equal(0.0, last.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Last_NonEmptySeries_ReturnsLastValue()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 10.0);
|
||||||
|
series.Add(200, 20.0);
|
||||||
|
|
||||||
|
var last = series.Last;
|
||||||
|
|
||||||
|
Assert.Equal(200, last.Time);
|
||||||
|
Assert.Equal(20.0, last.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastValue_EmptySeries_ReturnsNaN()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
|
||||||
|
Assert.True(double.IsNaN(series.LastValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastValue_NonEmptySeries_ReturnsLastValue()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 10.0);
|
||||||
|
series.Add(200, 20.0);
|
||||||
|
|
||||||
|
Assert.Equal(20.0, series.LastValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastTime_EmptySeries_ReturnsZero()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
|
||||||
|
Assert.Equal(0, series.LastTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LastTime_NonEmptySeries_ReturnsLastTime()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 10.0);
|
||||||
|
series.Add(200, 20.0);
|
||||||
|
|
||||||
|
Assert.Equal(200, series.LastTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Values_ReturnsReadOnlySpanOfValues()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 1.0);
|
||||||
|
series.Add(200, 2.0);
|
||||||
|
series.Add(300, 3.0);
|
||||||
|
|
||||||
|
ReadOnlySpan<double> values = series.Values;
|
||||||
|
|
||||||
|
Assert.Equal(3, values.Length);
|
||||||
|
Assert.Equal(1.0, values[0]);
|
||||||
|
Assert.Equal(2.0, values[1]);
|
||||||
|
Assert.Equal(3.0, values[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Times_ReturnsReadOnlySpanOfTimes()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 1.0);
|
||||||
|
series.Add(200, 2.0);
|
||||||
|
series.Add(300, 3.0);
|
||||||
|
|
||||||
|
ReadOnlySpan<long> times = series.Times;
|
||||||
|
|
||||||
|
Assert.Equal(3, times.Length);
|
||||||
|
Assert.Equal(100, times[0]);
|
||||||
|
Assert.Equal(200, times[1]);
|
||||||
|
Assert.Equal(300, times[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Indexer_ReturnsCorrectTValue()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 10.0);
|
||||||
|
series.Add(200, 20.0);
|
||||||
|
series.Add(300, 30.0);
|
||||||
|
|
||||||
|
Assert.Equal(100, series[0].Time);
|
||||||
|
Assert.Equal(10.0, series[0].Value);
|
||||||
|
Assert.Equal(200, series[1].Time);
|
||||||
|
Assert.Equal(20.0, series[1].Value);
|
||||||
|
Assert.Equal(300, series[2].Time);
|
||||||
|
Assert.Equal(30.0, series[2].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetEnumerator_IteratesAllValues()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 1.0);
|
||||||
|
series.Add(200, 2.0);
|
||||||
|
series.Add(300, 3.0);
|
||||||
|
|
||||||
|
var list = series.ToList();
|
||||||
|
|
||||||
|
Assert.Equal(3, list.Count);
|
||||||
|
Assert.Equal(1.0, list[0].Value);
|
||||||
|
Assert.Equal(2.0, list[1].Value);
|
||||||
|
Assert.Equal(3.0, list[2].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetEnumerator_NonGeneric_Works()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
series.Add(100, 1.0);
|
||||||
|
series.Add(200, 2.0);
|
||||||
|
|
||||||
|
IEnumerable enumerable = series;
|
||||||
|
var list = new List<object>();
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
list.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(2, list.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Pub_Event_IsRaisedOnAdd()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
TValue? received = null;
|
||||||
|
series.Pub += tv => received = tv;
|
||||||
|
|
||||||
|
series.Add(100, 42.0);
|
||||||
|
|
||||||
|
Assert.NotNull(received);
|
||||||
|
Assert.Equal(100, received.Value.Time);
|
||||||
|
Assert.Equal(42.0, received.Value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Pub_Event_IsRaisedOnUpdate()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
TValue? received = null;
|
||||||
|
series.Add(100, 42.0);
|
||||||
|
series.Pub += tv => received = tv;
|
||||||
|
|
||||||
|
series.Add(100, 43.0, isNew: false);
|
||||||
|
|
||||||
|
Assert.NotNull(received);
|
||||||
|
Assert.Equal(43.0, received.Value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Count_ReturnsCorrectValue()
|
||||||
|
{
|
||||||
|
var series = new TSeries();
|
||||||
|
|
||||||
|
Assert.Empty(series);
|
||||||
|
|
||||||
|
series.Add(100, 1.0);
|
||||||
|
Assert.Single(series);
|
||||||
|
|
||||||
|
series.Add(200, 2.0);
|
||||||
|
Assert.Equal(2, series.Count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace QuanTAlib.Tests
|
|||||||
public class TValueTests
|
public class TValueTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Constructor_SetsPropertiesCorrectly()
|
public void Constructor_WithLongTime_SetsPropertiesCorrectly()
|
||||||
{
|
{
|
||||||
long time = DateTime.UtcNow.Ticks;
|
long time = DateTime.UtcNow.Ticks;
|
||||||
double value = 123.45;
|
double value = 123.45;
|
||||||
@@ -18,34 +18,168 @@ namespace QuanTAlib.Tests
|
|||||||
Assert.Equal(value, tValue.Value);
|
Assert.Equal(value, tValue.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithDateTime_SetsPropertiesCorrectly()
|
||||||
|
{
|
||||||
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
double value = 123.45;
|
||||||
|
|
||||||
|
var tValue = new TValue(dateTime, value);
|
||||||
|
|
||||||
|
Assert.Equal(dateTime.Ticks, tValue.Time);
|
||||||
|
Assert.Equal(value, tValue.Value);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AsDateTime_ReturnsCorrectDateTime()
|
public void AsDateTime_ReturnsCorrectDateTime()
|
||||||
{
|
{
|
||||||
DateTime dt = new DateTime(2023, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
var dt = new DateTime(2023, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
||||||
long ticks = dt.Ticks;
|
var tValue = new TValue(dt.Ticks, 100.0);
|
||||||
var tValue = new TValue(ticks, 100.0);
|
|
||||||
|
|
||||||
Assert.Equal(dt, tValue.AsDateTime);
|
Assert.Equal(dt, tValue.AsDateTime);
|
||||||
|
Assert.Equal(DateTimeKind.Utc, tValue.AsDateTime.Kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ToString_FormatsCorrectly()
|
public void ToString_FormatsCorrectly()
|
||||||
{
|
{
|
||||||
DateTime dt = new DateTime(2023, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
var dt = new DateTime(2023, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
||||||
var tValue = new TValue(dt.Ticks, 123.456);
|
var tValue = new TValue(dt.Ticks, 123.456);
|
||||||
|
|
||||||
string result = tValue.ToString();
|
string result = tValue.ToString();
|
||||||
|
|
||||||
Assert.Contains(dt.ToString("yyyy-MM-dd HH:mm:ss"), result);
|
Assert.Contains("2023-01-01", result);
|
||||||
Assert.Contains("123.46", result); // Default formatting usually 2 decimals or similar
|
Assert.Contains("12:00:00", result);
|
||||||
|
Assert.Contains("123.46", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ImplicitConversion_ToDouble()
|
public void ImplicitConversion_ToDouble_ReturnsValue()
|
||||||
{
|
{
|
||||||
var tValue = new TValue(DateTime.UtcNow.Ticks, 42.0);
|
var tValue = new TValue(DateTime.UtcNow.Ticks, 42.0);
|
||||||
|
|
||||||
double val = tValue;
|
double val = tValue;
|
||||||
|
|
||||||
Assert.Equal(42.0, val);
|
Assert.Equal(42.0, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ImplicitConversion_ToDateTime_ReturnsCorrectDateTime()
|
||||||
|
{
|
||||||
|
var dateTime = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
|
||||||
|
var tValue = new TValue(dateTime.Ticks, 100.0);
|
||||||
|
|
||||||
|
DateTime result = tValue;
|
||||||
|
|
||||||
|
Assert.Equal(dateTime, result);
|
||||||
|
Assert.Equal(DateTimeKind.Utc, result.Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TValue_SameValues_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12345, 100.0);
|
||||||
|
|
||||||
|
Assert.True(tv1.Equals(tv2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TValue_DifferentTime_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12346, 100.0);
|
||||||
|
|
||||||
|
Assert.False(tv1.Equals(tv2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_TValue_DifferentValue_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12345, 101.0);
|
||||||
|
|
||||||
|
Assert.False(tv1.Equals(tv2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_Object_SameTValue_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
object tv2 = new TValue(12345, 100.0);
|
||||||
|
|
||||||
|
Assert.True(tv1.Equals(tv2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_Object_DifferentType_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var tv = new TValue(12345, 100.0);
|
||||||
|
object other = "not a TValue";
|
||||||
|
|
||||||
|
Assert.False(tv.Equals(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Equals_Object_Null_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var tv = new TValue(12345, 100.0);
|
||||||
|
|
||||||
|
Assert.False(tv.Equals(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetHashCode_SameValues_ReturnsSameHashCode()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12345, 100.0);
|
||||||
|
|
||||||
|
Assert.Equal(tv1.GetHashCode(), tv2.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetHashCode_DifferentValues_ReturnsDifferentHashCode()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12346, 100.0);
|
||||||
|
|
||||||
|
Assert.NotEqual(tv1.GetHashCode(), tv2.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EqualityOperator_SameValues_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12345, 100.0);
|
||||||
|
|
||||||
|
Assert.True(tv1 == tv2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EqualityOperator_DifferentValues_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12346, 100.0);
|
||||||
|
|
||||||
|
Assert.False(tv1 == tv2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InequalityOperator_SameValues_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12345, 100.0);
|
||||||
|
|
||||||
|
Assert.False(tv1 != tv2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InequalityOperator_DifferentValues_ReturnsTrue()
|
||||||
|
{
|
||||||
|
var tv1 = new TValue(12345, 100.0);
|
||||||
|
var tv2 = new TValue(12346, 100.0);
|
||||||
|
|
||||||
|
Assert.True(tv1 != tv2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||||
<Title>QuanTAlib</Title>
|
<Title>QuanTAlib</Title>
|
||||||
<Product>Library of TA Calculations, Charts and Strategies for Quantower</Product>
|
<Product>Library of TA Calculations, Charts and Strategies for Quantower</Product>
|
||||||
<Description>Quantitative Technical Analysis Library in C# for Quantower</Description>
|
<Description>Quantitative Technical Analysis Library in C# for Quantower</Description>
|
||||||
@@ -32,6 +32,10 @@
|
|||||||
<UserSecretsId>6afc11a7-4355-4f5e-9fdf-22431e5b03cb</UserSecretsId>
|
<UserSecretsId>6afc11a7-4355-4f5e-9fdf-22431e5b03cb</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="QuanTAlib.Tests" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="**\*.cs" Exclude="**\*.Tests.cs;**\*.Quantower.cs;obj\**\*.cs" />
|
<Compile Include="**\*.cs" Exclude="**\*.Tests.cs;**\*.Quantower.cs;obj\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user