mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-25 22:08:05 +00:00
event tests
This commit is contained in:
+92
-8
@@ -4,14 +4,19 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// Contains unit tests for bar-based indicators in QuanTAlib.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the BarIndicatorTests class.
|
||||
/// </summary>
|
||||
public BarIndicatorTests()
|
||||
{
|
||||
rnd = new Random((int)DateTime.Now.Ticks);
|
||||
@@ -19,9 +24,14 @@ public class BarIndicatorTests
|
||||
|
||||
private static readonly ITValue[] indicators = new ITValue[]
|
||||
{
|
||||
new Atr(period: 14),
|
||||
new Atr(period: 14),
|
||||
// Add other TBar-based indicators here
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the indicator produces consistent results when processing new and updated bars.
|
||||
/// </summary>
|
||||
/// <param name="indicator">The indicator to test.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GetIndicators))]
|
||||
public void IndicatorIsNew(ITValue indicator)
|
||||
@@ -29,7 +39,7 @@ public class BarIndicatorTests
|
||||
var indicator1 = indicator;
|
||||
var indicator2 = indicator;
|
||||
|
||||
MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!;
|
||||
MethodInfo calcMethod = FindCalcMethod(indicator.GetType());
|
||||
if (calcMethod == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Calc method not found for indicator type: {indicator.GetType().Name}");
|
||||
@@ -37,22 +47,96 @@ public class BarIndicatorTests
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the appropriate Calc method for the given indicator type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the indicator.</param>
|
||||
/// <returns>The MethodInfo for the Calc method.</returns>
|
||||
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!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the Calc method on the given indicator with the provided input.
|
||||
/// </summary>
|
||||
/// <param name="indicator">The indicator instance.</param>
|
||||
/// <param name="calcMethod">The Calc method to invoke.</param>
|
||||
/// <param name="input">The input TBar.</param>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random TBar for testing purposes.
|
||||
/// </summary>
|
||||
/// <param name="isNew">Indicates whether the generated bar should be marked as new.</param>
|
||||
/// <returns>A randomly generated TBar.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the list of indicators for parameterized tests.
|
||||
/// </summary>
|
||||
/// <returns>An enumerable of object arrays, each containing an indicator instance.</returns>
|
||||
public static IEnumerable<object[]> GetIndicators()
|
||||
{
|
||||
return indicators.Select(indicator => new object[] { indicator });
|
||||
|
||||
Reference in New Issue
Block a user