feat: add new indicators (Decay, Edecay, MinusDi, MinusDm, PlusDi, PlusDm, Maxindex, Minindex, Sarext) and update pine scripts, core libs, validation tests, and python bindings

This commit is contained in:
Miha Kralj
2026-03-09 13:45:46 -07:00
parent 8e43d62cbb
commit 031f1b5fe6
491 changed files with 6156 additions and 5590 deletions
+4 -4
View File
@@ -12,7 +12,7 @@ public class RingBufferTests
Assert.Equal(0, buffer.Count);
Assert.False(buffer.IsFull);
Assert.Equal(0, buffer.Sum);
Assert.Equal(0, buffer.Average);
Assert.True(double.IsNaN(buffer.Average));
}
[Fact]
@@ -192,7 +192,7 @@ public class RingBufferTests
Assert.Equal(0, buffer.Count);
Assert.Equal(0, buffer.Sum);
Assert.Equal(0, buffer.Average);
Assert.True(double.IsNaN(buffer.Average));
Assert.False(buffer.IsFull);
}
@@ -453,11 +453,11 @@ public class RingBufferTests
}
[Fact]
public void Average_EmptyBuffer_ReturnsZero()
public void Average_EmptyBuffer_ReturnsNaN()
{
var buffer = new RingBuffer(5);
Assert.Equal(0, buffer.Average);
Assert.True(double.IsNaN(buffer.Average));
}
[Fact]
+2 -2
View File
@@ -141,12 +141,12 @@ public sealed class RingBuffer : IEnumerable<double>
/// <summary>
/// Average of all elements in the buffer.
/// Returns 0 if buffer is empty.
/// Returns <see langword="double.NaN"/> if buffer is empty.
/// </summary>
public double Average
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count > 0 ? _sum / _count : 0;
get => _count > 0 ? _sum / _count : double.NaN;
}
/// <summary>
+7 -7
View File
@@ -90,7 +90,7 @@ public static class SimdExtensions
for (int i = 0; i < span.Length; i++)
{
double diff = span[i] - mean;
sumSquares += diff * diff;
sumSquares = Math.FusedMultiplyAdd(diff, diff, sumSquares);
}
return sumSquares / (span.Length - 1);
}
@@ -438,7 +438,7 @@ public static class SimdExtensions
for (; i < span.Length; i++)
{
double diff = span[i] - m;
result += diff * diff;
result = Math.FusedMultiplyAdd(diff, diff, result);
}
return result / (span.Length - 1);
@@ -682,17 +682,17 @@ public static class SimdExtensions
// Unroll scalar loop with 4 accumulators to break dependency chains
for (; i <= len - 4; i += 4)
{
s1 += Unsafe.Add(ref ar, i) * Unsafe.Add(ref br, i);
s2 += Unsafe.Add(ref ar, i + 1) * Unsafe.Add(ref br, i + 1);
s3 += Unsafe.Add(ref ar, i + 2) * Unsafe.Add(ref br, i + 2);
s4 += Unsafe.Add(ref ar, i + 3) * Unsafe.Add(ref br, i + 3);
s1 = Math.FusedMultiplyAdd(Unsafe.Add(ref ar, i), Unsafe.Add(ref br, i), s1);
s2 = Math.FusedMultiplyAdd(Unsafe.Add(ref ar, i + 1), Unsafe.Add(ref br, i + 1), s2);
s3 = Math.FusedMultiplyAdd(Unsafe.Add(ref ar, i + 2), Unsafe.Add(ref br, i + 2), s3);
s4 = Math.FusedMultiplyAdd(Unsafe.Add(ref ar, i + 3), Unsafe.Add(ref br, i + 3), s4);
}
double s = s1 + s2 + s3 + s4;
for (; i < len; i++)
{
s += Unsafe.Add(ref ar, i) * Unsafe.Add(ref br, i);
s = Math.FusedMultiplyAdd(Unsafe.Add(ref ar, i), Unsafe.Add(ref br, i), s);
}
return s;
}
+6
View File
@@ -213,6 +213,12 @@ public class TSeries : IReadOnlyList<TValue>, ITValuePublisher
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(DateTime time, double value, bool isNew = true) => Add(new TValue(time, value), isNew);
/// <remarks>
/// <b>Synthetic timestamps:</b> Each element receives a fabricated timestamp starting at
/// <see cref="DateTime.UtcNow"/> (captured once at call time) and incrementing by one minute
/// per item. These timestamps do not reflect real market time. Use the typed overloads
/// (<see cref="Add(TValue)"/>, <see cref="Add(DateTime,double,bool)"/>) when real timestamps matter.
/// </remarks>
public void Add(IEnumerable<double> values)
{
long t = DateTime.UtcNow.Ticks;