mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 20:48:04 +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,361 @@
|
||||
using Xunit;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class SigmoidTests
|
||||
{
|
||||
private readonly GBM _gbm = new(1000, 0.05, 0.2, seed: 100);
|
||||
private const double Epsilon = 1e-10;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Constructor Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithDefaultParameters_SetsCorrectName()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
Assert.Equal("Sigmoid(1.00,0.00)", sigmoid.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithCustomParameters_SetsCorrectName()
|
||||
{
|
||||
var sigmoid = new Sigmoid(k: 0.5, x0: 100.0);
|
||||
Assert.Equal("Sigmoid(0.50,100.00)", sigmoid.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithZeroK_ThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new Sigmoid(k: 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithNegativeK_ThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new Sigmoid(k: -1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WarmupPeriod_IsZero()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
Assert.Equal(0, sigmoid.WarmupPeriod);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Basic Update Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Update_ReturnsTValue()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
var input = new TValue(DateTime.UtcNow, 0.0);
|
||||
|
||||
var result = sigmoid.Update(input);
|
||||
|
||||
Assert.IsType<TValue>(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_AtMidpoint_ReturnsHalf()
|
||||
{
|
||||
var sigmoid = new Sigmoid(k: 1.0, x0: 0.0);
|
||||
var input = new TValue(DateTime.UtcNow, 0.0);
|
||||
|
||||
var result = sigmoid.Update(input);
|
||||
|
||||
Assert.Equal(0.5, result.Value, Epsilon);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_AtMidpointWithOffset_ReturnsHalf()
|
||||
{
|
||||
var sigmoid = new Sigmoid(k: 1.0, x0: 100.0);
|
||||
var input = new TValue(DateTime.UtcNow, 100.0);
|
||||
|
||||
var result = sigmoid.Update(input);
|
||||
|
||||
Assert.Equal(0.5, result.Value, Epsilon);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_Last_IsUpdated()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
var input = new TValue(DateTime.UtcNow, 1.0);
|
||||
|
||||
sigmoid.Update(input);
|
||||
|
||||
Assert.Equal(input.Time, sigmoid.Last.Time);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_IsHot_IsAlwaysTrue()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
Assert.True(sigmoid.IsHot);
|
||||
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 0.0));
|
||||
Assert.True(sigmoid.IsHot);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// isNew State Management Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Update_WithIsNewTrue_AdvancesState()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
var input1 = new TValue(DateTime.UtcNow, 1.0);
|
||||
var input2 = new TValue(DateTime.UtcNow.AddSeconds(1), 2.0);
|
||||
|
||||
var result1 = sigmoid.Update(input1, isNew: true);
|
||||
var result2 = sigmoid.Update(input2, isNew: true);
|
||||
|
||||
Assert.NotEqual(result1.Value, result2.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_WithIsNewFalse_ReplacesCurrentBar()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
var input1 = new TValue(DateTime.UtcNow, 1.0);
|
||||
var input2 = new TValue(DateTime.UtcNow, 2.0);
|
||||
|
||||
sigmoid.Update(input1, isNew: true);
|
||||
var result = sigmoid.Update(input2, isNew: false);
|
||||
|
||||
Assert.Equal(sigmoid.Last.Value, result.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_IterativeCorrections_RestoreState()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
|
||||
// Initial value
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 1.0), isNew: true);
|
||||
double afterFirst = sigmoid.Last.Value;
|
||||
|
||||
// Multiple corrections (isNew = false)
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 2.0), isNew: false);
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 3.0), isNew: false);
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 1.0), isNew: false);
|
||||
|
||||
// Should restore to same state as after first update with same input
|
||||
Assert.Equal(afterFirst, sigmoid.Last.Value, Epsilon);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// NaN/Infinity Handling Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Update_NaN_UsesLastValidValue()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 1.0), isNew: true);
|
||||
double lastValid = sigmoid.Last.Value;
|
||||
|
||||
var nanResult = sigmoid.Update(new TValue(DateTime.UtcNow.AddSeconds(1), double.NaN), isNew: true);
|
||||
|
||||
Assert.Equal(lastValid, nanResult.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_PositiveInfinity_UsesLastValidValue()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 0.0), isNew: true);
|
||||
double lastValid = sigmoid.Last.Value;
|
||||
|
||||
var infResult = sigmoid.Update(new TValue(DateTime.UtcNow.AddSeconds(1), double.PositiveInfinity), isNew: true);
|
||||
|
||||
Assert.Equal(lastValid, infResult.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_NegativeInfinity_UsesLastValidValue()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 0.0), isNew: true);
|
||||
double lastValid = sigmoid.Last.Value;
|
||||
|
||||
var infResult = sigmoid.Update(new TValue(DateTime.UtcNow.AddSeconds(1), double.NegativeInfinity), isNew: true);
|
||||
|
||||
Assert.Equal(lastValid, infResult.Value);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Reset Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Reset_ClearsState()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
|
||||
sigmoid.Update(new TValue(DateTime.UtcNow, 1.0), isNew: true);
|
||||
sigmoid.Reset();
|
||||
|
||||
Assert.Equal(default, sigmoid.Last);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// TSeries Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Update_TSeries_ReturnsCorrectLength()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
var series = new TSeries();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
series.Add(new TValue(DateTime.UtcNow.AddSeconds(i), i - 50), isNew: true);
|
||||
}
|
||||
|
||||
var result = sigmoid.Update(series);
|
||||
|
||||
Assert.Equal(series.Count, result.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Calculate_TSeries_ReturnsCorrectLength()
|
||||
{
|
||||
var series = new TSeries();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
series.Add(new TValue(DateTime.UtcNow.AddSeconds(i), i - 50), isNew: true);
|
||||
}
|
||||
|
||||
var result = Sigmoid.Batch(series);
|
||||
|
||||
Assert.Equal(series.Count, result.Count);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Span API Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Calculate_Span_EmptySource_ThrowsArgumentException()
|
||||
{
|
||||
double[] output = new double[10];
|
||||
Assert.Throws<ArgumentException>(() => Sigmoid.Batch(ReadOnlySpan<double>.Empty, output.AsSpan()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Calculate_Span_OutputTooSmall_ThrowsArgumentException()
|
||||
{
|
||||
double[] source = [1, 2, 3, 4, 5];
|
||||
double[] output = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Sigmoid.Batch(source.AsSpan(), output.AsSpan()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Calculate_Span_InvalidK_ThrowsArgumentException()
|
||||
{
|
||||
double[] source = [1, 2, 3, 4, 5];
|
||||
double[] output = new double[5];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Sigmoid.Batch(source.AsSpan(), output.AsSpan(), k: 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Calculate_Span_MatchesStreaming()
|
||||
{
|
||||
double[] source = new GBM(seed: 42).Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).CloseValues.ToArray();
|
||||
|
||||
double[] spanOutput = new double[source.Length];
|
||||
Sigmoid.Batch(source.AsSpan(), spanOutput.AsSpan());
|
||||
|
||||
var sigmoid = new Sigmoid();
|
||||
double[] streamOutput = new double[source.Length];
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
streamOutput[i] = sigmoid.Update(new TValue(DateTime.UtcNow.AddSeconds(i), source[i]), true).Value;
|
||||
}
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
Assert.Equal(streamOutput[i], spanOutput[i], Epsilon);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Calculate_Span_HandlesNaN()
|
||||
{
|
||||
double[] source = [1.0, double.NaN, 2.0];
|
||||
double[] output = new double[3];
|
||||
|
||||
Sigmoid.Batch(source.AsSpan(), output.AsSpan());
|
||||
|
||||
Assert.True(double.IsFinite(output[0]));
|
||||
Assert.True(double.IsFinite(output[1])); // NaN replaced with last valid
|
||||
Assert.True(double.IsFinite(output[2]));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Chaining Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Chaining_PublishesEvents()
|
||||
{
|
||||
var source = new TSeries();
|
||||
var sigmoid = new Sigmoid(source);
|
||||
int eventCount = 0;
|
||||
|
||||
sigmoid.Pub += (_, in _) => eventCount++;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
source.Add(new TValue(DateTime.UtcNow.AddSeconds(i), i), isNew: true);
|
||||
}
|
||||
|
||||
Assert.Equal(10, eventCount);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Steepness Parameter Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Update_HigherK_CreatesSteeperTransition()
|
||||
{
|
||||
var sigmoidLow = new Sigmoid(k: 0.5);
|
||||
var sigmoidHigh = new Sigmoid(k: 5.0);
|
||||
|
||||
// At x=1, higher k should give value closer to 1
|
||||
var resultLow = sigmoidLow.Update(new TValue(DateTime.UtcNow, 1.0));
|
||||
var resultHigh = sigmoidHigh.Update(new TValue(DateTime.UtcNow, 1.0));
|
||||
|
||||
Assert.True(resultHigh.Value > resultLow.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_DifferentX0_ShiftsMidpoint()
|
||||
{
|
||||
var sigmoid0 = new Sigmoid(k: 1.0, x0: 0.0);
|
||||
var sigmoid100 = new Sigmoid(k: 1.0, x0: 100.0);
|
||||
|
||||
// At x=0, sigmoid with x0=0 should be 0.5
|
||||
var result0 = sigmoid0.Update(new TValue(DateTime.UtcNow, 0.0));
|
||||
// At x=100, sigmoid with x0=100 should be 0.5
|
||||
var result100 = sigmoid100.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
|
||||
Assert.Equal(0.5, result0.Value, Epsilon);
|
||||
Assert.Equal(0.5, result100.Value, Epsilon);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
using Xunit;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Validation tests for Sigmoid indicator against mathematical properties.
|
||||
/// Sigmoid has no direct external library equivalents, so we validate against
|
||||
/// the mathematical definition: S(x) = 1 / (1 + exp(-k * (x - x0)))
|
||||
/// </summary>
|
||||
public class SigmoidValidationTests
|
||||
{
|
||||
private const double Epsilon = 1e-10;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Mathematical Definition Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Theory]
|
||||
[InlineData(0.0, 1.0, 0.0)] // S(0) with k=1, x0=0
|
||||
[InlineData(1.0, 1.0, 0.0)] // S(1) with k=1, x0=0
|
||||
[InlineData(-1.0, 1.0, 0.0)] // S(-1) with k=1, x0=0
|
||||
[InlineData(5.0, 1.0, 0.0)] // S(5) with k=1, x0=0
|
||||
[InlineData(-5.0, 1.0, 0.0)] // S(-5) with k=1, x0=0
|
||||
[InlineData(0.0, 2.0, 0.0)] // Different steepness
|
||||
[InlineData(100.0, 1.0, 100.0)] // Shifted midpoint
|
||||
public void Sigmoid_MatchesMathematicalDefinition(double x, double k, double x0)
|
||||
{
|
||||
var sigmoid = new Sigmoid(k, x0);
|
||||
var result = sigmoid.Update(new TValue(DateTime.UtcNow, x));
|
||||
|
||||
// Mathematical definition: S(x) = 1 / (1 + exp(-k * (x - x0)))
|
||||
double expected = 1.0 / (1.0 + Math.Exp(-k * (x - x0)));
|
||||
|
||||
Assert.Equal(expected, result.Value, Epsilon);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Symmetry Property Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Theory]
|
||||
[InlineData(1.0)]
|
||||
[InlineData(2.0)]
|
||||
[InlineData(5.0)]
|
||||
[InlineData(10.0)]
|
||||
public void Sigmoid_Symmetry_AroundMidpoint(double offset)
|
||||
{
|
||||
// Property: S(x0 + d) + S(x0 - d) = 1
|
||||
var sigmoid = new Sigmoid(k: 1.0, x0: 0.0);
|
||||
|
||||
var resultPlus = sigmoid.Update(new TValue(DateTime.UtcNow, offset));
|
||||
sigmoid.Reset();
|
||||
var resultMinus = sigmoid.Update(new TValue(DateTime.UtcNow, -offset));
|
||||
|
||||
Assert.Equal(1.0, resultPlus.Value + resultMinus.Value, Epsilon);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Midpoint Property Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Theory]
|
||||
[InlineData(0.0)]
|
||||
[InlineData(50.0)]
|
||||
[InlineData(-50.0)]
|
||||
[InlineData(100.0)]
|
||||
public void Sigmoid_AtMidpoint_ReturnsHalf(double x0)
|
||||
{
|
||||
// Property: S(x0) = 0.5 for any x0
|
||||
var sigmoid = new Sigmoid(k: 1.0, x0: x0);
|
||||
var result = sigmoid.Update(new TValue(DateTime.UtcNow, x0));
|
||||
|
||||
Assert.Equal(0.5, result.Value, Epsilon);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Range Property Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Sigmoid_OutputAlwaysBetweenZeroAndOne()
|
||||
{
|
||||
var sigmoid = new Sigmoid();
|
||||
var bars = new GBM(seed: 42).Fetch(1000, DateTime.UtcNow.Ticks, TimeSpan.FromSeconds(1));
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
var result = sigmoid.Update(bars.Close[i], true);
|
||||
|
||||
Assert.True(result.Value >= 0.0, $"Output {result.Value} should be >= 0 for input {bars.Close[i].Value}");
|
||||
Assert.True(result.Value <= 1.0, $"Output {result.Value} should be <= 1 for input {bars.Close[i].Value}");
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Monotonicity Property Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Sigmoid_IsStrictlyIncreasing()
|
||||
{
|
||||
// Property: if x1 < x2 then S(x1) < S(x2)
|
||||
var sigmoid = new Sigmoid();
|
||||
|
||||
double prevValue = double.NegativeInfinity;
|
||||
for (double x = -10; x <= 10; x += 0.5)
|
||||
{
|
||||
sigmoid.Reset();
|
||||
var result = sigmoid.Update(new TValue(DateTime.UtcNow, x));
|
||||
|
||||
Assert.True(result.Value > prevValue, $"S({x}) = {result.Value} should be > {prevValue}");
|
||||
prevValue = result.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Steepness Property Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Sigmoid_HigherK_SteeperTransition()
|
||||
{
|
||||
// At x = x0 + 1, higher k should produce values closer to 1
|
||||
double x = 1.0;
|
||||
|
||||
var sigmoidK1 = new Sigmoid(k: 1.0);
|
||||
var sigmoidK5 = new Sigmoid(k: 5.0);
|
||||
var sigmoidK10 = new Sigmoid(k: 10.0);
|
||||
|
||||
var result1 = sigmoidK1.Update(new TValue(DateTime.UtcNow, x));
|
||||
var result5 = sigmoidK5.Update(new TValue(DateTime.UtcNow, x));
|
||||
var result10 = sigmoidK10.Update(new TValue(DateTime.UtcNow, x));
|
||||
|
||||
Assert.True(result10.Value > result5.Value);
|
||||
Assert.True(result5.Value > result1.Value);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Derivative Property Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Sigmoid_DerivativeMaximumAtMidpoint()
|
||||
{
|
||||
// Property: The derivative of sigmoid is maximum at x0
|
||||
// S'(x) = k * S(x) * (1 - S(x))
|
||||
// At x0, S(x0) = 0.5, so S'(x0) = k * 0.5 * 0.5 = k/4
|
||||
double k = 2.0;
|
||||
var sigmoid = new Sigmoid(k: k, x0: 0.0);
|
||||
|
||||
// Numerical derivative using central difference
|
||||
double h = 0.0001;
|
||||
sigmoid.Reset();
|
||||
double sPlus = sigmoid.Update(new TValue(DateTime.UtcNow, h)).Value;
|
||||
sigmoid.Reset();
|
||||
double sMinus = sigmoid.Update(new TValue(DateTime.UtcNow, -h)).Value;
|
||||
|
||||
double numericalDerivative = (sPlus - sMinus) / (2 * h);
|
||||
double expectedDerivative = k / 4.0;
|
||||
|
||||
Assert.Equal(expectedDerivative, numericalDerivative, 1e-4);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Limit Property Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Sigmoid_ApproachesOneForLargePositive()
|
||||
{
|
||||
// lim(x→∞) S(x) = 1
|
||||
var sigmoid = new Sigmoid();
|
||||
var result = sigmoid.Update(new TValue(DateTime.UtcNow, 100.0));
|
||||
|
||||
Assert.True(result.Value > 0.99999);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sigmoid_ApproachesZeroForLargeNegative()
|
||||
{
|
||||
// lim(x→-∞) S(x) = 0
|
||||
var sigmoid = new Sigmoid();
|
||||
var result = sigmoid.Update(new TValue(DateTime.UtcNow, -100.0));
|
||||
|
||||
Assert.True(result.Value < 0.00001);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Inverse Relationship Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Theory]
|
||||
[InlineData(0.1)]
|
||||
[InlineData(0.25)]
|
||||
[InlineData(0.5)]
|
||||
[InlineData(0.75)]
|
||||
[InlineData(0.9)]
|
||||
public void Sigmoid_InverseIsLogit(double y)
|
||||
{
|
||||
// Logit(y) = ln(y / (1-y)) = x (inverse of sigmoid with k=1, x0=0)
|
||||
var sigmoid = new Sigmoid(k: 1.0, x0: 0.0);
|
||||
|
||||
// Calculate x from y using logit
|
||||
double x = Math.Log(y / (1 - y));
|
||||
|
||||
// Sigmoid of x should give y
|
||||
var result = sigmoid.Update(new TValue(DateTime.UtcNow, x));
|
||||
|
||||
Assert.Equal(y, result.Value, Epsilon);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// Span vs Streaming Consistency Tests
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Sigmoid_SpanAndStreaming_ProduceSameResults()
|
||||
{
|
||||
double k = 0.5;
|
||||
double x0 = 50.0;
|
||||
double[] source = new GBM(seed: 42).Fetch(500, DateTime.UtcNow.Ticks, TimeSpan.FromSeconds(1)).CloseValues.ToArray();
|
||||
|
||||
// Span calculation
|
||||
double[] spanOutput = new double[source.Length];
|
||||
Sigmoid.Batch(source.AsSpan(), spanOutput.AsSpan(), k, x0);
|
||||
|
||||
// Streaming calculation
|
||||
var sigmoid = new Sigmoid(k, x0);
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
var result = sigmoid.Update(new TValue(DateTime.UtcNow.AddSeconds(i), source[i]), true);
|
||||
Assert.Equal(spanOutput[i], result.Value, Epsilon);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user