Files
QuanTAlib/lib/volatility/jvoltyn/Jvoltyn.Validation.Tests.cs
T
Miha Kralj 951842acca Add validation tests for various volume and momentum indicators
- Introduced Massi validation tests to ensure mathematical properties hold for the Mass Index indicator.
- Added Va validation tests for Volume Accumulation, checking for finite outputs and correct accumulation behavior.
- Implemented Vf validation tests for Volume Force, verifying outputs for rising and falling prices, and ensuring batch and streaming results match.
- Created Vo validation tests for Volume Oscillator, confirming behavior with constant, increasing, and decreasing volumes.
- Developed Vroc validation tests for Volume Rate of Change, validating outputs for constant volume and changes in volume.
- Updated project file to include new momentum indicators (MACD and RSI) in the compilation.
2026-02-12 19:43:09 -08:00

189 lines
6.4 KiB
C#

// Jvoltyn: Mathematical property validation tests
// Jvoltyn is a proprietary Jurik Research indicator — no external library equivalents exist.
// Validation uses mathematical property testing: normalized output must be in [0, 100].
namespace QuanTAlib.Tests;
using Xunit;
public class JvoltynValidationTests
{
private const int DefaultPeriod = 10;
private const int TestDataLength = 500;
[Fact]
public void Jvoltyn_Output_IsFiniteForGbmData()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvoltyn = new Jvoltyn(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
var result = jvoltyn.Update(series[i], isNew: true);
Assert.True(double.IsFinite(result.Value),
$"Jvoltyn output must be finite at bar {i}, got {result.Value}");
}
}
[Fact]
public void Jvoltyn_Output_InRange0To100_AfterWarmup()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvoltyn = new Jvoltyn(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
var result = jvoltyn.Update(series[i], isNew: true);
if (jvoltyn.IsHot)
{
Assert.True(result.Value >= -0.01 && result.Value <= 100.01,
$"Jvoltyn output must be in [0, 100] after warmup at bar {i}, got {result.Value}");
}
}
}
[Fact]
public void Jvoltyn_ConstantSeries_ZeroNormalizedVolatility()
{
var jvoltyn = new Jvoltyn(DefaultPeriod);
double price = 100.0;
// Feed constant-price values
for (int i = 0; i < 300; i++)
{
jvoltyn.Update(new TValue(DateTime.UtcNow.AddMinutes(i), price), isNew: true);
}
// Constant series: d = 1 → normalized = (1-1)/(logParam-1)*100 = 0
Assert.Equal(0.0, jvoltyn.Last.Value, precision: 1);
}
[Fact]
public void Jvoltyn_FirstBar_ReturnsZero()
{
var jvoltyn = new Jvoltyn(DefaultPeriod);
var result = jvoltyn.Update(new TValue(DateTime.UtcNow, 100.0), isNew: true);
// First bar initializes bands to price, d=1 → normalized=0
Assert.Equal(0.0, result.Value, precision: 10);
}
[Fact]
public void Jvoltyn_UpperBand_GreaterOrEqualLowerBand()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvoltyn = new Jvoltyn(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
jvoltyn.Update(series[i], isNew: true);
Assert.True(jvoltyn.UpperBand >= jvoltyn.LowerBand,
$"UpperBand ({jvoltyn.UpperBand}) must be >= LowerBand ({jvoltyn.LowerBand}) at bar {i}");
}
}
[Fact]
public void Jvoltyn_RawVolatility_IsConsistentWithNormalized()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvoltyn = new Jvoltyn(DefaultPeriod);
// Calculate logParam manually to verify normalization
double lengthParam = (DefaultPeriod - 1.0) / 2.0;
double logParam = System.Math.Log(System.Math.Sqrt(lengthParam)) / System.Math.Log(2.0);
logParam = (logParam + 2.0) < 0.0 ? 0.0 : (logParam + 2.0);
double normFactor = System.Math.Abs(logParam - 1.0) > 1e-10 ? 100.0 / (logParam - 1.0) : 0.0;
for (int i = 0; i < series.Count; i++)
{
jvoltyn.Update(series[i], isNew: true);
if (i > 0) // Skip first bar initialization
{
double expectedNormalized = (jvoltyn.RawVolatility - 1.0) * normFactor;
Assert.Equal(expectedNormalized, jvoltyn.Last.Value, precision: 8);
}
}
}
[Fact]
public void Jvoltyn_BatchAndStreaming_ProduceSameResults()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
// Batch
var batchResults = Jvoltyn.Batch(series, DefaultPeriod);
// Streaming
var streamJvoltyn = new Jvoltyn(DefaultPeriod);
var streamResults = new double[series.Count];
for (int i = 0; i < series.Count; i++)
{
var result = streamJvoltyn.Update(series[i], isNew: true);
streamResults[i] = result.Value;
}
Assert.Equal(batchResults.Count, series.Count);
for (int i = 0; i < series.Count; i++)
{
Assert.Equal(batchResults.Values[i], streamResults[i], precision: 10);
}
}
[Fact]
public void Jvoltyn_SpanAndStreaming_ProduceSameResults()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var spanOutput = new double[series.Count];
Jvoltyn.Batch(series.Values, spanOutput, DefaultPeriod);
// Streaming
var streamJvoltyn = new Jvoltyn(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
streamJvoltyn.Update(series[i], isNew: true);
Assert.Equal(spanOutput[i], streamJvoltyn.Last.Value, precision: 10);
}
}
[Fact]
public void Jvoltyn_DifferentPeriods_ProduceDifferentResults()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvoltyn5 = new Jvoltyn(5);
var jvoltyn50 = new Jvoltyn(50);
for (int i = 0; i < series.Count; i++)
{
jvoltyn5.Update(series[i], isNew: true);
jvoltyn50.Update(series[i], isNew: true);
}
Assert.NotEqual(jvoltyn5.Last.Value, jvoltyn50.Last.Value);
}
[Fact]
public void Jvoltyn_BarCorrection_IsNewFalse_RestoresState()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvoltyn = new Jvoltyn(DefaultPeriod);
for (int i = 0; i < 30; i++)
{
jvoltyn.Update(series[i], isNew: true);
}
jvoltyn.Update(series[30], isNew: true);
double afterNew = jvoltyn.Last.Value;
jvoltyn.Update(series[30], isNew: false);
double afterCorrection = jvoltyn.Last.Value;
Assert.Equal(afterNew, afterCorrection, precision: 10);
}
}