mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 06:18: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,120 @@
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class RgmaIndicatorTests
|
||||
{
|
||||
[Fact]
|
||||
public void RgmaIndicator_Constructor_SetsDefaults()
|
||||
{
|
||||
var indicator = new RgmaIndicator();
|
||||
|
||||
Assert.Equal(10, indicator.Period);
|
||||
Assert.Equal(3, indicator.Passes);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
Assert.Equal("RGMA - Recursive Gaussian Moving Average", indicator.Name);
|
||||
Assert.False(indicator.SeparateWindow);
|
||||
Assert.True(indicator.OnBackGround);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RgmaIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new RgmaIndicator { Period = 10 };
|
||||
|
||||
Assert.Equal(0, RgmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RgmaIndicator_ShortName_IncludesParametersAndSource()
|
||||
{
|
||||
var indicator = new RgmaIndicator { Period = 15, Passes = 4, Source = SourceType.HLC3 };
|
||||
|
||||
Assert.Contains("RGMA", indicator.ShortName, StringComparison.Ordinal);
|
||||
Assert.Contains("15", indicator.ShortName, StringComparison.Ordinal);
|
||||
Assert.Contains("4", indicator.ShortName, StringComparison.Ordinal);
|
||||
Assert.Contains("HLC3", indicator.ShortName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RgmaIndicator_Initialize_CreatesInternalRgma()
|
||||
{
|
||||
var indicator = new RgmaIndicator { Period = 10, Passes = 3 };
|
||||
|
||||
indicator.Initialize();
|
||||
|
||||
Assert.Single(indicator.LinesSeries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RgmaIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
|
||||
{
|
||||
var indicator = new RgmaIndicator { Period = 10, Passes = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RgmaIndicator_ProcessUpdate_NewBar_ComputesValue()
|
||||
{
|
||||
var indicator = new RgmaIndicator { Period = 10, Passes = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(1), 102, 112, 98, 110);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewBar));
|
||||
|
||||
Assert.Equal(2, indicator.LinesSeries[0].Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RgmaIndicator_ProcessUpdate_NewTick_ProcessesWithoutError()
|
||||
{
|
||||
var indicator = new RgmaIndicator { Period = 10, Passes = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
double firstValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewTick));
|
||||
double secondValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
Assert.True(double.IsFinite(firstValue));
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RgmaIndicator_DifferentSourceTypes_Work()
|
||||
{
|
||||
var sources = new[] { SourceType.Open, SourceType.High, SourceType.Low, SourceType.Close, SourceType.HL2, SourceType.HLC3 };
|
||||
|
||||
foreach (var source in sources)
|
||||
{
|
||||
var indicator = new RgmaIndicator { Source = source, Period = 10, Passes = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 110, 90, 105);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)),
|
||||
$"Source {source} should produce finite value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class RgmaTests
|
||||
{
|
||||
[Fact]
|
||||
public void Rgma_Constructor_ValidatesInput()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new Rgma(0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new Rgma(-1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new Rgma(10, 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new Rgma(10, -1));
|
||||
|
||||
var rgma = new Rgma(10, 3);
|
||||
Assert.Equal("Rgma(10,3)", rgma.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_BasicCalculation_ReturnsFinite()
|
||||
{
|
||||
var rgma = new Rgma(10, passes: 3);
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1, seed: 42);
|
||||
int iterations = rgma.WarmupPeriod + 2;
|
||||
|
||||
TValue result = default;
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
result = rgma.Update(new TValue(bar.Time, bar.Close));
|
||||
}
|
||||
|
||||
Assert.True(double.IsFinite(result.Value));
|
||||
Assert.True(rgma.IsHot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_IsNewFalse_RestoresState()
|
||||
{
|
||||
var rgma = new Rgma(10, passes: 3);
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1, seed: 7);
|
||||
|
||||
TValue lastInput = default;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
lastInput = new TValue(bar.Time, bar.Close);
|
||||
rgma.Update(lastInput, isNew: true);
|
||||
}
|
||||
|
||||
double original = rgma.Last.Value;
|
||||
var corrected = new TValue(lastInput.Time, lastInput.Value * 1.1);
|
||||
|
||||
rgma.Update(corrected, isNew: false);
|
||||
rgma.Update(lastInput, isNew: false);
|
||||
|
||||
Assert.Equal(original, rgma.Last.Value, precision: 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_Reset_ClearsState()
|
||||
{
|
||||
var rgma = new Rgma(10, passes: 3);
|
||||
rgma.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
|
||||
rgma.Reset();
|
||||
|
||||
Assert.Equal(default, rgma.Last);
|
||||
Assert.False(rgma.IsHot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_Robustness_NaNAndInfinity_UsesLastValid()
|
||||
{
|
||||
var rgma = new Rgma(10, passes: 3);
|
||||
rgma.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
rgma.Update(new TValue(DateTime.UtcNow, 110.0));
|
||||
|
||||
TValue nanResult = rgma.Update(new TValue(DateTime.UtcNow, double.NaN));
|
||||
TValue posInfResult = rgma.Update(new TValue(DateTime.UtcNow, double.PositiveInfinity));
|
||||
TValue negInfResult = rgma.Update(new TValue(DateTime.UtcNow, double.NegativeInfinity));
|
||||
|
||||
Assert.True(double.IsFinite(nanResult.Value));
|
||||
Assert.True(double.IsFinite(posInfResult.Value));
|
||||
Assert.True(double.IsFinite(negInfResult.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_BatchMatchesStreaming()
|
||||
{
|
||||
int period = 12;
|
||||
int passes = 4;
|
||||
TSeries series = BuildSeries(250, seed: 11);
|
||||
|
||||
TSeries batch = Rgma.Batch(series, period, passes);
|
||||
var rgma = new Rgma(period, passes);
|
||||
|
||||
var streamValues = new List<double>(series.Count);
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
streamValues.Add(rgma.Update(series[i]).Value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
Assert.Equal(batch[i].Value, streamValues[i], precision: 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_SpanMatchesBatch()
|
||||
{
|
||||
int period = 16;
|
||||
int passes = 5;
|
||||
TSeries series = BuildSeries(200, seed: 21);
|
||||
double[] values = series.Values.ToArray();
|
||||
var output = new double[values.Length];
|
||||
|
||||
Rgma.Batch(values.AsSpan(), output.AsSpan(), period, passes);
|
||||
TSeries batch = Rgma.Batch(series, period, passes);
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
Assert.Equal(batch[i].Value, output[i], precision: 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_BatchSpan_ValidatesInput()
|
||||
{
|
||||
double[] source = [1, 2, 3, 4];
|
||||
double[] output = new double[4];
|
||||
double[] shortOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Rgma.Batch(source.AsSpan(), output.AsSpan(), 0, 3));
|
||||
Assert.Throws<ArgumentException>(() => Rgma.Batch(source.AsSpan(), output.AsSpan(), 10, 0));
|
||||
Assert.Throws<ArgumentException>(() => Rgma.Batch(source.AsSpan(), shortOutput.AsSpan(), 10, 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_BatchSpan_AllNonFinite_ReturnsNaNSeries()
|
||||
{
|
||||
double[] source = [double.NaN, double.PositiveInfinity, double.NegativeInfinity, double.NaN];
|
||||
double[] output = new double[source.Length];
|
||||
|
||||
Rgma.Batch(source.AsSpan(), output.AsSpan(), period: 5, passes: 3);
|
||||
|
||||
for (int i = 0; i < output.Length; i++)
|
||||
{
|
||||
Assert.True(double.IsNaN(output[i]));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rgma_Calculate_ReturnsConfiguredIndicatorAndMatchingResults()
|
||||
{
|
||||
const int period = 12;
|
||||
const int passes = 4;
|
||||
TSeries source = BuildSeries(120, seed: 33);
|
||||
|
||||
var (results, indicator) = Rgma.Calculate(source, period, passes);
|
||||
TSeries batch = Rgma.Batch(source, period, passes);
|
||||
|
||||
Assert.NotNull(indicator);
|
||||
Assert.Equal($"Rgma({period},{passes})", indicator.Name);
|
||||
Assert.Equal(period, indicator.WarmupPeriod);
|
||||
Assert.Equal(batch.Count, results.Count);
|
||||
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
Assert.Equal(batch[i].Value, results[i].Value, precision: 10);
|
||||
}
|
||||
}
|
||||
|
||||
private static TSeries BuildSeries(int count, int seed)
|
||||
{
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1, seed: seed);
|
||||
var t = new List<long>(count);
|
||||
var v = new List<double>(count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
t.Add(bar.Time);
|
||||
v.Add(bar.Close);
|
||||
}
|
||||
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Validation tests for RGMA (Recursive Gaussian Moving Average).
|
||||
/// Validates internal consistency across modes and checks the degenerate case:
|
||||
/// passes=1 reduces to EMA with alpha = 2/(period+1).
|
||||
/// </summary>
|
||||
public sealed class RgmaValidationTests : IDisposable
|
||||
{
|
||||
private readonly ValidationTestData _testData;
|
||||
private readonly ITestOutputHelper _output;
|
||||
private bool _disposed;
|
||||
|
||||
public RgmaValidationTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
_testData = new ValidationTestData();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_testData?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Passes1_MatchesEma_Batch()
|
||||
{
|
||||
int[] periods = { 5, 10, 20, 50 };
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
var rgma = new Rgma(period, passes: 1);
|
||||
var ema = new Ema(period);
|
||||
|
||||
var rgmaResult = rgma.Update(_testData.Data);
|
||||
var emaResult = ema.Update(_testData.Data);
|
||||
|
||||
int compareCount = Math.Min(200, rgmaResult.Count);
|
||||
int startIdx = rgmaResult.Count - compareCount;
|
||||
|
||||
for (int i = startIdx; i < rgmaResult.Count; i++)
|
||||
{
|
||||
Assert.Equal(emaResult[i].Value, rgmaResult[i].Value, 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
_output.WriteLine("RGMA(passes=1) Batch validated successfully against EMA");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Passes1_MatchesEma_Streaming()
|
||||
{
|
||||
int[] periods = { 5, 10, 20, 50 };
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
var rgma = new Rgma(period, passes: 1);
|
||||
var ema = new Ema(period);
|
||||
|
||||
var rgmaResults = new List<double>();
|
||||
var emaResults = new List<double>();
|
||||
|
||||
foreach (var item in _testData.Data)
|
||||
{
|
||||
rgmaResults.Add(rgma.Update(item).Value);
|
||||
emaResults.Add(ema.Update(item).Value);
|
||||
}
|
||||
|
||||
int compareCount = Math.Min(200, rgmaResults.Count);
|
||||
int startIdx = rgmaResults.Count - compareCount;
|
||||
|
||||
for (int i = startIdx; i < rgmaResults.Count; i++)
|
||||
{
|
||||
Assert.Equal(emaResults[i], rgmaResults[i], 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
_output.WriteLine("RGMA(passes=1) Streaming validated successfully against EMA");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Passes1_MatchesEma_Span()
|
||||
{
|
||||
int[] periods = { 5, 10, 20, 50 };
|
||||
double[] sourceData = _testData.RawData.ToArray();
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
double[] rgmaOutput = new double[sourceData.Length];
|
||||
double[] emaOutput = new double[sourceData.Length];
|
||||
|
||||
Rgma.Batch(sourceData.AsSpan(), rgmaOutput.AsSpan(), period, passes: 1);
|
||||
Ema.Batch(sourceData.AsSpan(), emaOutput.AsSpan(), period);
|
||||
|
||||
int compareCount = Math.Min(200, sourceData.Length);
|
||||
int startIdx = sourceData.Length - compareCount;
|
||||
|
||||
for (int i = startIdx; i < sourceData.Length; i++)
|
||||
{
|
||||
Assert.Equal(emaOutput[i], rgmaOutput[i], 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
_output.WriteLine("RGMA(passes=1) Span validated successfully against EMA");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_BatchStreamingSpan_Consistency()
|
||||
{
|
||||
int[] periods = { 5, 10, 20, 50 };
|
||||
int[] passes = { 1, 2, 3, 5 };
|
||||
|
||||
double[] sourceData = _testData.RawData.ToArray();
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
foreach (var passCount in passes)
|
||||
{
|
||||
// Batch (TSeries)
|
||||
var rgmaBatch = new Rgma(period, passCount);
|
||||
var batchResult = rgmaBatch.Update(_testData.Data);
|
||||
|
||||
// Streaming
|
||||
var rgmaStream = new Rgma(period, passCount);
|
||||
var streaming = new double[_testData.Data.Count];
|
||||
for (int i = 0; i < _testData.Data.Count; i++)
|
||||
{
|
||||
streaming[i] = rgmaStream.Update(_testData.Data[i]).Value;
|
||||
}
|
||||
|
||||
// Span
|
||||
var spanOutput = new double[sourceData.Length];
|
||||
Rgma.Batch(sourceData.AsSpan(), spanOutput.AsSpan(), period, passCount);
|
||||
|
||||
for (int i = 0; i < batchResult.Count; i++)
|
||||
{
|
||||
Assert.Equal(batchResult[i].Value, streaming[i], 1e-10);
|
||||
Assert.Equal(batchResult[i].Value, spanOutput[i], 1e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_output.WriteLine("RGMA Batch/Streaming/Span consistency validated successfully");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user