mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-18 10:38:05 +00:00
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:
@@ -310,4 +310,60 @@ public sealed class AfirmaValidationTests : IDisposable
|
||||
Assert.True(double.IsFinite(rectLast));
|
||||
Assert.True(double.IsFinite(bhLast));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Afirma_LeastSquares_Streaming_Matches_Batch()
|
||||
{
|
||||
int[] periods = { 5, 10, 20 };
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Batch calculation with leastSquares=true
|
||||
var afirmaBatch = new Afirma(period, leastSquares: true);
|
||||
var batchResult = afirmaBatch.Update(_testData.Data);
|
||||
|
||||
// Streaming calculation with leastSquares=true
|
||||
var afirmaStream = new Afirma(period, leastSquares: true);
|
||||
var streamResults = new List<double>();
|
||||
foreach (var item in _testData.Data)
|
||||
{
|
||||
streamResults.Add(afirmaStream.Update(item).Value);
|
||||
}
|
||||
|
||||
// Compare last 100 values
|
||||
int compareCount = Math.Min(100, batchResult.Count);
|
||||
for (int i = 0; i < compareCount; i++)
|
||||
{
|
||||
int idx = batchResult.Count - compareCount + i;
|
||||
Assert.Equal(batchResult[idx].Value, streamResults[idx], 1e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Afirma_Correction_Recomputes()
|
||||
{
|
||||
var ind = new Afirma(20);
|
||||
var t0 = DateTime.MinValue;
|
||||
|
||||
// Build state well past warmup
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
ind.Update(new TValue(t0.AddSeconds(i), 100.0 + i * 0.5));
|
||||
}
|
||||
|
||||
// Anchor bar
|
||||
var anchorTime = t0.AddSeconds(50);
|
||||
const double anchorValue = 125.0;
|
||||
ind.Update(new TValue(anchorTime, anchorValue), isNew: true);
|
||||
double anchorResult = ind.Last.Value;
|
||||
|
||||
// Correction with dramatically different value — must yield different result
|
||||
ind.Update(new TValue(anchorTime, anchorValue * 10), isNew: false);
|
||||
Assert.NotEqual(anchorResult, ind.Last.Value);
|
||||
|
||||
// Correction back to original — must exactly restore original result
|
||||
ind.Update(new TValue(anchorTime, anchorValue), isNew: false);
|
||||
Assert.Equal(anchorResult, ind.Last.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,12 @@ public sealed class Afirma : AbstractBase
|
||||
/// <summary>
|
||||
/// Creates AFIRMA with TSeries source for priming.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Primes the internal buffer from <paramref name="source"/> history, then overwrites
|
||||
/// <c>Last.Time</c> with <c>source.LastTime</c>, replacing the
|
||||
/// <see cref="DateTime.MinValue"/> placeholder set by <see cref="Prime"/>.
|
||||
/// Subscribes to future source updates via the publisher event.
|
||||
/// </remarks>
|
||||
public Afirma(TSeries source, int period, WindowType window = WindowType.BlackmanHarris, bool leastSquares = false)
|
||||
: this(period, window, leastSquares)
|
||||
{
|
||||
@@ -133,6 +139,11 @@ public sealed class Afirma : AbstractBase
|
||||
/// <summary>
|
||||
/// Initializes the indicator state using the provided history.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sets <c>Last.Time = <see cref="DateTime.MinValue"/></c> as a placeholder timestamp.
|
||||
/// Callers that invoke <see cref="Prime"/> directly must not rely on <c>Last.Time</c>
|
||||
/// until the first <see cref="Update(TValue, bool)"/> call assigns a real timestamp.
|
||||
/// </remarks>
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
if (source.Length == 0)
|
||||
@@ -541,7 +552,7 @@ public sealed class Afirma : AbstractBase
|
||||
int idx = (readIndex + bufferCount - 1 - j + period) % period;
|
||||
double v = buffer[idx];
|
||||
sy += v;
|
||||
sxy += j * v;
|
||||
sxy = Math.FusedMultiplyAdd(j, v, sxy);
|
||||
}
|
||||
|
||||
double denom = dn * sx2 - sx * sx;
|
||||
@@ -559,7 +570,7 @@ public sealed class Afirma : AbstractBase
|
||||
double v_ls;
|
||||
if (j < n)
|
||||
{
|
||||
v_ls = intercept + slope * j;
|
||||
v_ls = Math.FusedMultiplyAdd(slope, j, intercept);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Autoregressive FIR Moving Average (AFIRMA)", "AFIRMA", overlay=true)
|
||||
|
||||
Reference in New Issue
Block a user