mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-21 03:58:04 +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:
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Huber Loss (HUBER)", "HUBER")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Log-Cosh Loss", "LogCosh", overlay=false)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Arctangent Absolute Percentage Error", "MAAPE", overlay=false, format=format.percent)
|
||||
|
||||
@@ -71,4 +71,30 @@ public sealed class MaeValidationTests : IDisposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mae_Correction_Recomputes()
|
||||
{
|
||||
var ind = new Mae(20);
|
||||
|
||||
// Build state well past warmup
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
ind.Update(100.0 + i * 0.5, 98.0 + i * 0.5);
|
||||
}
|
||||
|
||||
// Anchor bar
|
||||
const double anchorActual = 125.0;
|
||||
const double anchorPredicted = 123.0;
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: true);
|
||||
double anchorResult = ind.Last.Value;
|
||||
|
||||
// Correction with dramatically different values — recompute must yield different result
|
||||
ind.Update(anchorActual * 10, anchorPredicted * 10, isNew: false);
|
||||
Assert.NotEqual(anchorResult, ind.Last.Value);
|
||||
|
||||
// Correction back to original — must exactly restore original result
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: false);
|
||||
Assert.Equal(anchorResult, ind.Last.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute Error (MAE)", "MAE")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute %Deviation (MAPD)", "MAPD")
|
||||
|
||||
@@ -84,4 +84,31 @@ public sealed class MapeValidationTests : IDisposable
|
||||
|
||||
return sum / actual.Length;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mape_Correction_Recomputes()
|
||||
{
|
||||
var ind = new Mape(20);
|
||||
|
||||
// Build state well past warmup (actual always > 0 so MAPE denominator is valid)
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
ind.Update(100.0 + i * 0.5, 98.0 + i * 0.5);
|
||||
}
|
||||
|
||||
// Anchor bar
|
||||
const double anchorActual = 125.0;
|
||||
const double anchorPredicted = 123.0;
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: true);
|
||||
double anchorResult = ind.Last.Value;
|
||||
|
||||
// MAPE is scale-invariant: ×10 on both actual and predicted leaves ratio unchanged.
|
||||
// Change only predicted to dramatically alter the error percentage.
|
||||
ind.Update(anchorActual, 10.0, isNew: false);
|
||||
Assert.NotEqual(anchorResult, ind.Last.Value);
|
||||
|
||||
// Correction back to original — must exactly restore original result
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: false);
|
||||
Assert.Equal(anchorResult, ind.Last.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute %Error (MAPE)", "MAPE")
|
||||
|
||||
@@ -152,7 +152,7 @@ public sealed class Mase : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return Update(new TValue(DateTime.UtcNow, actual), new TValue(DateTime.UtcNow, predicted), isNew);
|
||||
return Update(new TValue(DateTime.MinValue, actual), new TValue(DateTime.MinValue, predicted), isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Absolute Scaled Error (MASE)", "MASE")
|
||||
|
||||
@@ -104,7 +104,7 @@ public sealed class Mdae : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return Update(new TValue(DateTime.UtcNow, actual), new TValue(DateTime.UtcNow, predicted), isNew);
|
||||
return Update(new TValue(DateTime.MinValue, actual), new TValue(DateTime.MinValue, predicted), isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Median Absolute Error", "MdAE", overlay=false)
|
||||
|
||||
@@ -58,7 +58,7 @@ public sealed class Mdape : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return UpdateCore(DateTime.UtcNow, actual, predicted, isNew);
|
||||
return UpdateCore(DateTime.MinValue, actual, predicted, isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Median Absolute Percentage Error", "MdAPE", overlay=false, format=format.percent)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Error (ME)", "ME")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean %Error (MPE)", "MPE")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Relative Absolute Error", "MRAE", overlay=false)
|
||||
|
||||
@@ -71,4 +71,30 @@ public sealed class MseValidationTests : IDisposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mse_Correction_Recomputes()
|
||||
{
|
||||
var ind = new Mse(20);
|
||||
|
||||
// Build state well past warmup
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
ind.Update(100.0 + i * 0.5, 98.0 + i * 0.5);
|
||||
}
|
||||
|
||||
// Anchor bar
|
||||
const double anchorActual = 125.0;
|
||||
const double anchorPredicted = 123.0;
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: true);
|
||||
double anchorResult = ind.Last.Value;
|
||||
|
||||
// Correction with dramatically different values — recompute must yield different result
|
||||
ind.Update(anchorActual * 10, anchorPredicted * 10, isNew: false);
|
||||
Assert.NotEqual(anchorResult, ind.Last.Value);
|
||||
|
||||
// Correction back to original — must exactly restore original result
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: false);
|
||||
Assert.Equal(anchorResult, ind.Last.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Squared Error (MSE)", "MSE")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Mean Squared Logarithmic Error (MSLE)", "MSLE")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Pseudo-Huber Loss", "PseudoHuber", overlay=false)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Quantile Loss (Pinball Loss)", "QuantileLoss", overlay=false)
|
||||
|
||||
@@ -149,7 +149,7 @@ public sealed class Rae : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return Update(new TValue(DateTime.UtcNow, actual), new TValue(DateTime.UtcNow, predicted), isNew);
|
||||
return Update(new TValue(DateTime.MinValue, actual), new TValue(DateTime.MinValue, predicted), isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Relative Absolute Error (RAE)", "RAE")
|
||||
|
||||
@@ -73,4 +73,30 @@ public sealed class RmseValidationTests : IDisposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rmse_Correction_Recomputes()
|
||||
{
|
||||
var ind = new Rmse(20);
|
||||
|
||||
// Build state well past warmup
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
ind.Update(100.0 + i * 0.5, 98.0 + i * 0.5);
|
||||
}
|
||||
|
||||
// Anchor bar
|
||||
const double anchorActual = 125.0;
|
||||
const double anchorPredicted = 123.0;
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: true);
|
||||
double anchorResult = ind.Last.Value;
|
||||
|
||||
// Correction with dramatically different values — recompute must yield different result
|
||||
ind.Update(anchorActual * 10, anchorPredicted * 10, isNew: false);
|
||||
Assert.NotEqual(anchorResult, ind.Last.Value);
|
||||
|
||||
// Correction back to original — must exactly restore original result
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: false);
|
||||
Assert.Equal(anchorResult, ind.Last.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Root Mean Squared Error (RMSE)", "RMSE")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Root Mean Squared Logarithmic Error (RMSLE)", "RMSLE")
|
||||
|
||||
@@ -156,7 +156,7 @@ public sealed class Rse : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return Update(new TValue(DateTime.UtcNow, actual), new TValue(DateTime.UtcNow, predicted), isNew);
|
||||
return Update(new TValue(DateTime.MinValue, actual), new TValue(DateTime.MinValue, predicted), isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Relative Squared Error (RSE)", "RSE")
|
||||
|
||||
@@ -194,4 +194,30 @@ public sealed class RsquaredValidationTests : IDisposable
|
||||
Assert.True(double.IsFinite(rsq.Last.Value), "QuanTAlib R² last must be finite");
|
||||
Assert.True(rsq.Last.Value <= 1.0 + 1e-9, $"QuanTAlib R² should be ≤ 1, got {rsq.Last.Value}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rsquared_Correction_Recomputes()
|
||||
{
|
||||
var ind = new Rsquared(20);
|
||||
|
||||
// Build state well past warmup
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
ind.Update(100.0 + i * 0.5, 98.0 + i * 0.5);
|
||||
}
|
||||
|
||||
// Anchor bar
|
||||
const double anchorActual = 125.0;
|
||||
const double anchorPredicted = 123.0;
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: true);
|
||||
double anchorResult = ind.Last.Value;
|
||||
|
||||
// R² is scale-invariant: change only predicted (not ×10 both) to break R²
|
||||
ind.Update(anchorActual, 10.0, isNew: false);
|
||||
Assert.NotEqual(anchorResult, ind.Last.Value);
|
||||
|
||||
// Correction back to original — must exactly restore original result
|
||||
ind.Update(anchorActual, anchorPredicted, isNew: false);
|
||||
Assert.Equal(anchorResult, ind.Last.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public sealed class Rsquared : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return Update(new TValue(DateTime.UtcNow, actual), new TValue(DateTime.UtcNow, predicted), isNew);
|
||||
return Update(new TValue(DateTime.MinValue, actual), new TValue(DateTime.MinValue, predicted), isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("R² Coefficient of Determination (RSQUARED)", "RSQUARED")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Symmetric Mean Absolute %Error (SMAPE)", "SMAPE")
|
||||
|
||||
@@ -65,7 +65,7 @@ public sealed class TheilU : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return UpdateCore(DateTime.UtcNow, actual, predicted, isNew);
|
||||
return UpdateCore(DateTime.MinValue, actual, predicted, isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Theil's U Statistic", "TheilU", overlay=false)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Tukey's Biweight Loss", "TukeyBiweight", overlay=false)
|
||||
|
||||
@@ -128,7 +128,7 @@ public sealed class Wmape : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, bool isNew = true)
|
||||
{
|
||||
return Update(new TValue(DateTime.UtcNow, actual), new TValue(DateTime.UtcNow, predicted), isNew);
|
||||
return Update(new TValue(DateTime.MinValue, actual), new TValue(DateTime.MinValue, predicted), isNew);
|
||||
}
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Weighted Mean Absolute Percentage Error", "WMAPE", overlay=false, format=format.percent)
|
||||
|
||||
@@ -176,7 +176,7 @@ public sealed class Wrmse : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(double actual, double predicted, double weight, bool isNew = true)
|
||||
{
|
||||
return Update(new TValue(DateTime.UtcNow, actual), new TValue(DateTime.UtcNow, predicted), weight, isNew);
|
||||
return Update(new TValue(DateTime.MinValue, actual), new TValue(DateTime.MinValue, predicted), weight, isNew);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The MIT License (MIT)
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Weighted Root Mean Squared Error", "WRMSE", overlay=false)
|
||||
|
||||
Reference in New Issue
Block a user