diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml
index b3ca034c..c6cc6d3d 100644
--- a/.github/workflows/Publish.yml
+++ b/.github/workflows/Publish.yml
@@ -26,7 +26,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '8.x'
- name: Install JDK11 for Sonar Scanner
uses: actions/setup-java@v3
@@ -79,7 +79,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '8.x'
- name: Install dotnet tools
run: |
@@ -134,7 +134,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '8.x'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
@@ -150,39 +150,42 @@ jobs:
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
- SecurityCodeScan:
- runs-on: windows-latest
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Setup NuGet
- uses: nuget/setup-nuget@v1
-
- - name: Setup MSBuild
- uses: microsoft/setup-msbuild@v1
-
- - name: Setup .NET SDK
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: '3.1.x'
-
- - name: Set up projects for analysis
- uses: security-code-scan/security-code-scan-add-action@v1
-
- - name: Restore dependencies
- run: dotnet restore
-
- - name: Build
- run: dotnet build --no-restore --configuration Debug
-
- - name: Convert SARIF for uploading to GitHub
- uses: security-code-scan/security-code-scan-results-action@v1
-
- - name: Upload SARIF
- uses: github/codeql-action/upload-sarif@v3
+# converting SARIF action not working yet.
+ #SecurityCodeScan:
+ # runs-on: windows-latest
+ # steps:
+ # - name: Checkout repository
+ # uses: actions/checkout@v4
+ # with:
+ # fetch-depth: 0
+#
+ # - name: Setup NuGet
+ # uses: nuget/setup-nuget@v1
+#
+ # - name: Setup MSBuild
+ # uses: microsoft/setup-msbuild@v1
+#
+ # - name: Setup .NET SDK
+ # uses: actions/setup-dotnet@v3
+ # with:
+ # dotnet-version: |
+ # 8.x
+ # 3.1.x
+ # dotnet-quality: 'preview'
+#
+ # - name: Set up projects for analysis
+ # uses: security-code-scan/security-code-scan-add-action@v1
+#
+ # - name: Build
+ # run: |
+ # dotnet restore
+ # dotnet build --no-restore --configuration Debug
+#
+ # - name: Convert SARIF for uploading to GitHub
+ # uses: security-code-scan/security-code-scan-results-action@v1
+#
+ # - name: Upload SARIF
+ # uses: github/codeql-action/upload-sarif@v3
Codacy_Scan:
runs-on: ubuntu-latest
@@ -212,7 +215,7 @@ jobs:
sarif_file: results.sarif
build_publish:
- needs: [SonarCloud, Code_Coverage, CodeQL, SecurityCodeScan, Codacy_Scan]
+ needs: [SonarCloud, Code_Coverage, CodeQL, Codacy_Scan]
if: success()
runs-on: ubuntu-latest
steps:
@@ -224,7 +227,7 @@ jobs:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '8.x'
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b477d0fa..75b2bf41 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -10,5 +10,8 @@
"sonarlint.connectedMode.project": {
"connectionId": "mihakralj",
"projectKey": "mihakralj_QuanTAlib"
- }
+ },
+ "dotnet.dotnetPath": "C:/Program Files/dotnet",
+ "omnisharp.useModernNet": true,
+ "omnisharp.sdkPath": "C:/Program Files/dotnet/sdk"
}
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 65b4088f..6ee76064 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,11 +1,12 @@
- en-US
net8.0
+ preview
+
enable
enable
true
- preview
+ en-US
false
false
true
@@ -20,12 +21,6 @@
snupkg
AnyCPU
true
-
-
- $(GitVersion_NuGetVersion)
- $(GitVersion_AssemblySemVer)
- $(GitVersion_AssemblySemFileVer)
- $(GitVersion_InformationalVersion)
@@ -50,10 +45,6 @@
-
- all
- runtime; build; native; contentfiles; analyzers
-
@@ -63,5 +54,3 @@
-
-
diff --git a/QuanTAlib.sln b/QuanTAlib.sln
index 4d6759fe..bd69454d 100644
--- a/QuanTAlib.sln
+++ b/QuanTAlib.sln
@@ -1,18 +1,21 @@
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{1E050FA4-630E-4801-9DE9-D2536DACA9B0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{1B9AC248-76F8-44DD-958D-F1DC08EE1E87}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{2E9427C7-144F-488E-A29D-789ACC1C32AE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages\Averages.csproj", "{D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages\Averages.csproj", "{6BE10C39-4127-446C-818B-7976FCDD51D5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{B7DC44F7-D3A3-4C70-9025-513E0182B646}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}"
+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", "{E204F173-5840-4AA3-BED1-98C8D2F813E3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -23,29 +26,34 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Release|Any CPU.Build.0 = Release|Any CPU
- {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Release|Any CPU.Build.0 = Release|Any CPU
- {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Release|Any CPU.Build.0 = Release|Any CPU
- {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Release|Any CPU.Build.0 = Release|Any CPU
- {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Release|Any CPU.Build.0 = Release|Any CPU
- {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1E050FA4-630E-4801-9DE9-D2536DACA9B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2E9427C7-144F-488E-A29D-789ACC1C32AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2E9427C7-144F-488E-A29D-789ACC1C32AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2E9427C7-144F-488E-A29D-789ACC1C32AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2E9427C7-144F-488E-A29D-789ACC1C32AE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6BE10C39-4127-446C-818B-7976FCDD51D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6BE10C39-4127-446C-818B-7976FCDD51D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6BE10C39-4127-446C-818B-7976FCDD51D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6BE10C39-4127-446C-818B-7976FCDD51D5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {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.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
+ {E204F173-5840-4AA3-BED1-98C8D2F813E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E204F173-5840-4AA3-BED1-98C8D2F813E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E204F173-5840-4AA3-BED1-98C8D2F813E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E204F173-5840-4AA3-BED1-98C8D2F813E3}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {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}
EndGlobalSection
EndGlobal
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 0f30adf3..44158e8a 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -1,22 +1,26 @@
- net8.0
QuanTAlib.Tests
QuanTAlib.Tests
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
-
@@ -29,6 +33,7 @@
+
diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs
new file mode 100644
index 00000000..dd8063f3
--- /dev/null
+++ b/Tests/test_eventing.cs
@@ -0,0 +1,74 @@
+using Xunit;
+
+namespace QuanTAlib;
+
+public class EventingTests
+{
+ [Fact]
+ public void VerifyEventBasedCalculations()
+ {
+ // Create a random number generator with a fixed seed for reproducibility
+ var random = new Random(42);
+
+ // Create an input series to hold our random values
+ var input = new TSeries();
+ int p = 10;
+
+ // Create a list of indicator pairs (direct calculation and event-based)
+ var indicators = new List<(AbstractBase Direct, AbstractBase EventBased)>
+ {
+ (new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)),
+ (new Alma(p), new Alma(input, p)),
+ (new Convolution([1,2,3,2,1]), new Convolution(input, [1,2,3,2,1])),
+ (new Dema(p), new Dema(input, p)),
+ (new Dsma(p), new Dsma(input, p)),
+ (new Dwma(p), new Dwma(input, p)),
+ (new Ema(p), new Ema(input, p)),
+ (new Epma(p), new Epma(input, p)),
+ (new Frama(p), new Frama(input, p)),
+ (new Fwma(p), new Fwma(input, p)),
+ (new Gma(p), new Gma(input, p)),
+ (new Hma(p), new Hma(input, p)),
+ (new Htit(), new Htit(input)),
+ (new Hwma(p), new Hwma(input, p)),
+ (new Jma(p), new Jma(input, p)),
+ (new Kama(p), new Kama(input, p)),
+ (new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)),
+ (new Maaf(p), new Maaf(input, p)),
+ (new Mama(p), new Mama(input, p)),
+ (new Mgdi(p), new Mgdi(input, p)),
+ (new Mma(p), new Mma(input, p)),
+ (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)),
+ (new Rema(p), new Rema(input, p)),
+ (new Rma(p), new Rma(input, p)),
+
+ (new Sma(p), new Sma(input, p)),
+ (new Wma(p), new Wma(input, p)),
+ (new Rma(p), new Rma(input, p)),
+
+ (new Tema(p), new Tema(input, p)),
+ (new Kama(2, 30, 6), new Kama(input, 2, 30, 6)),
+
+ (new Zlema(p), new Zlema(input, p))
+ };
+
+ // Generate 200 random values and feed them to both direct and event-based indicators
+ for (int i = 0; i< 200; i++)
+ {
+ double randomValue = random.NextDouble() * 100;
+ input.Add(randomValue);
+
+ // Calculate direct indicators
+ foreach (var (direct, _) in indicators)
+ {
+ direct.Calc(randomValue);
+ }
+}
+
+// Compare the results of direct and event-based calculations
+foreach (var (direct, eventBased) in indicators)
+{
+ Assert.Equal(direct.Value, eventBased.Value, 9);
+}
+ }
+}
diff --git a/Tests/test_iTBar.cs b/Tests/test_iTBar.cs
index 3c8afe4a..79c8563c 100644
--- a/Tests/test_iTBar.cs
+++ b/Tests/test_iTBar.cs
@@ -4,55 +4,139 @@ using System.Diagnostics.CodeAnalysis;
namespace QuanTAlib;
+///
+/// Contains unit tests for bar-based indicators in QuanTAlib.
+///
[SuppressMessage("Security", "SCS0005:Weak random number generator.", Justification = "Acceptable for tests")]
-
public class BarIndicatorTests
{
private readonly Random rnd;
private const int SeriesLen = 1000;
private const int Corrections = 100;
+ ///
+ /// Initializes a new instance of the BarIndicatorTests class.
+ ///
public BarIndicatorTests()
{
rnd = new Random((int)DateTime.Now.Ticks);
}
- private static readonly iTValue[] indicators = new iTValue[]
+ private static readonly ITValue[] indicators = new ITValue[]
{
- new Atr(period: 14),
+ new Atr(period: 14),
+ // Add other TBar-based indicators here
};
+ ///
+ /// Tests if the indicator produces consistent results when processing new and updated bars.
+ ///
+ /// The indicator to test.
[Theory]
[MemberData(nameof(GetIndicators))]
- public void IndicatorIsNew(iTValue indicator)
+ public void IndicatorIsNew(ITValue indicator)
{
var indicator1 = indicator;
var indicator2 = indicator;
- MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!;
+ MethodInfo calcMethod = FindCalcMethod(indicator.GetType());
if (calcMethod == null)
{
- throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}");
+ throw new InvalidOperationException($"Calc method not found for indicator type: {indicator.GetType().Name}");
}
for (int i = 0; i < SeriesLen; i++)
{
- TBar item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: true);
- calcMethod.Invoke(indicator1, new object[] { item1 });
+ TBar item1 = GenerateRandomBar(isNew: true);
+ InvokeCalc(indicator1, calcMethod, item1);
for (int j = 0; j < Corrections; j++)
{
- item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: false);
- calcMethod.Invoke(indicator1, new object[] { item1 });
+ item1 = GenerateRandomBar(isNew: false);
+ InvokeCalc(indicator1, calcMethod, item1);
}
var item2 = new TBar(item1.Time, item1.Open, item1.High, item1.Low, item1.Close, item1.Volume, IsNew: true);
- calcMethod.Invoke(indicator2, new object[] { item2 });
+ InvokeCalc(indicator2, calcMethod, item2);
Assert.Equal(indicator1.Value, indicator2.Value);
}
}
+ ///
+ /// Finds the appropriate Calc method for the given indicator type.
+ ///
+ /// The type of the indicator.
+ /// The MethodInfo for the Calc method.
+ private static MethodInfo FindCalcMethod(Type type)
+ {
+ while (type != null && type != typeof(object))
+ {
+ var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
+ .Where(m => m.Name == "Calc")
+ .ToList();
+
+ if (methods.Count > 0)
+ {
+ // Prefer the method with TBar parameter
+ var method = methods.FirstOrDefault(m =>
+ {
+ var parameters = m.GetParameters();
+ return parameters.Length == 1 && parameters[0].ParameterType == typeof(TBar);
+ });
+
+ // If not found, return the first method
+ return method ?? methods.First();
+ }
+
+ type = type.BaseType!;
+ }
+ return null!;
+ }
+
+ ///
+ /// Invokes the Calc method on the given indicator with the provided input.
+ ///
+ /// The indicator instance.
+ /// The Calc method to invoke.
+ /// The input TBar.
+ private static void InvokeCalc(ITValue indicator, MethodInfo calcMethod, TBar input)
+ {
+ var parameters = calcMethod.GetParameters();
+ if (parameters.Length == 1)
+ {
+ calcMethod.Invoke(indicator, new object[] { input });
+ }
+ else if (parameters.Length == 2)
+ {
+ calcMethod.Invoke(indicator, new object[] { input, double.NaN });
+ }
+ else
+ {
+ throw new InvalidOperationException($"Invalid number of parameters for Calc method in indicator type: {indicator.GetType().Name}");
+ }
+ }
+
+ ///
+ /// Generates a random TBar for testing purposes.
+ ///
+ /// Indicates whether the generated bar should be marked as new.
+ /// A randomly generated TBar.
+ private TBar GenerateRandomBar(bool isNew)
+ {
+ double open = rnd.NextDouble() * 200 - 100;
+ double close = rnd.NextDouble() * 200 - 100;
+ double high = Math.Max(open, close) + rnd.NextDouble() * 10;
+ double low = Math.Min(open, close) - rnd.NextDouble() * 10;
+ long volume = rnd.Next(0, 10000);
+
+ return new TBar(Time: DateTime.Now, Open: open, High: high, Low: low, Close: close, Volume: volume, IsNew: isNew);
+ }
+
+ ///
+ /// Provides the list of indicators for parameterized tests.
+ ///
+ /// An enumerable of object arrays, each containing an indicator instance.
public static IEnumerable