Files
Miha Kralj 060649192f docs: remove C# Implementation Considerations sections, clean up temp scripts, reorganize test files
- Remove 'C# Implementation Considerations' sections from 34 indicator .md files
- Delete 29 temp PowerShell scripts (_fix_mojibake.ps1, _hex_scan.ps1, etc.)
- Move test files into tests/ subdirectories for consistent project structure
- Add trader-focused bullet points to indicator documentation
2026-03-12 12:34:16 -07:00

184 lines
6.3 KiB
C#

// Jvolty: Mathematical property validation tests
// Jvolty is a proprietary Jurik Research indicator — no external library equivalents exist.
// Validation uses mathematical property testing against known volatility band behaviors.
namespace QuanTAlib.Tests;
using Xunit;
public class JvoltyValidationTests
{
private const int DefaultPeriod = 10;
private const int TestDataLength = 500;
[Fact]
public void Jvolty_Output_IsFiniteForGbmData()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvolty = new Jvolty(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
var result = jvolty.Update(series[i], isNew: true);
Assert.True(double.IsFinite(result.Value),
$"Jvolty output must be finite at bar {i}, got {result.Value}");
}
}
[Fact]
public void Jvolty_Output_IsPositive_AfterWarmup()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvolty = new Jvolty(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
var result = jvolty.Update(series[i], isNew: true);
if (jvolty.IsHot)
{
Assert.True(result.Value >= 1.0,
$"Jvolty output must be >= 1.0 after warmup at bar {i}, got {result.Value}");
}
}
}
[Fact]
public void Jvolty_ConstantSeries_MinimumVolatility()
{
var jvolty = new Jvolty(DefaultPeriod);
double price = 100.0;
// Feed constant-price values
for (int i = 0; i < 300; i++)
{
jvolty.Update(new TValue(DateTime.UtcNow.AddMinutes(i), price), isNew: true);
}
// Constant series should produce minimum volatility (d = 1.0)
Assert.Equal(1.0, jvolty.Last.Value, precision: 1);
}
[Fact]
public void Jvolty_UpperBand_GreaterOrEqualLowerBand()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvolty = new Jvolty(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
jvolty.Update(series[i], isNew: true);
Assert.True(jvolty.UpperBand >= jvolty.LowerBand,
$"UpperBand ({jvolty.UpperBand}) must be >= LowerBand ({jvolty.LowerBand}) at bar {i}");
}
}
[Fact]
public void Jvolty_HighVolatility_ProducesHigherExponent()
{
// Low volatility data
var lowVolSeries = new GBM(sigma: 0.01, seed: 123).Fetch(300, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var lowJvolty = new Jvolty(DefaultPeriod);
for (int i = 0; i < lowVolSeries.Count; i++)
{
lowJvolty.Update(lowVolSeries[i], isNew: true);
}
double lowVolResult = lowJvolty.Last.Value;
// High volatility data
var highVolSeries = new GBM(sigma: 2.0, seed: 123).Fetch(300, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var highJvolty = new Jvolty(DefaultPeriod);
for (int i = 0; i < highVolSeries.Count; i++)
{
highJvolty.Update(highVolSeries[i], isNew: true);
}
double highVolResult = highJvolty.Last.Value;
// High volatility data should generally produce higher exponent values
// (This is a statistical property, not guaranteed per-sample)
Assert.True(highVolResult >= 1.0, "High vol result should be >= 1.0");
Assert.True(lowVolResult >= 1.0, "Low vol result should be >= 1.0");
}
[Fact]
public void Jvolty_BatchAndStreaming_ProduceSameResults()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
// Batch
var batchResults = Jvolty.Batch(series, DefaultPeriod);
// Streaming
var streamJvolty = new Jvolty(DefaultPeriod);
var streamResults = new double[series.Count];
for (int i = 0; i < series.Count; i++)
{
var result = streamJvolty.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 Jvolty_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];
Jvolty.Batch(series.Values, spanOutput, DefaultPeriod);
// Streaming
var streamJvolty = new Jvolty(DefaultPeriod);
for (int i = 0; i < series.Count; i++)
{
streamJvolty.Update(series[i], isNew: true);
Assert.Equal(spanOutput[i], streamJvolty.Last.Value, precision: 10);
}
}
[Fact]
public void Jvolty_DifferentPeriods_ProduceDifferentResults()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvolty5 = new Jvolty(5);
var jvolty50 = new Jvolty(50);
for (int i = 0; i < series.Count; i++)
{
jvolty5.Update(series[i], isNew: true);
jvolty50.Update(series[i], isNew: true);
}
// Different periods should produce different results
Assert.NotEqual(jvolty5.Last.Value, jvolty50.Last.Value);
}
[Fact]
public void Jvolty_BarCorrection_IsNewFalse_RestoresState()
{
var series = new GBM(sigma: 0.5, seed: 123).Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var jvolty = new Jvolty(DefaultPeriod);
// Process 30 bars
for (int i = 0; i < 30; i++)
{
jvolty.Update(series[i], isNew: true);
}
// Update bar 30 (isNew=true) then correct it (isNew=false)
jvolty.Update(series[30], isNew: true);
double afterNew = jvolty.Last.Value;
jvolty.Update(series[30], isNew: false);
double afterCorrection = jvolty.Last.Value;
Assert.Equal(afterNew, afterCorrection, precision: 10);
}
}