diff --git a/lib/averages/dema/Dema.cs b/lib/averages/dema/Dema.cs index 880c4984..d898720e 100644 --- a/lib/averages/dema/Dema.cs +++ b/lib/averages/dema/Dema.cs @@ -24,7 +24,7 @@ namespace QuanTAlib; [SkipLocalsInit] public sealed class Dema : ITValuePublisher { - private struct EmaState + private struct EmaState : IEquatable { public double Ema; public double E; @@ -32,6 +32,20 @@ public sealed class Dema : ITValuePublisher public bool IsCompensated; public static EmaState New() => new() { Ema = 0, E = 1.0, IsHot = false, IsCompensated = false }; + + public override bool Equals(object? obj) => obj is EmaState other && Equals(other); + + public bool Equals(EmaState other) => + Ema == other.Ema && + E == other.E && + IsHot == other.IsHot && + IsCompensated == other.IsCompensated; + + public override int GetHashCode() => HashCode.Combine(Ema, E, IsHot, IsCompensated); + + public static bool operator ==(EmaState left, EmaState right) => left.Equals(right); + + public static bool operator !=(EmaState left, EmaState right) => !left.Equals(right); } private readonly double _alpha; diff --git a/lib/averages/ema/Ema.cs b/lib/averages/ema/Ema.cs index 13edbe7e..af98805f 100644 --- a/lib/averages/ema/Ema.cs +++ b/lib/averages/ema/Ema.cs @@ -27,7 +27,7 @@ namespace QuanTAlib; [SkipLocalsInit] public sealed class Ema : ITValuePublisher { - private struct State + private struct State : IEquatable { public double Ema; public double E; @@ -35,6 +35,20 @@ public sealed class Ema : ITValuePublisher public bool IsCompensated; public static State New() => new() { Ema = 0, E = 1.0, IsHot = false, IsCompensated = false }; + + public override bool Equals(object? obj) => obj is State other && Equals(other); + + public bool Equals(State other) => + Ema == other.Ema && + E == other.E && + IsHot == other.IsHot && + IsCompensated == other.IsCompensated; + + public override int GetHashCode() => HashCode.Combine(Ema, E, IsHot, IsCompensated); + + public static bool operator ==(State left, State right) => left.Equals(right); + + public static bool operator !=(State left, State right) => !left.Equals(right); } private readonly double _alpha; diff --git a/lib/averages/hma/Hma.Tests.cs b/lib/averages/hma/Hma.Tests.cs index 64d7ea40..f50bff4f 100644 --- a/lib/averages/hma/Hma.Tests.cs +++ b/lib/averages/hma/Hma.Tests.cs @@ -37,9 +37,9 @@ public class HmaTests // IsHot is defined as Full.IsHot && Sqrt.IsHot. // Full becomes hot after 9 updates. // Sqrt becomes hot after 3 updates. - // So HMA should be hot after 9 updates. + // So HMA should be hot after 9 + 3 - 1 = 11 updates. - for (int i = 0; i < 8; i++) + for (int i = 0; i < 10; i++) { hma.Update(new TValue(DateTime.UtcNow, 100)); Assert.False(hma.IsHot); diff --git a/lib/averages/hma/Hma.cs b/lib/averages/hma/Hma.cs index d8552d02..93157a12 100644 --- a/lib/averages/hma/Hma.cs +++ b/lib/averages/hma/Hma.cs @@ -21,13 +21,15 @@ namespace QuanTAlib; public sealed class Hma : ITValuePublisher { private readonly int _period; + private readonly int _sqrtPeriod; private readonly Wma _wmaFull; private readonly Wma _wmaHalf; private readonly Wma _wmaSqrt; + private int _sampleCount; public string Name { get; } public TValue Last { get; private set; } - public bool IsHot => _wmaFull.IsHot && _wmaSqrt.IsHot; + public bool IsHot => _sampleCount >= _period + _sqrtPeriod - 1; public event Action? Pub; public Hma(int period) @@ -36,11 +38,11 @@ public sealed class Hma : ITValuePublisher _period = period; int halfPeriod = period / 2; - int sqrtPeriod = (int)Math.Sqrt(period); + _sqrtPeriod = (int)Math.Sqrt(period); _wmaFull = new Wma(period); _wmaHalf = new Wma(halfPeriod); - _wmaSqrt = new Wma(sqrtPeriod); + _wmaSqrt = new Wma(_sqrtPeriod); Name = $"Hma({period})"; } @@ -53,6 +55,8 @@ public sealed class Hma : ITValuePublisher [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue Update(TValue input, bool isNew = true) { + if (isNew) _sampleCount++; + // 1. Calculate WMA(n) TValue full = _wmaFull.Update(input, isNew); @@ -188,6 +192,7 @@ public sealed class Hma : ITValuePublisher _wmaFull.Reset(); _wmaHalf.Reset(); _wmaSqrt.Reset(); + _sampleCount = 0; Last = default; } } diff --git a/lib/averages/t3/T3.cs b/lib/averages/t3/T3.cs index cec59070..36e79027 100644 --- a/lib/averages/t3/T3.cs +++ b/lib/averages/t3/T3.cs @@ -26,15 +26,28 @@ namespace QuanTAlib; [SkipLocalsInit] public sealed class T3 : ITValuePublisher { - private struct State + private struct State : IEquatable { public double E1, E2, E3, E4, E5, E6; public bool IsInitialized; public static State New() => new() { IsInitialized = false }; + + public override bool Equals(object? obj) => obj is State other && Equals(other); + + public bool Equals(State other) => + E1 == other.E1 && E2 == other.E2 && E3 == other.E3 && + E4 == other.E4 && E5 == other.E5 && E6 == other.E6 && + IsInitialized == other.IsInitialized; + + public override int GetHashCode() => HashCode.Combine(E1, E2, E3, E4, E5, E6, IsInitialized); + + public static bool operator ==(State left, State right) => left.Equals(right); + + public static bool operator !=(State left, State right) => !left.Equals(right); } - private readonly struct Parameters + private readonly struct Parameters : IEquatable { public readonly double Alpha; public readonly double C1, C2, C3, C4; @@ -47,6 +60,19 @@ public sealed class T3 : ITValuePublisher C3 = c3; C4 = c4; } + + public override bool Equals(object? obj) => obj is Parameters other && Equals(other); + + public bool Equals(Parameters other) => + Alpha == other.Alpha && + C1 == other.C1 && C2 == other.C2 && + C3 == other.C3 && C4 == other.C4; + + public override int GetHashCode() => HashCode.Combine(Alpha, C1, C2, C3, C4); + + public static bool operator ==(Parameters left, Parameters right) => left.Equals(right); + + public static bool operator !=(Parameters left, Parameters right) => !left.Equals(right); } private readonly Parameters _params; diff --git a/lib/averages/tema/Tema.cs b/lib/averages/tema/Tema.cs index d8ed2edf..6b7374f5 100644 --- a/lib/averages/tema/Tema.cs +++ b/lib/averages/tema/Tema.cs @@ -26,7 +26,7 @@ namespace QuanTAlib; [SkipLocalsInit] public sealed class Tema : ITValuePublisher { - private struct EmaState + private struct EmaState : IEquatable { public double Ema; public double E; @@ -34,6 +34,20 @@ public sealed class Tema : ITValuePublisher public bool IsCompensated; public static EmaState New() => new() { Ema = 0, E = 1.0, IsHot = false, IsCompensated = false }; + + public override bool Equals(object? obj) => obj is EmaState other && Equals(other); + + public bool Equals(EmaState other) => + Ema == other.Ema && + E == other.E && + IsHot == other.IsHot && + IsCompensated == other.IsCompensated; + + public override int GetHashCode() => HashCode.Combine(Ema, E, IsHot, IsCompensated); + + public static bool operator ==(EmaState left, EmaState right) => left.Equals(right); + + public static bool operator !=(EmaState left, EmaState right) => !left.Equals(right); } private readonly double _alpha;