diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 31f09837..5cef9f7c 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -46,6 +46,10 @@
+
+
+
+
diff --git a/Tests/UpdateTestBase.cs b/Tests/UpdateTestBase.cs
new file mode 100644
index 00000000..7a245277
--- /dev/null
+++ b/Tests/UpdateTestBase.cs
@@ -0,0 +1,91 @@
+using Xunit;
+using System.Security.Cryptography;
+
+namespace QuanTAlib.Tests;
+
+public abstract class UpdateTestBase
+{
+ protected readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
+ protected const int RandomUpdates = 100;
+ protected const double ReferenceValue = 100.0;
+ protected const int precision = 8;
+
+ protected double GetRandomDouble()
+ {
+ byte[] bytes = new byte[8];
+ rng.GetBytes(bytes);
+ return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100
+ }
+
+ protected TBar GetRandomBar(bool IsNew)
+ {
+ double open = GetRandomDouble();
+ double high = open + Math.Abs(GetRandomDouble());
+ double low = open - Math.Abs(GetRandomDouble());
+ double close = low + ((high - low) * GetRandomDouble());
+ return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
+ }
+
+ protected void TestTValueUpdate(T indicator, Func calc) where T : class
+ {
+ var initialValue = calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
+ }
+ var finalValue = calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+
+ protected void TestTBarUpdate(T indicator, Func calc) where T : class
+ {
+ TBar r = GetRandomBar(true);
+ var initialValue = calc(r);
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(GetRandomBar(IsNew: false));
+ }
+ var finalValue = calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+
+ protected void TestDualTValueUpdate(T indicator, Func calc) where T : class
+ {
+ var initialValue = calc(
+ new TValue(DateTime.Now, ReferenceValue, IsNew: true),
+ new TValue(DateTime.Now, ReferenceValue, IsNew: true));
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(
+ new TValue(DateTime.Now, GetRandomDouble(), IsNew: false),
+ new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
+ }
+ var finalValue = calc(
+ new TValue(DateTime.Now, ReferenceValue, IsNew: false),
+ new TValue(DateTime.Now, ReferenceValue, IsNew: false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+
+ protected void TestDualTBarUpdate(T indicator, Func calc) where T : class
+ {
+ TBar bar1 = GetRandomBar(true);
+ TBar bar2 = GetRandomBar(true);
+ var initialValue = calc(bar1, bar2);
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(GetRandomBar(false), GetRandomBar(false));
+ }
+ var finalValue = calc(
+ new TBar(bar1.Time, bar1.Open, bar1.High, bar1.Low, bar1.Close, bar1.Volume, false),
+ new TBar(bar2.Time, bar2.Open, bar2.High, bar2.Low, bar2.Close, bar2.Volume, false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+}
diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs
index b0689b4d..5e4b1665 100644
--- a/Tests/test_eventing.cs
+++ b/Tests/test_eventing.cs
@@ -1,172 +1,112 @@
using Xunit;
using System.Security.Cryptography;
+using System.Reflection;
-#pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781
-
-namespace QuanTAlib;
+namespace QuanTAlib.Tests;
public class EventingTests
{
- [Fact]
- public void EventBasedCalculations()
+ private const int TestDataPoints = 200;
+ private const int DefaultPeriod = 10;
+ private const double Tolerance = 1e-9;
+
+ private static readonly (string Name, object[] DirectParams, object[] EventParams)[] ValueIndicators = new[]
{
- // Create a cryptographically secure random number generator
- using var rng = RandomNumberGenerator.Create();
+ ("Afirma", new object[] { DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }, new object[] { new TSeries(), DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }),
+ ("Alma", new object[] { DefaultPeriod, 0.85, 6.0 }, new object[] { new TSeries(), DefaultPeriod, 0.85, 6.0 }),
+ ("Convolution", new object[] { new double[] {1,2,3,2,1} }, new object[] { new TSeries(), new double[] {1,2,3,2,1} }),
+ ("Dema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Dsma", new object[] { DefaultPeriod, 0.9 }, new object[] { new TSeries(), DefaultPeriod, 0.9 }),
+ ("Dwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Ema", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }),
+ ("Epma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Pwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Frama", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Fwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Gma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Hma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Htit", new object[] { }, new object[] { new TSeries() }),
+ ("Hwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Jma", new object[] { DefaultPeriod, 0, 0.45, 10 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.45, 10 }),
+ ("Kama", new object[] { DefaultPeriod, 2, 30 }, new object[] { new TSeries(), DefaultPeriod, 2, 30 }),
+ ("Ltma", new object[] { 0.2 }, new object[] { new TSeries(), 0.2 }),
+ ("Maaf", new object[] { 39, 0.002 }, new object[] { new TSeries(), 39, 0.002 }),
+ ("Mama", new object[] { 0.5, 0.05 }, new object[] { new TSeries(), 0.5, 0.05 }),
+ ("Mgdi", new object[] { DefaultPeriod, 0.6 }, new object[] { new TSeries(), DefaultPeriod, 0.6 }),
+ ("Mma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Qema", new object[] { 0.2, 0.2, 0.2, 0.2 }, new object[] { new TSeries(), 0.2, 0.2, 0.2, 0.2 }),
+ ("Rema", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }),
+ ("Rma", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }),
+ ("Sma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Wma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Tema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Zlema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Sinema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Smma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("T3", new object[] { DefaultPeriod, 0.7, true }, new object[] { new TSeries(), DefaultPeriod, 0.7, true }),
+ ("Trima", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Vidya", new object[] { DefaultPeriod, 0, 0.2 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.2 }),
+ ("Apo", new object[] { 12, 26 }, new object[] { new TSeries(), 12, 26 }),
+ ("Macd", new object[] { 12, 26, 9 }, new object[] { new TSeries(), 12, 26, 9 }),
+ ("Rsi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rsx", new object[] { DefaultPeriod, 0, 0.55 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.55 }),
+ ("Cmo", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Cog", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Curvature", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Entropy", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Kurtosis", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Max", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }),
+ ("Median", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Min", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }),
+ ("Mode", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Percentile", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }),
+ ("Skew", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Slope", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Stddev", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Variance", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Zscore", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Beta", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Corr", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Hv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Jvolty", new object[] { DefaultPeriod, 0 }, new object[] { new TSeries(), DefaultPeriod, 0 }),
+ ("Rv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Rvi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mapd", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mase", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mda", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Me", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mpe", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Msle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rmse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rmsle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Smape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rsquared", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Huber", new object[] { DefaultPeriod, 1.0 }, new object[] { new TSeries(), DefaultPeriod, 1.0 })
+ };
- // Create input series to hold our random values
- var input = new TSeries();
- var barInput = new TBarSeries();
- int p = 10;
+ private static readonly (string Name, object[] DirectParams, object[] EventParams)[] BarIndicators = new[]
+ {
+ ("Adl", new object[] { }, new object[] { new TBarSeries() }),
+ ("Adosc", new object[] { 3, 10 }, new object[] { new TBarSeries(), 3, 10 }),
+ ("Aobv", new object[] { }, new object[] { new TBarSeries() }),
+ ("Cmf", new object[] { 20 }, new object[] { new TBarSeries(), 20 }),
+ ("Eom", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
+ ("Kvo", new object[] { 34, 55 }, new object[] { new TBarSeries(), 34, 55 }),
+ ("Atr", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
+ ("Chop", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
+ ("Dosc", new object[] { }, new object[] { new TBarSeries() })
+ };
- // Create a list of value-based indicator pairs
- var valueIndicators = new List<(string Name, AbstractBase Direct, AbstractBase EventBased)>
- {
- ("Afirma", new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)),
- ("Alma", new Alma(p), new Alma(input, p)),
- ("Convolution", new Convolution(new double[] {1,2,3,2,1}), new Convolution(input, new double[] {1,2,3,2,1})),
- ("Dema", new Dema(p), new Dema(input, p)),
- ("Dsma", new Dsma(p), new Dsma(input, p)),
- ("Dwma", new Dwma(p), new Dwma(input, p)),
- ("Ema", new Ema(p), new Ema(input, p)),
- ("Epma", new Epma(p), new Epma(input, p)),
- ("Pwma", new Pwma(p), new Pwma(input, p)),
- ("Frama", new Frama(p), new Frama(input, p)),
- ("Fwma", new Fwma(p), new Fwma(input, p)),
- ("Gma", new Gma(p), new Gma(input, p)),
- ("Hma", new Hma(p), new Hma(input, p)),
- ("Htit", new Htit(), new Htit(input)),
- ("Hwma", new Hwma(p), new Hwma(input, p)),
- ("Jma", new Jma(p), new Jma(input, p)),
- ("Kama", new Kama(p), new Kama(input, p)),
- ("Ltma", new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)),
- ("Maaf", new Maaf(p), new Maaf(input, p)),
- ("Mama", new Mama(p), new Mama(input, p)),
- ("Mgdi", new Mgdi(p, kFactor: 0.6), new Mgdi(input, p, kFactor: 0.6)),
- ("Mma", new Mma(p), new Mma(input, p)),
- ("Qema", new Qema(k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2), new Qema(input, k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2)),
- ("Rema", new Rema(p), new Rema(input, p)),
- ("Rma", new Rma(p), new Rma(input, p)),
- ("Sma", new Sma(p), new Sma(input, p)),
- ("Wma", new Wma(p), new Wma(input, p)),
- ("Rma", new Rma(p), new Rma(input, p)),
- ("Tema", new Tema(p), new Tema(input, p)),
- ("Kama", new Kama(2, 30, 6), new Kama(input, 2, 30, 6)),
- ("Zlema", new Zlema(p), new Zlema(input, p)),
- ("Sinema", new Sinema(p), new Sinema(input, p)),
- ("Smma", new Smma(p), new Smma(input, p)),
- ("T3", new T3(p), new T3(input, p)),
- ("Trima", new Trima(p), new Trima(input, p)),
- ("Vidya", new Vidya(p), new Vidya(input, p)),
- ("Apo", new Apo(12, 26), new Apo(input, 12, 26)),
- ("Macd", new Macd(12, 26, 9), new Macd(input, 12, 26, 9)),
- ("Rsi", new Rsi(p), new Rsi(input, p)),
- ("Rsx", new Rsx(p), new Rsx(input, p)),
- ("Cmo", new Cmo(p), new Cmo(input, p)),
- ("Cog", new Cog(p), new Cog(input, p)),
- ("Curvature", new Curvature(p), new Curvature(input, p)),
- ("Entropy", new Entropy(p), new Entropy(input, p)),
- ("Kurtosis", new Kurtosis(p), new Kurtosis(input, p)),
- ("Max", new Max(p), new Max(input, p)),
- ("Median", new Median(p), new Median(input, p)),
- ("Min", new Min(p), new Min(input, p)),
- ("Mode", new Mode(p), new Mode(input, p)),
- ("Percentile", new Percentile(p, 0.5), new Percentile(input, p, 0.5)),
- ("Skew", new Skew(p), new Skew(input, p)),
- ("Slope", new Slope(p), new Slope(input, p)),
- ("Stddev", new Stddev(p), new Stddev(input, p)),
- ("Variance", new Variance(p), new Variance(input, p)),
- ("Zscore", new Zscore(p), new Zscore(input, p)),
- ("Beta", new Beta(p), new Beta(input, p)),
- ("Corr", new Corr(p), new Corr(input, p)),
- // Volatility indicators (value-based)
- ("Hv", new Hv(p), new Hv(input, p)),
- ("Jvolty", new Jvolty(p), new Jvolty(input, p)),
- ("Rv", new Rv(p), new Rv(input, p)),
- ("Rvi", new Rvi(p), new Rvi(input, p)),
- // Error classes
- ("Mae", new Mae(p), new Mae(input, p)),
- ("Mapd", new Mapd(p), new Mapd(input, p)),
- ("Mape", new Mape(p), new Mape(input, p)),
- ("Mase", new Mase(p), new Mase(input, p)),
- ("Mda", new Mda(p), new Mda(input, p)),
- ("Me", new Me(p), new Me(input, p)),
- ("Mpe", new Mpe(p), new Mpe(input, p)),
- ("Mse", new Mse(p), new Mse(input, p)),
- ("Msle", new Msle(p), new Msle(input, p)),
- ("Rae", new Rae(p), new Rae(input, p)),
- ("Rmse", new Rmse(p), new Rmse(input, p)),
- ("Rmsle", new Rmsle(p), new Rmsle(input, p)),
- ("Rse", new Rse(p), new Rse(input, p)),
- ("Smape", new Smape(p), new Smape(input, p)),
- ("Rsquared", new Rsquared(p), new Rsquared(input, p)),
- ("Huber", new Huber(p), new Huber(input, p))
- };
+ public static IEnumerable