From 69aef2b127f8330798513cec71c778aaf3a36b91 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Thu, 7 Nov 2024 09:55:19 -0800 Subject: [PATCH] Dpo chart + refactored tests --- Tests/Tests.csproj | 4 + Tests/UpdateTestBase.cs | 91 ++++++++ Tests/test_eventing.cs | 323 +++++++++++++------------- Tests/test_quantower.cs | 55 ++++- Tests/test_updates_oscillators.cs | 246 +++----------------- Tests/test_updates_statistics.cs | 198 +++------------- Tests/test_updates_volatility.cs | 355 +++-------------------------- quantower/Momentum/DpoIndicator.cs | 8 +- 8 files changed, 416 insertions(+), 864 deletions(-) create mode 100644 Tests/UpdateTestBase.cs 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 GetValueIndicatorData() + => ValueIndicators.Select(x => new object[] { x.Name, x.DirectParams, x.EventParams }); - // Create a list of bar-based indicator pairs - var barIndicators = new List<(string Name, AbstractBase Direct, AbstractBase EventBased)> - { - // Volume indicators - ("Adl", new Adl(), new Adl(barInput)), - ("Adosc", new Adosc(3, 10), new Adosc(barInput, 3, 10)), - ("Aobv", new Aobv(), new Aobv(barInput)), - ("Cmf", new Cmf(20), new Cmf(barInput, 20)), - ("Eom", new Eom(14), new Eom(barInput, 14)), - ("Kvo", new Kvo(34, 55), new Kvo(barInput, 34, 55)), - // Volatility indicators (bar-based) - ("Atr", new Atr(14), new Atr(barInput, 14)), - // Oscillators (bar-based) - ("Chop", new Chop(14), new Chop(barInput, 14)), - ("Dosc", new Dosc(), new Dosc(barInput)) - }; - - // Generate 200 random values and feed them to indicators - for (int i = 0; i < 200; i++) - { - // Generate random value for value-based indicators - double randomValue = GetRandomDouble(rng) * 100; - input.Add(randomValue); - - // Calculate value-based indicators - foreach (var (_, direct, _) in valueIndicators) - { - direct.Calc(randomValue); - } - - // Generate random bar for bar-based indicators - var bar = new TBar( - DateTime.Now, - randomValue, - randomValue + Math.Abs(GetRandomDouble(rng) * 10), - randomValue - Math.Abs(GetRandomDouble(rng) * 10), - randomValue + (GetRandomDouble(rng) * 5), - Math.Abs(GetRandomDouble(rng) * 1000), - true - ); - barInput.Add(bar); - - // Calculate bar-based indicators - foreach (var (_, direct, _) in barIndicators) - { - direct.Calc(bar); - } - } - - // Compare the results for value-based indicators - foreach (var (name, direct, eventBased) in valueIndicators) - { - bool areEqual = (double.IsNaN(direct.Value) && double.IsNaN(eventBased.Value)) || - Math.Abs(direct.Value - eventBased.Value) < 1e-9; - Assert.True(areEqual, $"Value indicator {name} failed: Expected {direct.Value}, Actual {eventBased.Value}"); - } - - // Compare the results for bar-based indicators - foreach (var (name, direct, eventBased) in barIndicators) - { - bool areEqual = (double.IsNaN(direct.Value) && double.IsNaN(eventBased.Value)) || - Math.Abs(direct.Value - eventBased.Value) < 1e-9; - Assert.True(areEqual, $"Bar indicator {name} failed: Expected {direct.Value}, Actual {eventBased.Value}"); - } - } + public static IEnumerable GetBarIndicatorData() + => BarIndicators.Select(x => new object[] { x.Name, x.DirectParams, x.EventParams }); private static double GetRandomDouble(RandomNumberGenerator rng) { @@ -174,4 +114,69 @@ public class EventingTests rng.GetBytes(bytes); return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue; } + + private static TBar GenerateRandomBar(RandomNumberGenerator rng, double baseValue) + { + return new TBar( + DateTime.Now, + baseValue, + baseValue + Math.Abs(GetRandomDouble(rng) * 10), + baseValue - Math.Abs(GetRandomDouble(rng) * 10), + baseValue + (GetRandomDouble(rng) * 5), + Math.Abs(GetRandomDouble(rng) * 1000), + true + ); + } + + [Theory] + [MemberData(nameof(GetValueIndicatorData))] + public void ValueIndicatorEventTest(string indicatorName, object[] directParams, object[] eventParams) + { + using var rng = RandomNumberGenerator.Create(); + var input = (TSeries)eventParams[0]; + + // Create indicator instances using reflection + var indicatorType = Type.GetType($"QuanTAlib.{indicatorName}, QuanTAlib")!; + var directIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, directParams)!; + var eventIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, eventParams)!; + + // Generate test data and calculate + for (int i = 0; i < TestDataPoints; i++) + { + double randomValue = GetRandomDouble(rng) * 100; + input.Add(randomValue); + directIndicator.Calc(randomValue); + } + + bool areEqual = (double.IsNaN(directIndicator.Value) && double.IsNaN(eventIndicator.Value)) || + Math.Abs(directIndicator.Value - eventIndicator.Value) < Tolerance; + + Assert.True(areEqual, $"Value indicator {indicatorName} failed: Expected {directIndicator.Value}, Actual {eventIndicator.Value}"); + } + + [Theory] + [MemberData(nameof(GetBarIndicatorData))] + public void BarIndicatorEventTest(string indicatorName, object[] directParams, object[] eventParams) + { + using var rng = RandomNumberGenerator.Create(); + var barInput = (TBarSeries)eventParams[0]; + + // Create indicator instances using reflection + var indicatorType = Type.GetType($"QuanTAlib.{indicatorName}, QuanTAlib")!; + var directIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, directParams)!; + var eventIndicator = (AbstractBase)Activator.CreateInstance(indicatorType, eventParams)!; + + // Generate test data and calculate + for (int i = 0; i < TestDataPoints; i++) + { + var bar = GenerateRandomBar(rng, GetRandomDouble(rng) * 100); + barInput.Add(bar); + directIndicator.Calc(bar); + } + + bool areEqual = (double.IsNaN(directIndicator.Value) && double.IsNaN(eventIndicator.Value)) || + Math.Abs(directIndicator.Value - eventIndicator.Value) < Tolerance; + + Assert.True(areEqual, $"Bar indicator {indicatorName} failed: Expected {directIndicator.Value}, Actual {eventIndicator.Value}"); + } } diff --git a/Tests/test_quantower.cs b/Tests/test_quantower.cs index 23430830..1429698c 100644 --- a/Tests/test_quantower.cs +++ b/Tests/test_quantower.cs @@ -1,6 +1,10 @@ extern alias volatility; extern alias averages; extern alias statistics; +extern alias momentum; +extern alias oscillators; +extern alias volume; +extern alias experiments; using Xunit; using System.Reflection; @@ -8,6 +12,10 @@ using TradingPlatform.BusinessLayer; using statistics::QuanTAlib; using averages::QuanTAlib; using volatility::QuanTAlib; +using momentum::QuanTAlib; +using oscillators::QuanTAlib; +using volume::QuanTAlib; +using experiments::QuanTAlib; namespace QuanTAlib { @@ -43,6 +51,39 @@ namespace QuanTAlib } } + private static void TestIndicatorMultipleFields(string[] fieldNames) where T : Indicator, new() + { + var indicator = new T(); + try + { + var onInitMethod = typeof(T).GetMethod("OnInit", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(onInitMethod); + onInitMethod.Invoke(indicator, null); + var onUpdateMethod = typeof(T).GetMethod("OnUpdate", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(onUpdateMethod); + + foreach (var fieldName in fieldNames) + { + var field = typeof(T).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(field); + var fieldValue = field.GetValue(indicator); + Assert.NotNull(fieldValue); + } + + Assert.NotNull(indicator.ShortName); + Assert.NotEmpty(indicator.ShortName); + Assert.NotNull(indicator.Name); + Assert.NotEmpty(indicator.Name); + Assert.NotNull(indicator.Description); + Assert.NotEmpty(indicator.Description); + Assert.IsAssignableFrom(indicator); + } + catch (Exception ex) + { + throw new Xunit.Sdk.XunitException($"Test failed for {typeof(T).Name}: {ex.Message}"); + } + } + // Averages Indicators [Fact] public void Afirma() => TestIndicator(); [Fact] public void Alma() => TestIndicator(); @@ -95,9 +136,21 @@ namespace QuanTAlib // Volatility Indicators [Fact] public void Atr() => TestIndicator("atr"); - + [Fact] public void Cmo() => TestIndicator("cmo"); + [Fact] public void Cvi() => TestIndicator("cvi"); [Fact] public void Historical() => TestIndicator("historical"); + [Fact] public void Jbands() => TestIndicatorMultipleFields(new[] { "jmaUp", "jmaLo" }); + [Fact] public void Jvolty() => TestIndicator("jma"); [Fact] public void Realized() => TestIndicator("realized"); [Fact] public void Rvi() => TestIndicator("rvi"); + + // Momentum Indicators + [Fact] public void Adx() => TestIndicator("adx"); + [Fact] public void Adxr() => TestIndicator("adxr"); + [Fact] public void Apo() => TestIndicator("apo"); + [Fact] public void Dmi() => TestIndicator("dmi"); + [Fact] public void Dmx() => TestIndicator("dmx"); + [Fact] public void Dpo() => TestIndicator("dpo"); + [Fact] public void Macd() => TestIndicator("macd"); } } diff --git a/Tests/test_updates_oscillators.cs b/Tests/test_updates_oscillators.cs index 24964699..bd049ab9 100644 --- a/Tests/test_updates_oscillators.cs +++ b/Tests/test_updates_oscillators.cs @@ -1,340 +1,160 @@ using Xunit; -using System.Security.Cryptography; namespace QuanTAlib.Tests; -public class OscillatorsUpdateTests +public class OscillatorsUpdateTests : UpdateTestBase { - private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); - private const int RandomUpdates = 100; - private const double ReferenceValue = 100.0; - private const int precision = 8; - - private double GetRandomDouble() - { - byte[] bytes = new byte[8]; - rng.GetBytes(bytes); - return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100 - } - - private 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); - } - [Fact] public void Rsi_Update() { var indicator = new Rsi(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Rsx_Update() { var indicator = new Rsx(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Cmo_Update() { var indicator = new Cmo(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Ao_Update() { var indicator = new Ao(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Ac_Update() { var indicator = new Ac(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Aroon_Update() { var indicator = new Aroon(period: 25); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Bop_Update() { var indicator = new Bop(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Cci_Update() { var indicator = new Cci(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Cfo_Update() { var indicator = new Cfo(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Chop_Update() { var indicator = new Chop(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Cog_Update() { var indicator = new Cog(period: 10); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + TestTValueUpdate(indicator, indicator.Calc); + } - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + [Fact] + public void Coppock_Update() + { + var indicator = new Coppock(roc1Period: 14, roc2Period: 11, wmaPeriod: 10); + TestTValueUpdate(indicator, indicator.Calc); + } - Assert.Equal(initialValue, finalValue, precision); + [Fact] + public void Crsi_Update() + { + var indicator = new Crsi(period1: 10, period2: 14, period3: 30); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Smi_Update() { var indicator = new Smi(period: 10, smooth1: 3, smooth2: 3); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Srsi_Update() { var indicator = new Srsi(rsiPeriod: 14, stochPeriod: 14, smoothK: 3, smoothD: 3); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Stc_Update() { var indicator = new Stc(cyclePeriod: 10, fastPeriod: 23, slowPeriod: 50); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Stoch_Update() { var indicator = new Stoch(period: 14, smoothK: 3, smoothD: 3); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Tsi_Update() { var indicator = new Tsi(firstPeriod: 25, secondPeriod: 13); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Uo_Update() { var indicator = new Uo(period1: 7, period2: 14, period3: 28); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Willr_Update() { var indicator = new Willr(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Dosc_Update() { var indicator = new Dosc(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Efi_Update() { var indicator = new Efi(period: 13); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } } diff --git a/Tests/test_updates_statistics.cs b/Tests/test_updates_statistics.cs index ff6ed756..c28bc28e 100644 --- a/Tests/test_updates_statistics.cs +++ b/Tests/test_updates_statistics.cs @@ -1,272 +1,132 @@ using Xunit; -using System.Security.Cryptography; namespace QuanTAlib.Tests; -public class StatisticsUpdateTests +public class StatisticsUpdateTests : UpdateTestBase { - private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); - private const int RandomUpdates = 100; - private const double ReferenceValue = 100.0; - private const int precision = 8; - - private double GetRandomDouble() - { - byte[] bytes = new byte[8]; - rng.GetBytes(bytes); - return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100 - } - - private 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); - } - [Fact] public void Beta_Update() { var indicator = new Beta(period: 14); - TBar marketBar = GetRandomBar(true); - TBar assetBar = GetRandomBar(true); - double initialValue = indicator.Calc(marketBar, assetBar); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(false), GetRandomBar(false)); - } - double finalValue = indicator.Calc(new TBar(marketBar.Time, marketBar.Open, marketBar.High, marketBar.Low, marketBar.Close, marketBar.Volume, false), - new TBar(assetBar.Time, assetBar.Open, assetBar.High, assetBar.Low, assetBar.Close, assetBar.Volume, false)); - - Assert.Equal(initialValue, finalValue, precision); + TestDualTBarUpdate(indicator, indicator.Calc); } [Fact] public void Corr_Update() { var indicator = new Corr(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true), new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false), new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false), new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestDualTValueUpdate(indicator, indicator.Calc); } [Fact] public void Curvature_Update() { var indicator = new Curvature(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Entropy_Update() { var indicator = new Entropy(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Hurst_Update() { var indicator = new Hurst(period: 100, minLength: 10); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Kurtosis_Update() { var indicator = new Kurtosis(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Max_Update() { var indicator = new Max(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Median_Update() { var indicator = new Median(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Min_Update() { var indicator = new Min(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Mode_Update() { var indicator = new Mode(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Percentile_Update() { var indicator = new Percentile(period: 14, percent: 50); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Skew_Update() { var indicator = new Skew(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Slope_Update() { var indicator = new Slope(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Stddev_Update() { var indicator = new Stddev(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + TestTValueUpdate(indicator, indicator.Calc); + } - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + [Fact] + public void Theil_Update() + { + var indicator = new Theil(period: 14); + TestTValueUpdate(indicator, indicator.Calc); + } - Assert.Equal(initialValue, finalValue, precision); + [Fact] + public void Tsf_Update() + { + var indicator = new Tsf(period: 14); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Variance_Update() { var indicator = new Variance(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Zscore_Update() { var indicator = new Zscore(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } } diff --git a/Tests/test_updates_volatility.cs b/Tests/test_updates_volatility.cs index 85a58ea7..7a9c1314 100644 --- a/Tests/test_updates_volatility.cs +++ b/Tests/test_updates_volatility.cs @@ -1,504 +1,223 @@ using Xunit; -using System.Security.Cryptography; namespace QuanTAlib.Tests; -public class VolatilityUpdateTests +public class VolatilityUpdateTests : UpdateTestBase { - private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); - private const int RandomUpdates = 100; - private const double ReferenceValue = 100.0; - private const int precision = 8; - - private double GetRandomDouble() - { - byte[] bytes = new byte[8]; - rng.GetBytes(bytes); - return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100 - } - - private 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); - } - [Fact] public void Adr_Update() { var indicator = new Adr(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Atr_Update() { var indicator = new Atr(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); + TestTBarUpdate(indicator, indicator.Calc); + } - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + [Fact] + public void Atrs_Update() + { + var indicator = new Atrs(period: 14, factor: 2.0); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Ap_Update() { var indicator = new Ap(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Atrp_Update() { var indicator = new Atrp(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Bband_Update() { var indicator = new Bband(period: 20, multiplier: 2.0); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Ccv_Update() { var indicator = new Ccv(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Ce_Update() { var indicator = new Ce(period: 22, multiplier: 3.0); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Cv_Update() { var indicator = new Cv(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Cvi_Update() { var indicator = new Cvi(period: 10, smoothPeriod: 10); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Dchn_Update() { var indicator = new Dchn(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Ewma_Update() { var indicator = new Ewma(period: 20, lambda: 0.94); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Fcb_Update() { var indicator = new Fcb(period: 20, smoothing: 0.5); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Gkv_Update() { var indicator = new Gkv(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Historical_Update() { var indicator = new Hv(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Hlv_Update() { var indicator = new Hlv(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Jvolty_Update() { var indicator = new Jvolty(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Natr_Update() { var indicator = new Natr(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Pch_Update() { var indicator = new Pch(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Pv_Update() { var indicator = new Pv(period: 10); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Realized_Update() { var indicator = new Rv(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Rsv_Update() { var indicator = new Rsv(period: 10); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Rvi_Update() { var indicator = new Rvi(period: 14); - double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTValueUpdate(indicator, indicator.Calc); } [Fact] public void Sv_Update() { var indicator = new Sv(period: 20, lambda: 0.94); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Tr_Update() { var indicator = new Tr(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Ui_Update() { var indicator = new Ui(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Vc_Update() { var indicator = new Vc(period: 20, deviations: 2.0); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Vov_Update() { var indicator = new Vov(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Vr_Update() { var indicator = new Vr(shortPeriod: 10, longPeriod: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Vs_Update() { var indicator = new Vs(period: 14, multiplier: 2.0); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } [Fact] public void Yzv_Update() { var indicator = new Yzv(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); + TestTBarUpdate(indicator, indicator.Calc); } } diff --git a/quantower/Momentum/DpoIndicator.cs b/quantower/Momentum/DpoIndicator.cs index 61ea6f3f..0937956d 100644 --- a/quantower/Momentum/DpoIndicator.cs +++ b/quantower/Momentum/DpoIndicator.cs @@ -25,7 +25,7 @@ public class DpoIndicator : Indicator, IWatchlistIndicator [InputParameter("Show cold values", sortIndex: 3)] public bool ShowColdValues { get; set; } = true; - private Dpo? Dpo; + private Dpo? dpo; protected LineSeries? DpoSeries; public int MinHistoryDepths => Period * 2; int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths; @@ -42,14 +42,14 @@ public class DpoIndicator : Indicator, IWatchlistIndicator protected override void OnInit() { - Dpo = new Dpo(Period); + dpo = new Dpo(Period); base.OnInit(); } protected override void OnUpdate(UpdateArgs args) { TBar input = this.GetInputBar(args); - TValue result = Dpo!.Calc(input); + TValue result = dpo!.Calc(input); DpoSeries!.SetValue(result.Value); DpoSeries!.SetMarker(0, Color.Transparent); @@ -62,6 +62,6 @@ public class DpoIndicator : Indicator, IWatchlistIndicator public override void OnPaintChart(PaintChartEventArgs args) { base.OnPaintChart(args); - this.PaintSmoothCurve(args, DpoSeries!, Dpo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); + this.PaintSmoothCurve(args, DpoSeries!, dpo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2); } }