mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-24 05:28:05 +00:00
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
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
// Va: Mathematical property validation tests
|
||||
// Volume Accumulation is a cumulative indicator. No standard external library equivalents
|
||||
// with matching implementation. Validation uses mathematical property testing.
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
using Xunit;
|
||||
|
||||
public class VaValidationTests
|
||||
{
|
||||
private const int TestDataLength = 500;
|
||||
|
||||
[Fact]
|
||||
public void Va_Output_IsFiniteForGbmData()
|
||||
{
|
||||
var bars = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var va = new Va();
|
||||
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
var result = va.Update(bars[i], isNew: true);
|
||||
Assert.True(double.IsFinite(result.Value),
|
||||
$"Va output must be finite at bar {i}, got {result.Value}");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_CloseAboveMidpoint_PositiveAccumulation()
|
||||
{
|
||||
var va = new Va();
|
||||
|
||||
// Close is above midpoint: (H+L)/2 = 100, Close = 102
|
||||
var bar = new TBar(DateTime.UtcNow, 101, 101, 99, 102, 1000);
|
||||
var result = va.Update(bar, isNew: true);
|
||||
|
||||
// VA_period = volume * (close - midpoint) = 1000 * (102 - 100) = 2000
|
||||
Assert.True(result.Value > 0,
|
||||
$"VA should be positive when close > midpoint, got {result.Value}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_CloseBelowMidpoint_NegativeAccumulation()
|
||||
{
|
||||
var va = new Va();
|
||||
|
||||
// Close is below midpoint: (H+L)/2 = 100, Close = 98
|
||||
var bar = new TBar(DateTime.UtcNow, 101, 101, 99, 98, 1000);
|
||||
var result = va.Update(bar, isNew: true);
|
||||
|
||||
// VA_period = volume * (close - midpoint) = 1000 * (98 - 100) = -2000
|
||||
Assert.True(result.Value < 0,
|
||||
$"VA should be negative when close < midpoint, got {result.Value}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_CloseAtMidpoint_ZeroAccumulation()
|
||||
{
|
||||
var va = new Va();
|
||||
|
||||
// Close is exactly at midpoint
|
||||
var bar = new TBar(DateTime.UtcNow, 101, 101, 99, 100, 1000);
|
||||
var result = va.Update(bar, isNew: true);
|
||||
|
||||
Assert.Equal(0.0, result.Value, precision: 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_ZeroVolume_ZeroAccumulation()
|
||||
{
|
||||
var va = new Va();
|
||||
|
||||
// Even with close above midpoint, zero volume = zero VA contribution
|
||||
var bar = new TBar(DateTime.UtcNow, 101, 101, 99, 102, 0);
|
||||
var result = va.Update(bar, isNew: true);
|
||||
|
||||
Assert.Equal(0.0, result.Value, precision: 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_IsCumulative_AccumulatesOverBars()
|
||||
{
|
||||
var va = new Va();
|
||||
|
||||
// Bar 1: close above midpoint
|
||||
var bar1 = new TBar(DateTime.UtcNow, 101, 101, 99, 102, 1000);
|
||||
var r1 = va.Update(bar1, isNew: true);
|
||||
double expectedVa1 = 1000 * (102 - 100.0); // 2000
|
||||
|
||||
// Bar 2: close below midpoint
|
||||
var bar2 = new TBar(DateTime.UtcNow.AddMinutes(1), 101, 101, 99, 98, 500);
|
||||
var r2 = va.Update(bar2, isNew: true);
|
||||
double expectedVa2 = expectedVa1 + 500 * (98 - 100.0); // 2000 + (-1000) = 1000
|
||||
|
||||
Assert.Equal(expectedVa1, r1.Value, precision: 10);
|
||||
Assert.Equal(expectedVa2, r2.Value, precision: 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_KnownCalculation_MatchesManual()
|
||||
{
|
||||
var va = new Va();
|
||||
|
||||
// Manually verified calculation
|
||||
// Bar: O=100, H=105, L=95, C=103, V=2000
|
||||
// Midpoint = (105 + 95) / 2 = 100
|
||||
// VA_period = 2000 * (103 - 100) = 6000
|
||||
var bar = new TBar(DateTime.UtcNow, 100, 105, 95, 103, 2000);
|
||||
var result = va.Update(bar, isNew: true);
|
||||
|
||||
Assert.Equal(6000.0, result.Value, precision: 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_BatchAndStreaming_ProduceSameResults()
|
||||
{
|
||||
var bars = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
// Batch
|
||||
var batchResults = Va.Batch(bars);
|
||||
|
||||
// Streaming
|
||||
var streamVa = new Va();
|
||||
var streamResults = new double[bars.Count];
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
var result = streamVa.Update(bars[i], isNew: true);
|
||||
streamResults[i] = result.Value;
|
||||
}
|
||||
|
||||
Assert.Equal(batchResults.Count, bars.Count);
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
Assert.Equal(batchResults.Values[i], streamResults[i], precision: 8);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_SpanAndStreaming_ProduceSameResults()
|
||||
{
|
||||
var bars = new GBM(sigma: 0.5, seed: 123).Fetch(TestDataLength, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var spanOutput = new double[bars.Count];
|
||||
|
||||
Va.Batch(
|
||||
bars.High.Values, bars.Low.Values,
|
||||
bars.Close.Values, bars.Volume.Values,
|
||||
spanOutput);
|
||||
|
||||
// Streaming
|
||||
var streamVa = new Va();
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
var result = streamVa.Update(bars[i], isNew: true);
|
||||
Assert.Equal(spanOutput[i], result.Value, precision: 8);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Va_BarCorrection_IsNewFalse_RestoresState()
|
||||
{
|
||||
var bars = new GBM(sigma: 0.5, seed: 123).Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var va = new Va();
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
va.Update(bars[i], isNew: true);
|
||||
}
|
||||
|
||||
va.Update(bars[30], isNew: true);
|
||||
double afterNew = va.Last.Value;
|
||||
|
||||
va.Update(bars[30], isNew: false);
|
||||
double afterCorrection = va.Last.Value;
|
||||
|
||||
Assert.Equal(afterNew, afterCorrection, precision: 10);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user