diff --git a/QuanTAlib.sln b/QuanTAlib.sln
index 068fcd79..ad664505 100644
--- a/QuanTAlib.sln
+++ b/QuanTAlib.sln
@@ -12,6 +12,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Avera
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\_Volatility.csproj", "{B7DC44F7-D3A3-4C70-9025-513E0182B646}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Oscillators", "quantower\Oscillators\_Oscillators.csproj", "{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volume", "quantower\Volume\_Volume.csproj", "{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Momentum", "quantower\Momentum\_Momentum.csproj", "{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Experiments", "quantower\Experiments\_Experiments.csproj", "{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{1CF111D9-33E6-4A11-8FEC-F23300A78D15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{2D97C971-20BF-40DB-94AA-3279F787D3CB}"
@@ -40,12 +48,27 @@ Global
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release | Any CPU.ActiveCfg = Release | Any CPU
- {B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release | Any CPU.Build.0 = Release | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug | Any CPU.ActiveCfg = Debug | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug | Any CPU.Build.0 = Debug | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release | Any CPU.ActiveCfg = Release | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release | Any CPU.Build.0 = Release | Any CPU
+ {B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.Build.0 = Release|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -55,5 +78,9 @@ Global
{2E9427C7-144F-488E-A29D-789ACC1C32AE} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{6BE10C39-4127-446C-818B-7976FCDD51D5} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{B7DC44F7-D3A3-4C70-9025-513E0182B646} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
EndGlobalSection
EndGlobal
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 5406876e..34c65028 100644
--- a/Tests/test_eventing.cs
+++ b/Tests/test_eventing.cs
@@ -1,170 +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", System.Array.Empty