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,80 @@
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class HoltIndicatorTests
|
||||
{
|
||||
[Fact]
|
||||
public void HoltIndicator_Constructor_SetsDefaults()
|
||||
{
|
||||
var indicator = new HoltIndicator();
|
||||
|
||||
Assert.Equal(10, indicator.Period);
|
||||
Assert.Equal(0, indicator.Gamma);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
Assert.Equal("HOLT - Holt Exponential Moving Average", indicator.Name);
|
||||
Assert.False(indicator.SeparateWindow);
|
||||
Assert.True(indicator.OnBackGround);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HoltIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new HoltIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(0, HoltIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HoltIndicator_ShortName_IncludesPeriodAndSource()
|
||||
{
|
||||
var indicator = new HoltIndicator { Period = 15 };
|
||||
|
||||
Assert.Contains("HOLT", indicator.ShortName, StringComparison.Ordinal);
|
||||
Assert.Contains("15", indicator.ShortName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HoltIndicator_Initialize_CreatesInternalIndicator()
|
||||
{
|
||||
var indicator = new HoltIndicator { Period = 10, Gamma = 0.3 };
|
||||
|
||||
indicator.Initialize();
|
||||
|
||||
Assert.Single(indicator.LinesSeries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HoltIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
|
||||
{
|
||||
var indicator = new HoltIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HoltIndicator_ProcessUpdate_NewBar_ComputesValue()
|
||||
{
|
||||
var indicator = new HoltIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(1), 102, 108, 100, 106);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewBar));
|
||||
|
||||
Assert.Equal(2, indicator.LinesSeries[0].Count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class HoltTests
|
||||
{
|
||||
private readonly GBM _gbm = new(startPrice: 100, mu: 0.05, sigma: 0.5, seed: 42);
|
||||
|
||||
// === A) Constructor validation ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_ZeroPeriod_Throws()
|
||||
{
|
||||
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new Holt(0));
|
||||
Assert.Equal("period", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_NegativePeriod_Throws()
|
||||
{
|
||||
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new Holt(-1));
|
||||
Assert.Equal("period", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_GammaTooLow_Throws()
|
||||
{
|
||||
var ex = Assert.Throws<ArgumentException>(() => new Holt(10, gamma: -0.1));
|
||||
Assert.Equal("gamma", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_GammaTooHigh_Throws()
|
||||
{
|
||||
var ex = Assert.Throws<ArgumentException>(() => new Holt(10, gamma: 1.1));
|
||||
Assert.Equal("gamma", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_ValidConstruction_SetsName()
|
||||
{
|
||||
var holt = new Holt(10);
|
||||
Assert.Equal("Holt(10)", holt.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_ValidConstruction_WithGamma_SetsName()
|
||||
{
|
||||
var holt = new Holt(10, gamma: 0.3);
|
||||
Assert.Equal("Holt(10,0.30)", holt.Name);
|
||||
}
|
||||
|
||||
// === B) Basic calculation ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_Update_ReturnsTValue()
|
||||
{
|
||||
var holt = new Holt(10);
|
||||
TValue result = holt.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
Assert.True(double.IsFinite(result.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_FirstBar_ReturnsInput()
|
||||
{
|
||||
var holt = new Holt(10);
|
||||
TValue result = holt.Update(new TValue(DateTime.UtcNow, 42.0));
|
||||
Assert.Equal(42.0, result.Value, 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Last_IsAccessible()
|
||||
{
|
||||
var holt = new Holt(10);
|
||||
holt.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
Assert.True(double.IsFinite(holt.Last.Value));
|
||||
}
|
||||
|
||||
// === C) State + bar correction ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_IsNew_True_AdvancesState()
|
||||
{
|
||||
var holt = new Holt(5);
|
||||
var bars = _gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holt.Update(series[i]);
|
||||
}
|
||||
|
||||
double valueAfterAll = holt.Last.Value;
|
||||
holt.Update(new TValue(DateTime.UtcNow, 999.0), isNew: true);
|
||||
Assert.NotEqual(valueAfterAll, holt.Last.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_IsNew_False_RollsBack()
|
||||
{
|
||||
var holt = new Holt(5);
|
||||
var bars = _gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holt.Update(series[i]);
|
||||
}
|
||||
|
||||
double baseline = holt.Last.Value;
|
||||
holt.Update(new TValue(DateTime.UtcNow, 999.0), isNew: false);
|
||||
_ = holt.Last.Value;
|
||||
|
||||
// Correction should produce a different value from baseline (999 != last close)
|
||||
// But the state should have been rolled back first
|
||||
holt.Update(series[^1], isNew: false);
|
||||
Assert.Equal(baseline, holt.Last.Value, 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_IterativeCorrections_Restore()
|
||||
{
|
||||
var holt = new Holt(5);
|
||||
var bars = _gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
for (int i = 0; i < series.Count - 1; i++)
|
||||
{
|
||||
holt.Update(series[i]);
|
||||
}
|
||||
|
||||
// Feed last bar as new
|
||||
holt.Update(series[^1], isNew: true);
|
||||
double expected = holt.Last.Value;
|
||||
|
||||
// Correct it multiple times
|
||||
for (int j = 0; j < 5; j++)
|
||||
{
|
||||
holt.Update(series[^1], isNew: false);
|
||||
}
|
||||
|
||||
Assert.Equal(expected, holt.Last.Value, 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Reset_ClearsState()
|
||||
{
|
||||
var holt = new Holt(10);
|
||||
var bars = _gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holt.Update(series[i]);
|
||||
}
|
||||
|
||||
Assert.True(holt.IsHot);
|
||||
holt.Reset();
|
||||
Assert.False(holt.IsHot);
|
||||
Assert.Equal(default, holt.Last);
|
||||
}
|
||||
|
||||
// === D) Warmup/convergence ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_IsHot_FlipsAtWarmup()
|
||||
{
|
||||
var holt = new Holt(10);
|
||||
var bars = _gbm.Fetch(20, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
holt.Update(series[i]);
|
||||
Assert.False(holt.IsHot);
|
||||
}
|
||||
|
||||
holt.Update(series[9]);
|
||||
Assert.True(holt.IsHot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_WarmupPeriod_MatchesPeriod()
|
||||
{
|
||||
var holt = new Holt(15);
|
||||
Assert.Equal(15, holt.WarmupPeriod);
|
||||
}
|
||||
|
||||
// === E) Robustness ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_NaN_UsesLastValid()
|
||||
{
|
||||
var holt = new Holt(5);
|
||||
holt.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
holt.Update(new TValue(DateTime.UtcNow, 101.0));
|
||||
_ = holt.Last.Value;
|
||||
|
||||
holt.Update(new TValue(DateTime.UtcNow, double.NaN));
|
||||
Assert.True(double.IsFinite(holt.Last.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Infinity_UsesLastValid()
|
||||
{
|
||||
var holt = new Holt(5);
|
||||
holt.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
holt.Update(new TValue(DateTime.UtcNow, 101.0));
|
||||
|
||||
holt.Update(new TValue(DateTime.UtcNow, double.PositiveInfinity));
|
||||
Assert.True(double.IsFinite(holt.Last.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_BatchNaN_Safe()
|
||||
{
|
||||
double[] src = [100, double.NaN, 102, double.NaN, 104];
|
||||
double[] dst = new double[5];
|
||||
Holt.Batch(src, dst, 3);
|
||||
for (int i = 0; i < dst.Length; i++)
|
||||
{
|
||||
Assert.True(double.IsFinite(dst[i]), $"dst[{i}] is not finite");
|
||||
}
|
||||
}
|
||||
|
||||
// === F) Consistency (4 modes) ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_AllModes_Match()
|
||||
{
|
||||
var bars = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
int period = 10;
|
||||
|
||||
// Mode 1: Streaming
|
||||
var holtStream = new Holt(period);
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holtStream.Update(series[i]);
|
||||
}
|
||||
double streamResult = holtStream.Last.Value;
|
||||
|
||||
// Mode 2: Batch TSeries
|
||||
TSeries batchResult = Holt.Batch(series, period);
|
||||
double batchLast = batchResult[^1].Value;
|
||||
|
||||
// Mode 3: Span
|
||||
double[] src = new double[series.Count];
|
||||
double[] dst = new double[series.Count];
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
src[i] = series[i].Value;
|
||||
}
|
||||
Holt.Batch(src, dst, period);
|
||||
double spanLast = dst[^1];
|
||||
|
||||
// Mode 4: Event-based
|
||||
var holtEvent = new Holt(period);
|
||||
double eventResult = 0;
|
||||
holtEvent.Pub += (object? s, in TValueEventArgs e) => { eventResult = e.Value.Value; };
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holtEvent.Update(series[i]);
|
||||
}
|
||||
|
||||
Assert.Equal(streamResult, batchLast, 10);
|
||||
Assert.Equal(streamResult, spanLast, 10);
|
||||
Assert.Equal(streamResult, eventResult, 10);
|
||||
}
|
||||
|
||||
// === G) Span API tests ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_Span_MismatchedLength_Throws()
|
||||
{
|
||||
double[] src = [1, 2, 3];
|
||||
double[] dst = new double[2];
|
||||
var ex = Assert.Throws<ArgumentException>(() => Holt.Batch(src, dst, 5));
|
||||
Assert.Equal("output", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Span_EmptyInput_NoOutput()
|
||||
{
|
||||
double[] src = [];
|
||||
double[] dst = [];
|
||||
Holt.Batch(src, dst, 5);
|
||||
Assert.Empty(dst);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Span_ZeroPeriod_Throws()
|
||||
{
|
||||
double[] src = [1, 2, 3];
|
||||
double[] dst = new double[3];
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Holt.Batch(src, dst, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Span_MatchesTSeries()
|
||||
{
|
||||
var bars = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
int period = 10;
|
||||
|
||||
TSeries bts = Holt.Batch(series, period);
|
||||
|
||||
double[] src = new double[series.Count];
|
||||
double[] dst = new double[series.Count];
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
src[i] = series[i].Value;
|
||||
}
|
||||
Holt.Batch(src, dst, period);
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
Assert.Equal(bts[i].Value, dst[i], 10);
|
||||
}
|
||||
}
|
||||
|
||||
// === H) Chainability ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_Pub_Fires()
|
||||
{
|
||||
var holt = new Holt(5);
|
||||
int count = 0;
|
||||
holt.Pub += (object? s, in TValueEventArgs e) => { count++; };
|
||||
|
||||
holt.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
holt.Update(new TValue(DateTime.UtcNow, 101.0));
|
||||
Assert.Equal(2, count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_EventChaining_Works()
|
||||
{
|
||||
var holt1 = new Holt(5);
|
||||
var holt2 = new Holt(holt1, 3);
|
||||
|
||||
holt1.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
Assert.True(double.IsFinite(holt2.Last.Value));
|
||||
}
|
||||
|
||||
// === Holt-specific tests ===
|
||||
|
||||
[Fact]
|
||||
public void Holt_ConstantInput_ConvergesToLevel()
|
||||
{
|
||||
var holt = new Holt(10);
|
||||
double constant = 50.0;
|
||||
|
||||
for (int i = 0; i < 200; i++)
|
||||
{
|
||||
holt.Update(new TValue(DateTime.UtcNow, constant));
|
||||
}
|
||||
|
||||
// With constant input, trend -> 0, level -> constant, output -> constant
|
||||
Assert.Equal(constant, holt.Last.Value, 6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Calculate_ReturnsHotInstance()
|
||||
{
|
||||
var bars = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var (results, indicator) = Holt.Calculate(bars.Close, 10);
|
||||
|
||||
Assert.True(indicator.IsHot);
|
||||
Assert.Equal(100, results.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_Prime_SetsState()
|
||||
{
|
||||
var bars = _gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
var holtPrimed = new Holt(10);
|
||||
double[] values = new double[series.Count];
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
values[i] = series[i].Value;
|
||||
}
|
||||
holtPrimed.Prime(values);
|
||||
|
||||
var holtStreamed = new Holt(10);
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holtStreamed.Update(series[i]);
|
||||
}
|
||||
|
||||
Assert.Equal(holtStreamed.Last.Value, holtPrimed.Last.Value, 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_GammaZero_EqualsAutoGamma()
|
||||
{
|
||||
var bars = _gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
var holt0 = new Holt(10, gamma: 0);
|
||||
var holtAuto = new Holt(10);
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holt0.Update(series[i]);
|
||||
holtAuto.Update(series[i]);
|
||||
}
|
||||
|
||||
Assert.Equal(holt0.Last.Value, holtAuto.Last.Value, 15);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Holt_DifferentGamma_ProducesDifferentOutput()
|
||||
{
|
||||
var bars = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
var holt1 = new Holt(10, gamma: 0.1);
|
||||
var holt2 = new Holt(10, gamma: 0.9);
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holt1.Update(series[i]);
|
||||
holt2.Update(series[i]);
|
||||
}
|
||||
|
||||
Assert.NotEqual(holt1.Last.Value, holt2.Last.Value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class HoltValidationTests
|
||||
{
|
||||
private readonly GBM _gbm = new(startPrice: 100, mu: 0.05, sigma: 0.5, seed: 42);
|
||||
private readonly TSeries _series;
|
||||
|
||||
public HoltValidationTests()
|
||||
{
|
||||
var bars = _gbm.Fetch(5000, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
_series = bars.Close;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates against holt.pine reference implementation logic.
|
||||
/// Manually computes Holt using the same equations.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Holt_MatchesPineScriptReference()
|
||||
{
|
||||
int period = 10;
|
||||
double alpha = 2.0 / (period + 1.0);
|
||||
double gamma = alpha; // gamma=0 means use alpha
|
||||
var holt = new Holt(period);
|
||||
|
||||
double level = 0;
|
||||
double trend = 0;
|
||||
bool initialized = false;
|
||||
|
||||
for (int i = 0; i < _series.Count; i++)
|
||||
{
|
||||
holt.Update(_series[i]);
|
||||
double src = _series[i].Value;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
level = src;
|
||||
trend = 0;
|
||||
initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
double prevLevel = level;
|
||||
level = (alpha * src) + ((1.0 - alpha) * (prevLevel + trend));
|
||||
trend = (gamma * (level - prevLevel)) + ((1.0 - gamma) * trend);
|
||||
}
|
||||
|
||||
double expected = initialized && i > 0 ? level + trend : src;
|
||||
Assert.Equal(expected, holt.Last.Value, 9);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that constant input converges to the constant value.
|
||||
/// Level → constant, trend → 0, output → constant.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Holt_ConstantInput_ConvergesToValue()
|
||||
{
|
||||
double constant = 75.0;
|
||||
var holt = new Holt(20);
|
||||
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
holt.Update(new TValue(DateTime.UtcNow, constant));
|
||||
}
|
||||
|
||||
Assert.Equal(constant, holt.Last.Value, 6);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates deterministic output with same seed.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Holt_Deterministic_SameSeed()
|
||||
{
|
||||
int period = 10;
|
||||
|
||||
var holt1 = new Holt(period);
|
||||
var holt2 = new Holt(period);
|
||||
|
||||
for (int i = 0; i < _series.Count; i++)
|
||||
{
|
||||
holt1.Update(_series[i]);
|
||||
holt2.Update(_series[i]);
|
||||
}
|
||||
|
||||
Assert.Equal(holt1.Last.Value, holt2.Last.Value, 15);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that batch and streaming produce identical results.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Holt_BatchAndStreaming_Match()
|
||||
{
|
||||
int period = 15;
|
||||
|
||||
var holtStream = new Holt(period);
|
||||
for (int i = 0; i < _series.Count; i++)
|
||||
{
|
||||
holtStream.Update(_series[i]);
|
||||
}
|
||||
|
||||
TSeries batchResult = Holt.Batch(_series, period);
|
||||
|
||||
Assert.Equal(holtStream.Last.Value, batchResult[^1].Value, 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that different gamma values produce different outputs.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Holt_DifferentGamma_DifferentOutputs()
|
||||
{
|
||||
var bars = _gbm.Fetch(300, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
var holt1 = new Holt(10, gamma: 0.1);
|
||||
var holt2 = new Holt(10, gamma: 0.9);
|
||||
|
||||
int differenceCount = 0;
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holt1.Update(series[i]);
|
||||
holt2.Update(series[i]);
|
||||
|
||||
if (i > 20)
|
||||
{
|
||||
double diff = Math.Abs(holt1.Last.Value - holt2.Last.Value);
|
||||
if (diff > 1e-10)
|
||||
{
|
||||
differenceCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(differenceCount > 100, $"Expected >100 different values, got {differenceCount}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that different periods produce different outputs.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Holt_DifferentPeriods_DifferentOutputs()
|
||||
{
|
||||
var bars = _gbm.Fetch(300, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
var holt5 = new Holt(5);
|
||||
var holt50 = new Holt(50);
|
||||
|
||||
int differenceCount = 0;
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
holt5.Update(series[i]);
|
||||
holt50.Update(series[i]);
|
||||
|
||||
if (i > 50)
|
||||
{
|
||||
double diff = Math.Abs(holt5.Last.Value - holt50.Last.Value);
|
||||
if (diff > 1e-10)
|
||||
{
|
||||
differenceCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(differenceCount > 100, $"Expected >100 different values, got {differenceCount}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user