Adl, Adosc, Aobv, Cmf

This commit is contained in:
Miha
2024-10-28 15:29:29 -07:00
parent 6b79f8158c
commit 45c6f08e1e
14 changed files with 1266 additions and 23 deletions
+251
View File
@@ -0,0 +1,251 @@
using Xunit;
namespace QuanTAlib.Tests;
public class CoreTests
{
#region CircularBuffer Tests
[Fact]
public void CircularBuffer_BasicOperations()
{
var buffer = new CircularBuffer(5);
// Test initial state
Assert.Equal(5, buffer.Capacity);
Assert.Equal(0, buffer.Count);
// Test adding items
buffer.Add(1.0);
buffer.Add(2.0);
Assert.Equal(2, buffer.Count);
Assert.Equal(1.0, buffer[0]);
Assert.Equal(2.0, buffer[^1]);
// Test overflow behavior
buffer.Add(3.0);
buffer.Add(4.0);
buffer.Add(5.0);
buffer.Add(6.0); // Should remove oldest item (1.0)
Assert.Equal(5, buffer.Count);
Assert.Equal(2.0, buffer[0]);
Assert.Equal(6.0, buffer[^1]);
}
[Fact]
public void CircularBuffer_UpdateBehavior()
{
var buffer = new CircularBuffer(3);
// Add new values
buffer.Add(1.0, isNew: true);
buffer.Add(2.0, isNew: true);
Assert.Equal(2, buffer.Count);
// Update last value
buffer.Add(2.5, isNew: false);
Assert.Equal(2, buffer.Count);
Assert.Equal(2.5, buffer[^1]);
}
[Fact]
public void CircularBuffer_MinMaxSumAverage()
{
var buffer = new CircularBuffer(5);
buffer.Add(1.0);
buffer.Add(2.0);
buffer.Add(3.0);
buffer.Add(4.0);
buffer.Add(5.0);
Assert.Equal(1.0, buffer.Min());
Assert.Equal(5.0, buffer.Max());
Assert.Equal(15.0, buffer.Sum());
Assert.Equal(3.0, buffer.Average());
}
[Fact]
public void CircularBuffer_Enumeration()
{
var buffer = new CircularBuffer(3);
buffer.Add(1.0);
buffer.Add(2.0);
buffer.Add(3.0);
var list = buffer.ToList();
Assert.Equal(3, list.Count);
Assert.Equal(1.0, list[0]);
Assert.Equal(3.0, list[2]);
}
#endregion
#region TBar Tests
[Fact]
public void TBar_Construction()
{
// Default constructor
var bar1 = new TBar();
Assert.Equal(0, bar1.Open);
Assert.True(bar1.IsNew);
// Value constructor
var bar2 = new TBar(10.0);
Assert.Equal(10.0, bar2.Open);
Assert.Equal(10.0, bar2.High);
Assert.Equal(10.0, bar2.Low);
Assert.Equal(10.0, bar2.Close);
// Full constructor
var time = DateTime.Now;
var bar3 = new TBar(time, 10.0, 12.0, 9.0, 11.0, 1000.0, false);
Assert.Equal(time, bar3.Time);
Assert.Equal(10.0, bar3.Open);
Assert.Equal(12.0, bar3.High);
Assert.Equal(9.0, bar3.Low);
Assert.Equal(11.0, bar3.Close);
Assert.Equal(1000.0, bar3.Volume);
Assert.False(bar3.IsNew);
}
[Fact]
public void TBar_DerivedValues()
{
var bar = new TBar(DateTime.Now, 10.0, 20.0, 5.0, 15.0, 1000.0);
Assert.Equal(12.5, bar.HL2); // (20 + 5) / 2
Assert.Equal(12.5, bar.OC2); // (10 + 15) / 2
Assert.Equal(11.67, bar.OHL3, 2); // (10 + 20 + 5) / 3
Assert.Equal(13.33, bar.HLC3, 2); // (20 + 5 + 15) / 3
Assert.Equal(12.5, bar.OHLC4); // (10 + 20 + 5 + 15) / 4
Assert.Equal(13.75, bar.HLCC4); // (20 + 5 + 15 + 15) / 4
}
[Fact]
public void TBarSeries_Operations()
{
var series = new TBarSeries();
var time = DateTime.Now;
var bar1 = new TBar(time, 10.0, 12.0, 9.0, 11.0, 1000.0);
var bar2 = new TBar(time.AddMinutes(1), 11.0, 13.0, 10.0, 12.0, 1100.0);
// Test adding bars
series.Add(bar1);
series.Add(bar2);
Assert.Equal(2, series.Count);
// Test updating last bar
var bar2Update = new TBar(bar2.Time, 11.0, 13.5, 9.5, 12.5, 1200.0, false);
series.Add(bar2Update);
Assert.Equal(2, series.Count);
Assert.Equal(12.5, series.Last.Close);
// Test derived series
Assert.Equal(11.0, series.Open.Last.Value);
Assert.Equal(13.5, series.High.Last.Value);
Assert.Equal(9.5, series.Low.Last.Value);
Assert.Equal(12.5, series.Close.Last.Value);
Assert.Equal(1200.0, series.Volume.Last.Value);
}
#endregion
#region TValue Tests
[Fact]
public void TValue_Construction()
{
// Default constructor
var value1 = new TValue();
Assert.Equal(0, value1.Value);
Assert.True(value1.IsNew);
Assert.True(value1.IsHot);
// Value constructor
var value2 = new TValue(10.0);
Assert.Equal(10.0, value2.Value);
// Full constructor
var time = DateTime.Now;
var value3 = new TValue(time, 10.0, false, false);
Assert.Equal(time, value3.Time);
Assert.Equal(10.0, value3.Value);
Assert.False(value3.IsNew);
Assert.False(value3.IsHot);
}
[Fact]
public void TValue_Conversions()
{
var value = new TValue(10.0);
// Test implicit conversions
double d = value;
Assert.Equal(10.0, d);
DateTime time = value;
Assert.Equal(value.Time, time);
// Test implicit conversion from double
TValue newValue = 20.0;
Assert.Equal(20.0, newValue.Value);
}
[Fact]
public void TSeries_Operations()
{
var series = new TSeries();
var time = DateTime.Now;
// Test adding values
series.Add(time, 10.0);
series.Add(time.AddMinutes(1), 20.0);
Assert.Equal(2, series.Count);
// Test updating last value
series.Add(new TValue(time.AddMinutes(1), 25.0, false));
Assert.Equal(2, series.Count);
Assert.Equal(25.0, series.Last.Value);
// Test adding range of values
var values = new[] { 30.0, 40.0, 50.0 };
foreach (var value in values)
{
series.Add(time.AddMinutes(series.Count + 1), value);
}
Assert.Equal(5, series.Count);
// Test conversions
var doubleList = (List<double>)series;
Assert.Equal(5, doubleList.Count);
Assert.Equal(50.0, doubleList[^1]);
var doubleArray = (double[])series;
Assert.Equal(5, doubleArray.Length);
Assert.Equal(50.0, doubleArray[^1]);
}
[Fact]
public void TSeries_EventHandling()
{
var series = new TSeries();
var receivedValues = new List<double>();
var time = DateTime.Now;
series.Pub += (object sender, in ValueEventArgs args) => receivedValues.Add(args.Tick.Value);
series.Add(time, 10.0);
series.Add(time.AddMinutes(1), 20.0);
series.Add(time.AddMinutes(2), 30.0);
Assert.Equal(3, receivedValues.Count);
Assert.Equal(10.0, receivedValues[0]);
Assert.Equal(20.0, receivedValues[1]);
Assert.Equal(30.0, receivedValues[2]);
}
#endregion
}
+53 -16
View File
@@ -13,12 +13,13 @@ public class EventingTests
// Create a cryptographically secure random number generator
using var rng = RandomNumberGenerator.Create();
// Create an input series to hold our random values
// Create input series to hold our random values
var input = new TSeries();
var barInput = new TBarSeries();
int p = 10;
// Create a list of indicator pairs (direct calculation and event-based) with names
var indicators = new List<(string Name, AbstractBase Direct, AbstractBase EventBased)>
// 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)),
@@ -51,19 +52,15 @@ public class EventingTests
("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)),
// Added missing averages
("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)),
// momentum indicators
("Apo", new Apo(12, 26), new Apo(input, 12, 26)),
// oscillators
("Rsi", new Rsi(p), new Rsi(input, p)),
("Rsx", new Rsx(p), new Rsx(input, p)),
("Cmo", new Cmo(p), new Cmo(input, p)),
// statistics
("Curvature", new Curvature(p), new Curvature(input, p)),
("Entropy", new Entropy(p), new Entropy(input, p)),
("Kurtosis", new Kurtosis(p), new Kurtosis(input, p)),
@@ -77,12 +74,12 @@ public class EventingTests
("Stddev", new Stddev(p), new Stddev(input, p)),
("Variance", new Variance(p), new Variance(input, p)),
("Zscore", new Zscore(p), new Zscore(input, p)),
// volatility
// 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
// 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)),
@@ -101,26 +98,66 @@ public class EventingTests
("Huber", new Huber(p), new Huber(input, p))
};
// Generate 200 random values and feed them to both direct and event-based indicators
// 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))
};
// 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 direct indicators
foreach (var (_, direct, _) in indicators)
// 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 of direct and event-based calculations
for (int i = 0; i < indicators.Count; i++)
// Compare the results for value-based indicators
foreach (var (name, direct, eventBased) in valueIndicators)
{
var (name, direct, eventBased) = indicators[i];
bool areEqual = (double.IsNaN(direct.Value) && double.IsNaN(eventBased.Value)) ||
Math.Abs(direct.Value - eventBased.Value) < 1e-9;
Assert.True(areEqual, $"Indicator {name} failed: Expected {direct.Value}, Actual {eventBased.Value}");
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}");
}
}
+159
View File
@@ -0,0 +1,159 @@
using Xunit;
using System.Security.Cryptography;
namespace QuanTAlib.Tests;
public class VolumeUpdateTests
{
private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
private const int RandomUpdates = 100;
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();
double volume = Math.Abs(GetRandomDouble()) * 1000; // Random positive volume
return new TBar(DateTime.Now, open, high, low, close, volume, IsNew);
}
[Fact]
public void Adl_Update()
{
var indicator = new Adl();
TBar r = GetRandomBar(true);
// First calculation with IsNew: true
double value1 = indicator.Calc(r);
// Multiple recalculations with IsNew: false should not change the value
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
}
// Final calculation with IsNew: false should match initial value
double value2 = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(value1, value2, precision);
// New calculation with IsNew: true should update the value
double value3 = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: true));
Assert.NotEqual(value1, value3, precision);
}
[Fact]
public void Adosc_Update()
{
var indicator = new Adosc(shortPeriod: 3, longPeriod: 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);
}
[Fact]
public void Aobv_Update()
{
var indicator = new Aobv();
TBar r = GetRandomBar(true);
// First calculation with IsNew: true
double value1 = indicator.Calc(r);
// Multiple recalculations with IsNew: false should not change the value
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
}
// Final calculation with IsNew: false should match initial value
double value2 = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
Assert.Equal(value1, value2, precision);
// New calculation with IsNew: true should update the value
double value3 = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: true));
Assert.NotEqual(value1, value3, precision);
}
[Fact]
public void Cmf_Update()
{
var indicator = new Cmf(period: 20);
TBar r = GetRandomBar(true);
// Generate a sequence of bars for warmup
var warmupBars = new List<TBar>();
for (int i = 0; i < indicator.WarmupPeriod; i++)
{
var bar = GetRandomBar(IsNew: true);
warmupBars.Add(bar);
indicator.Calc(bar);
}
// Calculate initial value after warmup
double initialValue = indicator.Calc(r);
// Apply random updates
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(GetRandomBar(IsNew: false));
}
// Reset and replay the same sequence
indicator.Init();
foreach (var bar in warmupBars)
{
indicator.Calc(bar);
}
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 Eom_Update()
{
var indicator = new Eom(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);
}
[Fact]
public void Kvo_Update()
{
var indicator = new Kvo(shortPeriod: 34, longPeriod: 55);
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);
}
}