From 839313c9f23cfc9ebfcf8013ff07b175e99a93a2 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Thu, 10 Oct 2024 16:23:23 -0700 Subject: [PATCH 1/2] Pwma --- Tests/test_eventing.cs | 3 +- Tests/test_iTValue.cs | 1 + docs/indicators/indicators.md | 2 +- lib/averages/Pwma.cs | 85 ++++++++++++++++++++++++++++ quantower/Averages/AlmaIndicator.cs | 6 +- quantower/Averages/DemaIndicator.cs | 2 +- quantower/Averages/DsmaIndicator.cs | 2 +- quantower/Averages/DwmaIndicator.cs | 2 +- quantower/Averages/EmaIndicator.cs | 2 +- quantower/Averages/EpmaIndicator.cs | 2 +- quantower/Averages/FramaIndicator.cs | 2 +- quantower/Averages/FwmaIndicator.cs | 2 +- quantower/Averages/GmaIndicator.cs | 2 +- quantower/Averages/PwmaIndicator.cs | 23 ++++++++ 14 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 lib/averages/Pwma.cs create mode 100644 quantower/Averages/PwmaIndicator.cs diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs index f51e50b0..395cd4b8 100644 --- a/Tests/test_eventing.cs +++ b/Tests/test_eventing.cs @@ -28,6 +28,7 @@ public class EventingTests ("Dwma", new Dwma(p), new Dwma(input, p)), ("Ema", new Ema(p), new Ema(input, p)), ("Epma", new Epma(p), new Epma(input, p)), + ("Pwma", new Pwma(p), new Pwma(input, p)), ("Frama", new Frama(p), new Frama(input, p)), ("Fwma", new Fwma(p), new Fwma(input, p)), ("Gma", new Gma(p), new Gma(input, p)), @@ -81,4 +82,4 @@ public class EventingTests rng.GetBytes(bytes); return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue; } -} \ No newline at end of file +} diff --git a/Tests/test_iTValue.cs b/Tests/test_iTValue.cs index 887c78cf..2886c5bc 100644 --- a/Tests/test_iTValue.cs +++ b/Tests/test_iTValue.cs @@ -35,6 +35,7 @@ public class IndicatorTests new Dsma(period: 14), new Dwma(period: 14), new Epma(period: 14), + new Pwma(period: 14), new Frama(period: 14), new Fwma(period: 14), new Gma(period: 14), diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md index d11d04e8..16cffcb4 100644 --- a/docs/indicators/indicators.md +++ b/docs/indicators/indicators.md @@ -72,7 +72,7 @@ |MGDI - McGinley Dynamic Indicator|`Mgdi`|`✔️`||| |MMA - Modified Moving Average|`Mma`|||| |PPMA - Pivot Point Moving Average||||| -|PWMA - Pascal's Weighted Moving Average||||| +|PWMA - Pascal's Weighted Moving Average|`Pwma`|||| |QEMA - Quad Exponential Moving Average|`Qema`|||| |RMA - WildeR's Moving Average|`Rma`|||| |SINEMA - Sine Weighted Moving Average|`Sinema`|||| diff --git a/lib/averages/Pwma.cs b/lib/averages/Pwma.cs new file mode 100644 index 00000000..fc2aac76 --- /dev/null +++ b/lib/averages/Pwma.cs @@ -0,0 +1,85 @@ +namespace QuanTAlib; + +public class Pwma : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Pwma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Pwma"; + WarmupPeriod = period; + Init(); + } + + public Pwma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + + // Adjust for partial periods during warmup + if (_index < _period) + { + double[] partialKernel = GenerateKernel(_index); + result /= partialKernel.Sum(); + } + + IsHot = _index >= WarmupPeriod; + + return result; + } + + public static double[] GenerateKernel(int period) + { + double[] kernel = new double[period]; + kernel[0] = 1; + + for (int i = 1; i < period; i++) + { + for (int j = i; j > 0; j--) + { + kernel[j] += kernel[j - 1]; + } + } + + // Normalize the kernel + double weightSum = kernel.Sum(); + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } +} diff --git a/quantower/Averages/AlmaIndicator.cs b/quantower/Averages/AlmaIndicator.cs index 251ad0f1..f1daddab 100644 --- a/quantower/Averages/AlmaIndicator.cs +++ b/quantower/Averages/AlmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class AlmaIndicator : IndicatorBase { @@ -7,10 +7,10 @@ public class AlmaIndicator : IndicatorBase public int Period { get; set; } = 10; [InputParameter("Offset", sortIndex: 5)] - public double Offset = 0.85; + public double Offset { get; set; } = 0.85; [InputParameter("Sigma", sortIndex: 6)] - public double Sigma = 6.0; + public double Sigma { get; set; } = 6.0; private Alma? ma; protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"ALMA {Period} : {Offset:F2} : {Sigma:F0} : {SourceName}"; diff --git a/quantower/Averages/DemaIndicator.cs b/quantower/Averages/DemaIndicator.cs index 185cf6a5..f943f289 100644 --- a/quantower/Averages/DemaIndicator.cs +++ b/quantower/Averages/DemaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class DemaIndicator : IndicatorBase { diff --git a/quantower/Averages/DsmaIndicator.cs b/quantower/Averages/DsmaIndicator.cs index 9bf1dc6d..e4259399 100644 --- a/quantower/Averages/DsmaIndicator.cs +++ b/quantower/Averages/DsmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class DsmaIndicator : IndicatorBase { diff --git a/quantower/Averages/DwmaIndicator.cs b/quantower/Averages/DwmaIndicator.cs index 5b80c398..2126c023 100644 --- a/quantower/Averages/DwmaIndicator.cs +++ b/quantower/Averages/DwmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class DwmaIndicator : IndicatorBase { diff --git a/quantower/Averages/EmaIndicator.cs b/quantower/Averages/EmaIndicator.cs index 7adecd30..ab07c5b0 100644 --- a/quantower/Averages/EmaIndicator.cs +++ b/quantower/Averages/EmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class EmaIndicator : IndicatorBase { diff --git a/quantower/Averages/EpmaIndicator.cs b/quantower/Averages/EpmaIndicator.cs index 1a92d33c..239fba4f 100644 --- a/quantower/Averages/EpmaIndicator.cs +++ b/quantower/Averages/EpmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class EpmaIndicator : IndicatorBase { diff --git a/quantower/Averages/FramaIndicator.cs b/quantower/Averages/FramaIndicator.cs index 865f4a53..0e5e8d1b 100644 --- a/quantower/Averages/FramaIndicator.cs +++ b/quantower/Averages/FramaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class FramaIndicator : IndicatorBase { diff --git a/quantower/Averages/FwmaIndicator.cs b/quantower/Averages/FwmaIndicator.cs index b4a9a389..5d24849b 100644 --- a/quantower/Averages/FwmaIndicator.cs +++ b/quantower/Averages/FwmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class FwmaIndicator : IndicatorBase { diff --git a/quantower/Averages/GmaIndicator.cs b/quantower/Averages/GmaIndicator.cs index 5d85e499..4d794534 100644 --- a/quantower/Averages/GmaIndicator.cs +++ b/quantower/Averages/GmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class GmaIndicator : IndicatorBase { diff --git a/quantower/Averages/PwmaIndicator.cs b/quantower/Averages/PwmaIndicator.cs new file mode 100644 index 00000000..29d7bdcc --- /dev/null +++ b/quantower/Averages/PwmaIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class PwmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Pwma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"PWMA {Period} : {SourceName}"; + + public PwmaIndicator() : base() + { + Name = "PWMA - Pascal's Weighted Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Pwma(period: Period); + } +} From cc45cebeb4586387f8f0f87d61269c889536fc58 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Fri, 11 Oct 2024 18:02:09 -0700 Subject: [PATCH 2/2] tests and cleanup --- Tests/Tests.csproj | 20 +- Tests/test_eventing.cs | 21 +- Tests/test_iTValue.cs | 158 ------ Tests/test_quantower.cs | 94 ++++ Tests/test_skender.stock.cs | 2 +- Tests/test_talib.cs | 20 - Tests/test_updates_averages.cs | 529 ++++++++++++++++++ Tests/test_updates_errors.cs | 259 +++++++++ Tests/test_updates_statistics.cs | 214 +++++++ docs/indicators/indicators.md | 32 +- lib/averages/Maaf.cs | 12 +- lib/errors/Huberloss.cs | 138 +++++ lib/errors/Mae.cs | 123 ++++ lib/errors/Mapd.cs | 132 +++++ lib/errors/Mape.cs | 132 +++++ lib/errors/Mase.cs | 136 +++++ lib/errors/Mda.cs | 135 +++++ lib/errors/Me.cs | 122 ++++ lib/errors/Mpe.cs | 132 +++++ lib/errors/Mse.cs | 124 ++++ lib/errors/Msle.cs | 126 +++++ lib/errors/Rae.cs | 129 +++++ lib/errors/Rmse.cs | 122 ++++ lib/errors/Rmsle.cs | 126 +++++ lib/errors/Rse.cs | 132 +++++ lib/errors/Rsquared.cs | 132 +++++ lib/errors/Smape.cs | 132 +++++ lib/quantalib.csproj | 21 +- lib/statistics/Curvature.cs | 23 +- lib/statistics/Entropy.cs | 16 +- lib/statistics/Kurtosis.cs | 22 + lib/statistics/Max.cs | 44 +- lib/statistics/Median.cs | 21 + lib/statistics/Min.cs | 40 +- lib/statistics/Mode.cs | 17 + lib/statistics/Percentile.cs | 18 +- lib/statistics/Skew.cs | 19 +- lib/statistics/Slope.cs | 29 + lib/statistics/Stddev.cs | 18 + lib/statistics/Variance.cs | 19 + lib/statistics/Zscore.cs | 23 +- notebooks/means.dib | 4 +- quantower/Averages/AfirmaIndicator.cs | 6 +- quantower/Averages/AlmaIndicator.cs | 2 + quantower/Averages/Averages.csproj | 22 +- quantower/Averages/DemaIndicator.cs | 1 + quantower/Averages/DsmaIndicator.cs | 1 + quantower/Averages/DwmaIndicator.cs | 2 +- quantower/Averages/EmaIndicator.cs | 2 +- quantower/Averages/EpmaIndicator.cs | 1 + quantower/Averages/FramaIndicator.cs | 2 +- quantower/Averages/FwmaIndicator.cs | 2 +- quantower/Averages/GmaIndicator.cs | 2 +- quantower/Averages/HmaIndicator.cs | 2 +- quantower/Averages/HtitIndicator.cs | 1 + quantower/Averages/HwmaIndicator.cs | 5 +- quantower/Averages/JmaIndicator.cs | 2 +- quantower/Averages/KamaIndicator.cs | 2 +- quantower/Averages/LtmaIndicator.cs | 1 + quantower/Averages/MaafIndicator.cs | 5 +- quantower/Averages/MamaIndicator.cs | 2 +- quantower/Averages/MgdiIndicator.cs | 3 +- quantower/Averages/MmaIndicator.cs | 1 + quantower/Averages/PwmaIndicator.cs | 1 + quantower/Averages/QemaIndicator.cs | 2 +- quantower/Averages/RemaIndicator.cs | 1 + quantower/Averages/RmaIndicator.cs | 4 +- quantower/Averages/SinemaIndicator.cs | 1 + quantower/Averages/SmaIndicator.cs | 2 +- quantower/Averages/SmmaIndicator.cs | 2 +- quantower/Averages/T3Indicator.cs | 1 + quantower/Averages/TemaIndicator.cs | 1 + quantower/Averages/TrimaIndicator.cs | 2 +- quantower/Averages/VidyaIndicator.cs | 2 +- quantower/Averages/WmaIndicator.cs | 2 +- quantower/Averages/ZlemaIndicator.cs | 4 +- quantower/Statistics/CurvatureIndicator.cs | 3 +- quantower/Statistics/EntropyIndicator.cs | 3 +- quantower/Statistics/KurtosisIndicator.cs | 3 +- quantower/Statistics/MaxIndicator.cs | 3 +- quantower/Statistics/MedianIndicator.cs | 3 +- quantower/Statistics/MinIndicator.cs | 3 +- quantower/Statistics/ModeIndicator.cs | 3 +- quantower/Statistics/PercentileIndicator.cs | 6 +- quantower/Statistics/SkewIndicator.cs | 4 +- quantower/Statistics/SlopeIndicator.cs | 3 +- quantower/Statistics/Statistics.csproj | 22 +- quantower/Statistics/StddevIndicator.cs | 3 +- ...rianceIndictor.cs => VarianceIndicator.cs} | 3 +- quantower/Statistics/ZscoreIndicator.cs | 4 +- quantower/Volatility/AtrIndicator.cs | 3 +- quantower/Volatility/HistoricalIndicator.cs | 3 +- quantower/Volatility/RealizedIndicator.cs | 3 +- quantower/Volatility/RviIndicator.cs | 7 +- quantower/Volatility/Volatility.csproj | 22 +- quantower/_IndicatorBase.cs | 3 +- 96 files changed, 3640 insertions(+), 327 deletions(-) delete mode 100644 Tests/test_iTValue.cs create mode 100644 Tests/test_quantower.cs create mode 100644 Tests/test_updates_averages.cs create mode 100644 Tests/test_updates_errors.cs create mode 100644 Tests/test_updates_statistics.cs create mode 100644 lib/errors/Huberloss.cs create mode 100644 lib/errors/Mae.cs create mode 100644 lib/errors/Mapd.cs create mode 100644 lib/errors/Mape.cs create mode 100644 lib/errors/Mase.cs create mode 100644 lib/errors/Mda.cs create mode 100644 lib/errors/Me.cs create mode 100644 lib/errors/Mpe.cs create mode 100644 lib/errors/Mse.cs create mode 100644 lib/errors/Msle.cs create mode 100644 lib/errors/Rae.cs create mode 100644 lib/errors/Rmse.cs create mode 100644 lib/errors/Rmsle.cs create mode 100644 lib/errors/Rse.cs create mode 100644 lib/errors/Rsquared.cs create mode 100644 lib/errors/Smape.cs rename quantower/Statistics/{VarianceIndictor.cs => VarianceIndicator.cs} (86%) diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 974ec457..da2d0d68 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -13,7 +13,7 @@ all runtime; build; native; contentfiles; analyzers - + @@ -25,15 +25,19 @@ - - + + ..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + - \ No newline at end of file + + + + + diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs index 395cd4b8..9a61defd 100644 --- a/Tests/test_eventing.cs +++ b/Tests/test_eventing.cs @@ -8,7 +8,7 @@ namespace QuanTAlib; public class EventingTests { [Fact] - public void VerifyEventBasedCalculations() + public void EventBasedCalculations() { // Create a cryptographically secure random number generator using var rng = RandomNumberGenerator.Create(); @@ -50,7 +50,24 @@ public class EventingTests ("Rma", new Rma(p), new Rma(input, p)), ("Tema", new Tema(p), new Tema(input, p)), ("Kama", new Kama(2, 30, 6), new Kama(input, 2, 30, 6)), - ("Zlema", new Zlema(p), new Zlema(input, p)) + ("Zlema", new Zlema(p), new Zlema(input, p)), + // error classes + ("Mae", new Mae(p), new Mae(input, p)), + ("Mapd", new Mapd(p), new Mapd(input, p)), + ("Mape", new Mape(p), new Mape(input, p)), + ("Mase", new Mase(p), new Mase(input, p)), + ("Mda", new Mda(p), new Mda(input, p)), + ("Me", new Me(p), new Me(input, p)), + ("Mpe", new Mpe(p), new Mpe(input, p)), + ("Mse", new Mse(p), new Mse(input, p)), + ("Msle", new Msle(p), new Msle(input, p)), + ("Rae", new Rae(p), new Rae(input, p)), + ("Rmse", new Rmse(p), new Rmse(input, p)), + ("Rmsle", new Rmsle(p), new Rmsle(input, p)), + ("Rse", new Rse(p), new Rse(input, p)), + ("Smape", new Smape(p), new Smape(input, p)), + ("Rsquared", new Rsquared(p), new Rsquared(input, p)), + ("Huberloss", new Huberloss(p), new Huberloss(input, p)) }; // Generate 200 random values and feed them to both direct and event-based indicators diff --git a/Tests/test_iTValue.cs b/Tests/test_iTValue.cs deleted file mode 100644 index 2886c5bc..00000000 --- a/Tests/test_iTValue.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Xunit; -using System.Reflection; -using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; - -namespace QuanTAlib; - -public class IndicatorTests -{ - private readonly RandomNumberGenerator rng; - private const int SeriesLen = 1000; - private const int Corrections = 100; - - public IndicatorTests() - { - rng = RandomNumberGenerator.Create(); - } - - private int GetRandomNumber(int minValue, int maxValue) - { - byte[] randomBytes = new byte[4]; - rng.GetBytes(randomBytes); - int randomInt = BitConverter.ToInt32(randomBytes, 0); - return Math.Abs(randomInt % (maxValue - minValue)) + minValue; - } - - // skipcq: CS-R1055 - private static readonly ITValue[] indicators = - { - new Ema(period: 10, useSma: true), - new Alma(period: 14, offset: 0.85, sigma: 6), - new Afirma(periods: 4, taps: 4, window: Afirma.WindowType.Blackman), - new Convolution(new[] { 1.0, 2, 3, 2, 1 }), - new Dema(period: 14), - new Dsma(period: 14), - new Dwma(period: 14), - new Epma(period: 14), - new Pwma(period: 14), - new Frama(period: 14), - new Fwma(period: 14), - new Gma(period: 14), - new Hma(period: 14), - new Hwma(period: 14), - new Kama(period: 14), - new Mama(fastLimit: 0.5, slowLimit: 0.05), - new Mgdi(period: 14), - new Mma(period: 14), - new Qema(), - new Rema(period: 14), - new Rma(period: 14), - new Sinema(period: 14), - new Sma(period: 14), - new Smma(period: 14), - new T3(period: 14), - new Tema(period: 14), - new Trima(period: 14), - new Vidya(shortPeriod: 14, longPeriod: 30, alpha: 0.2), - new Wma(period: 14), - new Zlema(period: 14), - - new Curvature(period: 14), - new Entropy(period: 14), - new Kurtosis(period: 14), - new Max(period: 14, decay: 0.01), - new Median(period: 14), - new Min(period: 14, decay: 0.01), - new Median(period: 14), - new Mode(period: 14), - new Percentile(period: 14, percent: 50), - new Skew(period: 14), - new Slope(period: 14), - new Stddev(period: 14), - new Variance(period: 14), - new Zscore(period: 14), - - new Historical(period: 14), - new Realized(period: 14) - }; - - [Theory] - [MemberData(nameof(GetIndicators))] - public void IndicatorIsNew(ITValue indicator) - { - var indicator1 = indicator; - var indicator2 = indicator; - - MethodInfo calcMethod = FindCalcMethod(indicator.GetType()); - if (calcMethod == null) - { - throw new InvalidOperationException($"Calc method not found for indicator type: {indicator.GetType().Name}"); - } - - for (int i = 0; i < SeriesLen; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: GetRandomNumber(-100, 100), IsNew: true); - InvokeCalc(indicator1, calcMethod, item1); - - for (int j = 0; j < Corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: GetRandomNumber(-100, 100), IsNew: false); - InvokeCalc(indicator1, calcMethod, item1); - } - - var item2 = new TValue(item1.Time, item1.Value, IsNew: true); - InvokeCalc(indicator2, calcMethod, item2); - - Assert.Equal(indicator1.Value, indicator2.Value); - } - } - - private static MethodInfo FindCalcMethod(Type type) - { - while (type != null && type != typeof(object)) - { - var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) - .Where(m => m.Name == "Calc") - .ToList(); - - if (methods.Count > 0) - { - // Prefer the method with TValue parameter - var method = methods.FirstOrDefault(m => - { - var parameters = m.GetParameters(); - return parameters.Length == 1 && parameters[0].ParameterType == typeof(TValue); - }); - - // If not found, return the first method - return method ?? methods.First(); - } - - type = type.BaseType!; - } - return null!; - } - - private static void InvokeCalc(ITValue indicator, MethodInfo calcMethod, TValue input) - { - var parameters = calcMethod.GetParameters(); - if (parameters.Length == 1) - { - calcMethod.Invoke(indicator, new object[] { input }); - } - else if (parameters.Length == 2) - { - calcMethod.Invoke(indicator, new object[] { input, double.NaN }); - } - else - { - throw new InvalidOperationException($"Invalid number of parameters for Calc method in indicator type: {indicator.GetType().Name}"); - } - } - - public static IEnumerable GetIndicators() - { - return indicators.Select(indicator => new object[] { indicator }); - } -} diff --git a/Tests/test_quantower.cs b/Tests/test_quantower.cs new file mode 100644 index 00000000..fc7e9058 --- /dev/null +++ b/Tests/test_quantower.cs @@ -0,0 +1,94 @@ +using Xunit; +using System; +using System.Reflection; +using TradingPlatform.BusinessLayer; + +namespace QuanTAlib +{ + public class QuantowerTests + { + private void TestIndicator(string fieldName = "ma") where T : Indicator, new() + { + var indicator = new T(); + try + { + var onInitMethod = typeof(T).GetMethod("OnInit", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(onInitMethod); + onInitMethod.Invoke(indicator, null); + + var field = typeof(T).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(field); + var fieldValue = field.GetValue(indicator); + Assert.NotNull(fieldValue); + + Assert.NotNull(indicator.ShortName); + Assert.NotEmpty(indicator.ShortName); + Assert.NotNull(indicator.Name); + Assert.NotEmpty(indicator.Name); + Assert.NotNull(indicator.Description); + Assert.NotEmpty(indicator.Description); + Assert.IsAssignableFrom(indicator); + } + catch (Exception ex) + { + throw new Xunit.Sdk.XunitException($"Test failed for {typeof(T).Name}: {ex.Message}"); + } + } + + // Averages Indicators + [Fact] public void Afirma() => TestIndicator(); + [Fact] public void Alma() => TestIndicator(); + [Fact] public void Dema() => TestIndicator(); + [Fact] public void Dsma() => TestIndicator(); + [Fact] public void Dwma() => TestIndicator(); + [Fact] public void Ema() => TestIndicator(); + [Fact] public void Epma() => TestIndicator(); + [Fact] public void Frama() => TestIndicator(); + [Fact] public void Fwma() => TestIndicator(); + [Fact] public void Gma() => TestIndicator(); + [Fact] public void Hma() => TestIndicator(); + [Fact] public void Htit() => TestIndicator(); + [Fact] public void Hwma() => TestIndicator(); + [Fact] public void Jma() => TestIndicator(); + [Fact] public void Kama() => TestIndicator(); + [Fact] public void Ltma() => TestIndicator(); + [Fact] public void Maaf() => TestIndicator(); + [Fact] public void Mama() => TestIndicator(); + [Fact] public void Mgdi() => TestIndicator(); + [Fact] public void Mma() => TestIndicator(); + [Fact] public void Pwma() => TestIndicator(); + [Fact] public void Qema() => TestIndicator(); + [Fact] public void Rema() => TestIndicator(); + [Fact] public void Rma() => TestIndicator(); + [Fact] public void Sinema() => TestIndicator(); + [Fact] public void Sma() => TestIndicator(); + [Fact] public void Smma() => TestIndicator(); + [Fact] public void T3() => TestIndicator(); + [Fact] public void Tema() => TestIndicator(); + [Fact] public void Trima() => TestIndicator(); + [Fact] public void Vidya() => TestIndicator(); + [Fact] public void Wma() => TestIndicator(); + [Fact] public void Zlema() => TestIndicator(); + + // Statistics Indicators + [Fact] public void Curvature() => TestIndicator("curvature"); + [Fact] public void Entropy() => TestIndicator("entropy"); + [Fact] public void Kurtosis() => TestIndicator("kurtosis"); + [Fact] public void Max() => TestIndicator("ma"); + [Fact] public void Median() => TestIndicator("med"); + [Fact] public void Min() => TestIndicator("mi"); + [Fact] public void Mode() => TestIndicator("mode"); + [Fact] public void Percentile() => TestIndicator("percentile"); + [Fact] public void Skew() => TestIndicator("skew"); + [Fact] public void Slope() => TestIndicator("slope"); + [Fact] public void Stddev() => TestIndicator("stddev"); + [Fact] public void Variance() => TestIndicator("variance"); + [Fact] public void Zscore() => TestIndicator("zScore"); + + // Volatility Indicators + [Fact] public void Atr() => TestIndicator("atr"); + [Fact] public void Historical() => TestIndicator("historical"); + [Fact] public void Realized() => TestIndicator("realized"); + [Fact] public void Rvi() => TestIndicator("rvi"); + } +} diff --git a/Tests/test_skender.stock.cs b/Tests/test_skender.stock.cs index a936e08f..3dbf6a2a 100644 --- a/Tests/test_skender.stock.cs +++ b/Tests/test_skender.stock.cs @@ -5,7 +5,7 @@ using System.Security.Cryptography; #pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781 -namespace QuanTAlib; +namespace QuanTAlib.Tests; public class SkenderTests { diff --git a/Tests/test_talib.cs b/Tests/test_talib.cs index 08d75046..78d21597 100644 --- a/Tests/test_talib.cs +++ b/Tests/test_talib.cs @@ -109,26 +109,6 @@ public class TAlibTests } } - [Fact] - public void WMA() - { - for (int run = 0; run < iterations; run++) - { - int period = GetRandomNumber(5, 55); - Wma ma = new(period); - TSeries QL = new(); - foreach (TBar item in feed) - { QL.Add(ma.Calc(new TValue(item.Time, item.Close))); } - Core.Wma(data, 0, QL.Length - 1, TALIB, out int outBegIdx, out _, period); - Assert.Equal(QL.Length, TALIB.Count()); - for (int i = QL.Length - 1; i > 2000; i--) - { - Assert.InRange(TALIB[i - outBegIdx] - QL[i].Value, -range, range); - } - } - } - - [Fact] public void T3() { diff --git a/Tests/test_updates_averages.cs b/Tests/test_updates_averages.cs new file mode 100644 index 00000000..2c06f8c5 --- /dev/null +++ b/Tests/test_updates_averages.cs @@ -0,0 +1,529 @@ +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class AveragesUpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100 + } + + [Fact] + public void Afirma_Update() + { + var indicator = new Afirma(periods: 14, taps: 4, window: Afirma.WindowType.Blackman); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Alma_Update() + { + var indicator = new Alma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Convolution_Update() + { + var indicator = new Convolution(new double[] { 1, 2, 3, 2, 1 }); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Dema_Update() + { + var indicator = new Dema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Dsma_Update() + { + var indicator = new Dsma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Dwma_Update() + { + var indicator = new Dwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Ema_Update() + { + var indicator = new Ema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Epma_Update() + { + var indicator = new Epma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Frama_Update() + { + var indicator = new Frama(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Fwma_Update() + { + var indicator = new Fwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Gma_Update() + { + var indicator = new Gma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Hma_Update() + { + var indicator = new Hma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Htit_Update() + { + var indicator = new Htit(); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Hwma_Update() + { + var indicator = new Hwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Jma_Update() + { + var indicator = new Jma(period: 14, phase: 0); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Kama_Update() + { + var indicator = new Kama(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Ltma_Update() + { + var indicator = new Ltma(gamma: 0.2); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Maaf_Update() + { + var indicator = new Maaf(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mama_Update() + { + var indicator = new Mama(fastLimit: 0.5, slowLimit: 0.05); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mgdi_Update() + { + var indicator = new Mgdi(period: 14, kFactor: 0.6); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mma_Update() + { + var indicator = new Mma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Pwma_Update() + { + var indicator = new Pwma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Qema_Update() + { + var indicator = new Qema(k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rema_Update() + { + var indicator = new Rema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rma_Update() + { + var indicator = new Rma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Sinema_Update() + { + var indicator = new Sinema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Sma_Update() + { + var indicator = new Sma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Smma_Update() + { + var indicator = new Smma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void T3_Update() + { + var indicator = new T3(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Tema_Update() + { + var indicator = new Tema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Trima_Update() + { + var indicator = new Trima(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Vidya_Update() + { + var indicator = new Vidya(shortPeriod: 14, longPeriod: 30, alpha: 0.2); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Wma_Update() + { + var indicator = new Wma(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Zlema_Update() + { + var indicator = new Zlema(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/Tests/test_updates_errors.cs b/Tests/test_updates_errors.cs new file mode 100644 index 00000000..67db6037 --- /dev/null +++ b/Tests/test_updates_errors.cs @@ -0,0 +1,259 @@ +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class UpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100 + } + + [Fact] + public void Huberloss_Update() + { + var indicator = new Huberloss(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mae_Update() + { + var indicator = new Mae(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mapd_Update() + { + var indicator = new Mapd(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mape_Update() + { + var indicator = new Mape(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mase_Update() + { + var indicator = new Mase(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mda_Update() + { + var indicator = new Mda(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Me_Update() + { + var indicator = new Me(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mpe_Update() + { + var indicator = new Mpe(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mse_Update() + { + var indicator = new Mse(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Msle_Update() + { + var indicator = new Msle(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rae_Update() + { + var indicator = new Rae(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rmse_Update() + { + var indicator = new Rmse(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rmsle_Update() + { + var indicator = new Rmsle(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rse_Update() + { + var indicator = new Rse(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Smape_Update() + { + var indicator = new Smape(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rsquared_Update() + { + var indicator = new Rsquared(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/Tests/test_updates_statistics.cs b/Tests/test_updates_statistics.cs new file mode 100644 index 00000000..6fca5957 --- /dev/null +++ b/Tests/test_updates_statistics.cs @@ -0,0 +1,214 @@ +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class StatisticsUpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200 - 100; // Range: -100 to 100 + } + + [Fact] + public void Curvature_Update() + { + var indicator = new Curvature(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Entropy_Update() + { + var indicator = new Entropy(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Kurtosis_Update() + { + var indicator = new Kurtosis(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Max_Update() + { + var indicator = new Max(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Median_Update() + { + var indicator = new Median(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Min_Update() + { + var indicator = new Min(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Mode_Update() + { + var indicator = new Mode(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Percentile_Update() + { + var indicator = new Percentile(period: 14, percent: 50); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Skew_Update() + { + var indicator = new Skew(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Slope_Update() + { + var indicator = new Slope(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Stddev_Update() + { + var indicator = new Stddev(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Variance_Update() + { + var indicator = new Variance(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Zscore_Update() + { + var indicator = new Zscore(period: 14); + double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true)); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md index 16cffcb4..7b28a378 100644 --- a/docs/indicators/indicators.md +++ b/docs/indicators/indicators.md @@ -13,7 +13,7 @@ |OHLC4 - Average Price|`️.OHLC4`|CandlePart.OHLC4|AvgPrice|| |HLCC4 - Weighted Price|`️.HLCC4`||WclPrice|| |
|||| -|**STATISTICS AND NUMERICAL ANALYSIS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| +|**STATISTICS, ERRORS AND NUMERICAL ANALYSIS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| |BETA - Beta coefficient||||| |CORR - Correlation Coefficient||||| |CURVATURE - Rate of Change in Direction or Slope|`Curvature`|||| @@ -21,28 +21,28 @@ |KURTOSIS - Measure of Tails/Peakedness|`Kurtosis`|||| |HUBER - Huber Loss||||| |MAX - Maximum with exponential decay|`Max`|||| -|MAE - Mean Absolute Error||||| -|MAPD - Mean Absolute Percentage Deviation||||| -|MAPE - Mean Absolute Percentage Error||||| -|MASE - Mean Absolute Scaled Error||||| +|MAE - Mean Absolute Error|`Mae`|||| +|MAPD - Mean Absolute Percentage Deviation|`Mapd`|||| +|MAPE - Mean Absolute Percentage Error|`Mape`|||| +|MASE - Mean Absolute Scaled Error|`Mase`|||| |MDA - Mean Directional Accuracy||||| -|ME - Mean Error||||| +|ME - Mean Error|`Me`|||| |MEDIAN - Middle value|`Median`|||| |MIN - Minimum with exponential decay|`Min`|||| |MODE - Most Frequent Value|`Mode`|||| -|MPE - Pean Percentage Error||||| -|MSE - Mean Squared Error||||| -|MSLE - Mean Squared Logarithmic Error||||| +|MPE - Pean Percentage Error|`Mpe`|||| +|MSE - Mean Squared Error|`Mse`|||| +|MSLE - Mean Squared Logarithmic Error|`Msle`|||| |PERCENTILE - Rank Order|`Percentile`|||| |RSQUARED - Coefficient of Determination R-Squared||||| -|RAE - Relative Absolute Error||||| -|RMSE - Root Mean Squared Error||||| -|RSE - Relateive Squared Error||||| -|RMSLE - Root Mean Squared Logarithmic Error||||| +|RAE - Relative Absolute Error|`Rae`|||| +|RMSE - Root Mean Squared Error|`Rmse`|||| +|RSE - Relateive Squared Error|`Rse`|||| +|RMSLE - Root Mean Squared Logarithmic Error|`Rmsle`|||| |SKEW - Skewness, asymmetry of distribution|`Skew`|||| |SLOPE - Rate of Change, Linear Regression|`Slope`|||| -|SMAPE - Symmetric Mean Absolute Percentage Error||||| -|STDDEV - Standard Deviation, Measure of Spread||||| +|SMAPE - Symmetric Mean Absolute Percentage Error|`Smape`|||| +|STDDEV - Standard Deviation, Measure of Spread|`Stddev`|||| |THEIL - Theil's U Statistics||||| |VARIANCE - Average of Squared Deviations|`Variance`|||| |ZSCORE - Standardized Score|`Zscore`|||| @@ -87,7 +87,7 @@ |TSF - Time Series Forecast|||`✔️`|`✔️`| |VIDYA - Variable Index Dynamic Average|`Vidya`|||`✔️`| |VORTEX - Vortex Indicator||`✔️`||| -|WMA - Weighted Moving Average|`Wma`|`✔️`|`✔️`|`✔️`| +|WMA - Weighted Moving Average|`Wma`|`✔️`||`✔️`| |ZLEMA - Zero Lag EMA Average|`Zlema`|||`✔️`| |
|||| |**VOLATILITY INDICATORS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| diff --git a/lib/averages/Maaf.cs b/lib/averages/Maaf.cs index bc730ade..fd4503da 100644 --- a/lib/averages/Maaf.cs +++ b/lib/averages/Maaf.cs @@ -14,18 +14,18 @@ public class Maaf : AbstractBase private readonly int _period; - public Maaf(int Period = 39, double Threshold = 0.002) + public Maaf(int period = 39, double threshold = 0.002) { - _period = Period; - _threshold = Threshold; + _period = period; + _threshold = threshold; _priceBuffer = new CircularBuffer(4); - _smoothBuffer = new CircularBuffer(Period); + _smoothBuffer = new CircularBuffer(period); Name = "MAAF"; - WarmupPeriod = Period; + WarmupPeriod = period; Init(); } - public Maaf(object source, int Period = 39, double Threshold = 0.002) : this(Period, Threshold) + public Maaf(object source, int period = 39, double threshold = 0.002) : this(period, threshold) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); diff --git a/lib/errors/Huberloss.cs b/lib/errors/Huberloss.cs new file mode 100644 index 00000000..71d77fac --- /dev/null +++ b/lib/errors/Huberloss.cs @@ -0,0 +1,138 @@ +namespace QuanTAlib; + +/// +/// Represents a Huber Loss calculator that combines the best properties of L2 squared loss for normal data +/// and L1 absolute loss for outliers. +/// +/// +/// The Huberloss class calculates the Huber Loss using circular buffers +/// to efficiently manage the actual and predicted data points within the specified period. +/// +public class Huberloss : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + private readonly double _delta; + + /// + /// Initializes a new instance of the Huberloss class with the specified period and delta. + /// + /// The period over which to calculate the Huber Loss. + /// The threshold at which to switch from squared to linear loss. + /// + /// Thrown when period is less than 1 or delta is less than or equal to 0. + /// + public Huberloss(int period, double delta = 1.0) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + if (delta <= 0) + { + throw new ArgumentOutOfRangeException(nameof(delta), "Delta must be greater than 0."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + _delta = delta; + Name = $"Huberloss(period={period}, delta={delta})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Huberloss(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Huberloss instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Huberloss instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Huber Loss calculation for the current period. + /// + /// + /// The calculated Huber Loss value for the current period. + /// + /// + /// This method calculates the Huber Loss using the formula: + /// L(a, p) = 0.5 * (a - p)^2 for |a - p| <= delta + /// L(a, p) = delta * |a - p| - 0.5 * delta^2 for |a - p| > delta + /// where a is the actual value, p is the predicted value, and delta is the threshold. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double huberLoss = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumLoss = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = Math.Abs(actualValues[i] - predictedValues[i]); + if (error <= _delta) + { + sumLoss += 0.5 * error * error; + } + else + { + sumLoss += _delta * error - 0.5 * _delta * _delta; + } + } + + huberLoss = sumLoss / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return huberLoss; + } + + /// + /// Calculates the Huber Loss for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Huber Loss. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Mae.cs b/lib/errors/Mae.cs new file mode 100644 index 00000000..316abfb4 --- /dev/null +++ b/lib/errors/Mae.cs @@ -0,0 +1,123 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Absolute Error calculator that measures the average absolute difference +/// between actual values and predicted values. +/// +/// +/// The Mae class calculates the Mean Absolute Error using circular buffers +/// to efficiently manage the actual and predicted data points within the specified period. +/// +public class Mae : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Mae class with the specified period. + /// + /// The period over which to calculate the Mean Absolute Error. + /// + /// Thrown when period is less than 1. + /// + public Mae(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mae(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mae class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Error. + public Mae(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mae instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Mae instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Absolute Error calculation for the current period. + /// + /// + /// The calculated Mean Absolute Error value for the current period. + /// + /// + /// This method calculates the Mean Absolute Error using the formula: + /// MAE = sum(|actual - predicted|) / n + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// If Input2.Value is NaN, it uses the average of actual values as the predicted value. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mae = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumOfAbsoluteDifferences = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + sumOfAbsoluteDifferences += Math.Abs(actualValues[i] - predictedValues[i]); + } + + mae = sumOfAbsoluteDifferences / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return mae; + } + + /// + /// Calculates the Mean Absolute Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Mean Absolute Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Mapd.cs b/lib/errors/Mapd.cs new file mode 100644 index 00000000..531836dc --- /dev/null +++ b/lib/errors/Mapd.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Absolute Percentage Deviation calculator that measures the average absolute percentage difference +/// between actual values and predicted values. +/// +/// +/// The Mapd class calculates the Mean Absolute Percentage Deviation using circular buffers +/// to efficiently manage the actual and predicted data points within the specified period. +/// +public class Mapd : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Mapd class with the specified period. + /// + /// The period over which to calculate the Mean Absolute Percentage Deviation. + /// + /// Thrown when period is less than 1. + /// + public Mapd(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mapd(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mapd class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Deviation. + public Mapd(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mapd instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Mapd instance based on whether a new value is being processed. + /// + /// Indicates whether the current input is a new value. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Absolute Percentage Deviation calculation for the current period. + /// + /// + /// The calculated Mean Absolute Percentage Deviation value for the current period. + /// + /// + /// This method calculates the Mean Absolute Percentage Deviation using the formula: + /// MAPD = (sum(|actual - predicted| / |actual|) / n) * 100 + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// If there's only one value in the buffer or if any actual value is zero, those values are excluded from the calculation. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mapd = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumOfAbsolutePercentageDeviations = 0; + int validCount = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + if (actualValues[i] != 0) + { + sumOfAbsolutePercentageDeviations += Math.Abs((actualValues[i] - predictedValues[i]) / actualValues[i]); + validCount++; + } + } + + if (validCount > 0) + { + mapd = (sumOfAbsolutePercentageDeviations / validCount) * 100; + } + } + + IsHot = _index >= WarmupPeriod; + return mapd; + } + + /// + /// Calculates the Mean Absolute Percentage Deviation for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Mean Absolute Percentage Deviation. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Mape.cs b/lib/errors/Mape.cs new file mode 100644 index 00000000..dda866e5 --- /dev/null +++ b/lib/errors/Mape.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Absolute Percentage Error calculator that measures the average absolute percentage difference +/// between actual values and predicted values. +/// +/// +/// The Mape class calculates the Mean Absolute Percentage Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Mape : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Mape class with the specified period. + /// + /// The period over which to calculate the Mean Absolute Percentage Error. + /// + /// Thrown when period is less than 1. + /// + public Mape(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mape(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Mape(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mape instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Mape instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Absolute Percentage Error calculation for the current period. + /// + /// + /// The calculated Mean Absolute Percentage Error value for the current period. + /// + /// + /// This method calculates the Mean Absolute Percentage Error using the formula: + /// MAPE = (sum(|actual - predicted| / |actual|) / n) * 100 + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// If any actual value is zero, it is excluded from the calculation to avoid division by zero. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mape = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumAbsolutePercentageError = 0; + int validCount = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + if (actualValues[i] != 0) + { + sumAbsolutePercentageError += Math.Abs((actualValues[i] - predictedValues[i]) / actualValues[i]); + validCount++; + } + } + + if (validCount > 0) + { + mape = (sumAbsolutePercentageError / validCount) * 100; + } + } + + IsHot = _index >= WarmupPeriod; + return mape; + } + + /// + /// Calculates the Mean Absolute Percentage Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Mean Absolute Percentage Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Mase.cs b/lib/errors/Mase.cs new file mode 100644 index 00000000..b1e5673a --- /dev/null +++ b/lib/errors/Mase.cs @@ -0,0 +1,136 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Absolute Scaled Error calculator that measures the ratio of the mean absolute error +/// of the forecast values to the mean absolute error of the naive forecast. +/// +/// +/// The Mase class calculates the Mean Absolute Scaled Error using circular buffers +/// to efficiently manage the data points within the specified period. +/// +public class Mase : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _forecastBuffer; + private readonly int _period; + + /// + /// Initializes a new instance of the Mase class with the specified period. + /// + /// The period over which to calculate the Mean Absolute Scaled Error. + /// + /// Thrown when period is less than 3. + /// + public Mase(int period) + { + if (period < 3) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 3."); + } + _period = period; + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _forecastBuffer = new CircularBuffer(period); + Name = $"Mase(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mase class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Scaled Error. + public Mase(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mase instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _forecastBuffer.Clear(); + } + + /// + /// Manages the state of the Mase instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Absolute Scaled Error calculation for the current period. + /// + /// + /// The calculated Mean Absolute Scaled Error value for the current period. + /// + /// + /// This method calculates the Mean Absolute Scaled Error using the formula: + /// MASE = mean(|actual - forecast|) / mean(|actual[t] - actual[t-1]|) + /// where actual is each actual value and forecast is each forecast value. + /// If there are fewer than 3 values in the buffers, the method returns 0. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double forecast = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _forecastBuffer.Add(forecast, Input.IsNew); + + double mase = 0; + if (_actualBuffer.Count >= 3) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var forecastValues = _forecastBuffer.GetSpan().ToArray(); + + double sumAbsoluteError = 0; + double sumAbsoluteNaiveError = 0; + + int count = Math.Min(_actualBuffer.Count, _period); + + for (int i = 1; i < count; i++) + { + sumAbsoluteError += Math.Abs(actualValues[i] - forecastValues[i]); + sumAbsoluteNaiveError += Math.Abs(actualValues[i] - actualValues[i - 1]); + } + + double meanAbsoluteError = sumAbsoluteError / (count - 1); + double meanAbsoluteNaiveError = sumAbsoluteNaiveError / (count - 1); + + if (meanAbsoluteNaiveError != 0) + { + mase = meanAbsoluteError / meanAbsoluteNaiveError; + } + } + + IsHot = _index >= WarmupPeriod; + return mase; + } + + /// + /// Calculates the Mean Absolute Scaled Error for the given actual and forecast values. + /// + /// The actual value. + /// The forecast value. + /// The calculated Mean Absolute Scaled Error. + public double Calc(double actual, double forecast) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, forecast); + return Calculation(); + } +} diff --git a/lib/errors/Mda.cs b/lib/errors/Mda.cs new file mode 100644 index 00000000..4c184603 --- /dev/null +++ b/lib/errors/Mda.cs @@ -0,0 +1,135 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Directional Accuracy calculator that measures the average accuracy +/// of predicted directional changes compared to actual directional changes. +/// +/// +/// The Mda class calculates the Mean Directional Accuracy using a circular buffer +/// to efficiently manage the data points within the specified period. +/// Mean Directional Accuracy is useful in financial analysis for evaluating the performance +/// of forecasting models in predicting the direction of price movements. +/// +public class Mda : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _forecastBuffer; + + /// + /// Initializes a new instance of the Mda class with the specified period. + /// + /// The period over which to calculate the Mean Directional Accuracy. + /// + /// Thrown when period is less than 2. + /// + public Mda(int period) + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + WarmupPeriod = 1; + _actualBuffer = new CircularBuffer(period); + _forecastBuffer = new CircularBuffer(period); + Name = $"Mda(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mda class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Directional Accuracy. + public Mda(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mda instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _forecastBuffer.Clear(); + } + + /// + /// Manages the state of the Mda instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Directional Accuracy calculation for the current period. + /// + /// + /// The calculated Mean Directional Accuracy value for the current period. + /// + /// + /// This method calculates the Mean Directional Accuracy using the formula: + /// MDA = (number of correct directional predictions / total number of predictions) * 100 + /// A correct directional prediction is when the sign of the actual change matches + /// the sign of the predicted change. + /// The result is expressed as a percentage, where 100% indicates perfect directional accuracy + /// and 50% indicates performance no better than random guessing. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double forecast = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _forecastBuffer.Add(forecast, Input.IsNew); + + double mda = 0; + if (_actualBuffer.Count > 1) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var forecastValues = _forecastBuffer.GetSpan().ToArray(); + + int correctPredictions = 0; + int totalPredictions = actualValues.Length - 1; + + for (int i = 1; i < actualValues.Length; i++) + { + double actualChange = actualValues[i] - actualValues[i - 1]; + double forecastChange = forecastValues[i] - actualValues[i - 1]; + + if ((actualChange >= 0 && forecastChange >= 0) || (actualChange < 0 && forecastChange < 0)) + { + correctPredictions++; + } + } + + mda = (double)correctPredictions / totalPredictions * 100; + } + + IsHot = _actualBuffer.Count > 1; // MDA calc is valid from bar 2 + return mda; + } + + /// + /// Calculates the Mean Directional Accuracy for the given actual and forecast values. + /// + /// The actual value. + /// The forecast value. + /// The calculated Mean Directional Accuracy. + public double Calc(double actual, double forecast) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, forecast); + return Calculation(); + } +} diff --git a/lib/errors/Me.cs b/lib/errors/Me.cs new file mode 100644 index 00000000..e6356f79 --- /dev/null +++ b/lib/errors/Me.cs @@ -0,0 +1,122 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Error calculator that measures the average difference +/// between actual values and predicted values. +/// +/// +/// The Me class calculates the Mean Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Me : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Me class with the specified period. + /// + /// The period over which to calculate the Mean Error. + /// + /// Thrown when period is less than 1. + /// + public Me(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Me(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Me(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Me instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Me instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Error calculation for the current period. + /// + /// + /// The calculated Mean Error value for the current period. + /// + /// + /// This method calculates the Mean Error using the formula: + /// ME = sum(actual - predicted) / n + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double me = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + sumError += actualValues[i] - predictedValues[i]; + } + + me = sumError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return me; + } + + /// + /// Calculates the Mean Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Mean Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Mpe.cs b/lib/errors/Mpe.cs new file mode 100644 index 00000000..a9dd9be6 --- /dev/null +++ b/lib/errors/Mpe.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Percentage Error calculator that measures the average percentage difference +/// between actual values and predicted values. +/// +/// +/// The Mpe class calculates the Mean Percentage Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Mpe : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Mpe class with the specified period. + /// + /// The period over which to calculate the Mean Percentage Error. + /// + /// Thrown when period is less than 1. + /// + public Mpe(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mpe(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Mpe(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mpe instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Mpe instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Percentage Error calculation for the current period. + /// + /// + /// The calculated Mean Percentage Error value for the current period. + /// + /// + /// This method calculates the Mean Percentage Error using the formula: + /// MPE = (sum((actual - predicted) / actual) / n) * 100 + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// If any actual value is zero, it is excluded from the calculation to avoid division by zero. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mpe = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumPercentageError = 0; + int validCount = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + if (actualValues[i] != 0) + { + sumPercentageError += (actualValues[i] - predictedValues[i]) / actualValues[i]; + validCount++; + } + } + + if (validCount > 0) + { + mpe = (sumPercentageError / validCount) * 100; + } + } + + IsHot = _index >= WarmupPeriod; + return mpe; + } + + /// + /// Calculates the Mean Percentage Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Mean Percentage Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Mse.cs b/lib/errors/Mse.cs new file mode 100644 index 00000000..fcde24ac --- /dev/null +++ b/lib/errors/Mse.cs @@ -0,0 +1,124 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Squared Error calculator that measures the average of the squares +/// of the differences between actual values and predicted values. +/// +/// +/// The Mse class calculates the Mean Squared Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Mse : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Mse class with the specified period. + /// + /// The period over which to calculate the Mean Squared Error. + /// + /// Thrown when period is less than 1. + /// + public Mse(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Mse(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Mse(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Mse instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + + /// + /// Manages the state of the Mse instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Squared Error calculation for the current period. + /// + /// + /// The calculated Mean Squared Error value for the current period. + /// + /// + /// This method calculates the Mean Squared Error using the formula: + /// MSE = sum((actual - predicted)^2) / n + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double mse = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = actualValues[i] - predictedValues[i]; + sumSquaredError += error * error; + } + + mse = sumSquaredError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return mse; + } + + /// + /// Calculates the Mean Squared Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Mean Squared Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + _lastValidValue = predicted; + return Calculation(); + } +} diff --git a/lib/errors/Msle.cs b/lib/errors/Msle.cs new file mode 100644 index 00000000..524b9c28 --- /dev/null +++ b/lib/errors/Msle.cs @@ -0,0 +1,126 @@ +namespace QuanTAlib; + +/// +/// Represents a Mean Squared Logarithmic Error calculator that measures the average of the squares +/// of the differences between the logarithms of actual values and predicted values. +/// +/// +/// The Msle class calculates the Mean Squared Logarithmic Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Msle : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Msle class with the specified period. + /// + /// The period over which to calculate the Mean Squared Logarithmic Error. + /// + /// Thrown when period is less than 1. + /// + public Msle(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Msle(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Msle(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Msle instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Msle instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Mean Squared Logarithmic Error calculation for the current period. + /// + /// + /// The calculated Mean Squared Logarithmic Error value for the current period. + /// + /// + /// This method calculates the Mean Squared Logarithmic Error using the formula: + /// MSLE = sum((log(actual + 1) - log(predicted + 1))^2) / n + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// We add 1 to both actual and predicted values to avoid taking the log of zero. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double msle = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredLogError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double logActual = Math.Log(actualValues[i] + 1); + double logPredicted = Math.Log(predictedValues[i] + 1); + double logError = logActual - logPredicted; + sumSquaredLogError += logError * logError; + } + + msle = sumSquaredLogError / _actualBuffer.Count; + } + + IsHot = _index >= WarmupPeriod; + return msle; + } + + /// + /// Calculates the Mean Squared Logarithmic Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Mean Squared Logarithmic Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Rae.cs b/lib/errors/Rae.cs new file mode 100644 index 00000000..532b397a --- /dev/null +++ b/lib/errors/Rae.cs @@ -0,0 +1,129 @@ +namespace QuanTAlib; + +/// +/// Represents a Relative Absolute Error calculator that measures the ratio of the sum of absolute errors +/// to the sum of absolute differences between actual values and the mean of actual values. +/// +/// +/// The Rae class calculates the Relative Absolute Error using circular buffers +/// to efficiently manage the data points within the specified period. +/// +public class Rae : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Rae class with the specified period. + /// + /// The period over which to calculate the Relative Absolute Error. + /// + /// Thrown when period is less than 2. + /// + public Rae(int period) + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rae(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Rae(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Rae instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Rae instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Relative Absolute Error calculation for the current period. + /// + /// + /// The calculated Relative Absolute Error value for the current period. + /// + /// + /// This method calculates the Relative Absolute Error using the formula: + /// RAE = sum(|actual - predicted|) / sum(|actual - mean(actual)|) + /// where actual is each actual value, predicted is each predicted value, and mean(actual) is the average of actual values. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rae = 0; + if (_actualBuffer.Count >= 2) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double actualMean = actualValues.Average(); + double sumAbsoluteError = 0; + double sumAbsoluteDifferenceFromMean = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + sumAbsoluteError += Math.Abs(actualValues[i] - predictedValues[i]); + sumAbsoluteDifferenceFromMean += Math.Abs(actualValues[i] - actualMean); + } + + if (sumAbsoluteDifferenceFromMean != 0) + { + rae = sumAbsoluteError / sumAbsoluteDifferenceFromMean; + } + } + + IsHot = _index >= WarmupPeriod; + return rae; + } + + /// + /// Calculates the Relative Absolute Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Relative Absolute Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Rmse.cs b/lib/errors/Rmse.cs new file mode 100644 index 00000000..98c30097 --- /dev/null +++ b/lib/errors/Rmse.cs @@ -0,0 +1,122 @@ +namespace QuanTAlib; + +/// +/// Represents a Root Mean Squared Error calculator that measures the square root of the average +/// of the squares of the differences between actual values and predicted values. +/// +/// +/// The Rmse class calculates the Root Mean Squared Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Rmse : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Rmse class with the specified period. + /// + /// The period over which to calculate the Root Mean Squared Error. + /// + /// Thrown when period is less than 1. + /// + public Rmse(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rmse(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Rmse(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + /// + /// Initializes the Rmse instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Rmse instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Root Mean Squared Error calculation for the current period. + /// + /// + /// The calculated Root Mean Squared Error value for the current period. + /// + /// + /// This method calculates the Root Mean Squared Error using the formula: + /// RMSE = sqrt(sum((actual - predicted)^2) / n) + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rmse = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = actualValues[i] - predictedValues[i]; + sumSquaredError += error * error; + } + + rmse = Math.Sqrt(sumSquaredError / _actualBuffer.Count); + } + + IsHot = _index >= WarmupPeriod; + return rmse; + } + + /// + /// Calculates the Root Mean Squared Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Root Mean Squared Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Rmsle.cs b/lib/errors/Rmsle.cs new file mode 100644 index 00000000..ed23cd34 --- /dev/null +++ b/lib/errors/Rmsle.cs @@ -0,0 +1,126 @@ +namespace QuanTAlib; + +/// +/// Represents a Root Mean Squared Logarithmic Error calculator that measures the square root of the average +/// of the squares of the differences between the logarithms of actual values and predicted values. +/// +/// +/// The Rmsle class calculates the Root Mean Squared Logarithmic Error using a circular buffer +/// to efficiently manage the data points within the specified period. +/// +public class Rmsle : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Rmsle class with the specified period. + /// + /// The period over which to calculate the Root Mean Squared Logarithmic Error. + /// + /// Thrown when period is less than 1. + /// + public Rmsle(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rmsle(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Rmsle(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Rmsle instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Rmsle instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Root Mean Squared Logarithmic Error calculation for the current period. + /// + /// + /// The calculated Root Mean Squared Logarithmic Error value for the current period. + /// + /// + /// This method calculates the Root Mean Squared Logarithmic Error using the formula: + /// RMSLE = sqrt(sum((log(actual + 1) - log(predicted + 1))^2) / n) + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// We add 1 to both actual and predicted values to avoid taking the log of zero. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rmsle = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSquaredLogError = 0; + for (int i = 0; i < _actualBuffer.Count; i++) + { + double logActual = Math.Log(actualValues[i] + 1); + double logPredicted = Math.Log(predictedValues[i] + 1); + double logError = logActual - logPredicted; + sumSquaredLogError += logError * logError; + } + + rmsle = Math.Sqrt(sumSquaredLogError / _actualBuffer.Count); + } + + IsHot = _index >= WarmupPeriod; + return rmsle; + } + + /// + /// Calculates the Root Mean Squared Logarithmic Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Root Mean Squared Logarithmic Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Rse.cs b/lib/errors/Rse.cs new file mode 100644 index 00000000..49203f86 --- /dev/null +++ b/lib/errors/Rse.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +/// +/// Represents a Relative Squared Error calculator that measures the ratio of the sum of squared errors +/// to the sum of squared differences between actual values and the mean of actual values. +/// +/// +/// The Rse class calculates the Relative Squared Error using circular buffers +/// to efficiently manage the data points within the specified period. +/// +public class Rse : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Rse class with the specified period. + /// + /// The period over which to calculate the Relative Squared Error. + /// + /// Thrown when period is less than 2. + /// + public Rse(int period) + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rse(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Rse(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Rse instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Rse instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Relative Squared Error calculation for the current period. + /// + /// + /// The calculated Relative Squared Error value for the current period. + /// + /// + /// This method calculates the Relative Squared Error using the formula: + /// RSE = sum((actual - predicted)^2) / sum((actual - mean(actual))^2) + /// where actual is each actual value, predicted is each predicted value, and mean(actual) is the average of actual values. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rse = 0; + if (_actualBuffer.Count >= 2) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double actualMean = actualValues.Average(); + double sumSquaredError = 0; + double sumSquaredDifferenceFromMean = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + double error = actualValues[i] - predictedValues[i]; + sumSquaredError += error * error; + + double differenceFromMean = actualValues[i] - actualMean; + sumSquaredDifferenceFromMean += differenceFromMean * differenceFromMean; + } + + if (sumSquaredDifferenceFromMean != 0) + { + rse = sumSquaredError / sumSquaredDifferenceFromMean; + } + } + + IsHot = _index >= WarmupPeriod; + return rse; + } + + /// + /// Calculates the Relative Squared Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Relative Squared Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Rsquared.cs b/lib/errors/Rsquared.cs new file mode 100644 index 00000000..16fd695e --- /dev/null +++ b/lib/errors/Rsquared.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +/// +/// Represents a Coefficient of Determination (R-squared) calculator that measures the proportion of +/// the variance in the dependent variable that is predictable from the independent variable(s). +/// +/// +/// The Rsquared class calculates the Coefficient of Determination using circular buffers +/// to efficiently manage the actual and predicted data points within the specified period. +/// +public class Rsquared : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Rsquared class with the specified period. + /// + /// The period over which to calculate the Coefficient of Determination. + /// + /// Thrown when period is less than 2. + /// + public Rsquared(int period) + { + if (period < 2) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Rsquared(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Rsquared(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Rsquared instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Rsquared instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Coefficient of Determination calculation for the current period. + /// + /// + /// The calculated Coefficient of Determination value for the current period. + /// + /// + /// This method calculates the Coefficient of Determination using the formula: + /// R^2 = 1 - (SSres / SStot) + /// where SSres is the sum of squared residuals and SStot is the total sum of squares. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double rsquared = 0; + if (_actualBuffer.Count >= 2) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double actualMean = actualValues.Average(); + double ssRes = 0; + double ssTot = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + double residual = actualValues[i] - predictedValues[i]; + ssRes += residual * residual; + + double deviation = actualValues[i] - actualMean; + ssTot += deviation * deviation; + } + + if (ssTot != 0) + { + rsquared = 1 - (ssRes / ssTot); + } + } + + IsHot = _index >= WarmupPeriod; + return rsquared; + } + + /// + /// Calculates the Coefficient of Determination for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Coefficient of Determination. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/errors/Smape.cs b/lib/errors/Smape.cs new file mode 100644 index 00000000..292b93b7 --- /dev/null +++ b/lib/errors/Smape.cs @@ -0,0 +1,132 @@ +namespace QuanTAlib; + +/// +/// Represents a Symmetric Mean Absolute Percentage Error calculator that measures the percentage difference +/// between actual and predicted values, using a symmetric formula to handle both positive and negative errors equally. +/// +/// +/// The Smape class calculates the Symmetric Mean Absolute Percentage Error using circular buffers +/// to efficiently manage the data points within the specified period. +/// +public class Smape : AbstractBase +{ + private readonly CircularBuffer _actualBuffer; + private readonly CircularBuffer _predictedBuffer; + + /// + /// Initializes a new instance of the Smape class with the specified period. + /// + /// The period over which to calculate the Symmetric Mean Absolute Percentage Error. + /// + /// Thrown when period is less than 1. + /// + public Smape(int period) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + WarmupPeriod = period; + _actualBuffer = new CircularBuffer(period); + _predictedBuffer = new CircularBuffer(period); + Name = $"Smape(period={period})"; + Init(); + } + + /// + /// Initializes a new instance of the Mape class with the specified source and period. + /// + /// The source object to subscribe to for value updates. + /// The period over which to calculate the Mean Absolute Percentage Error. + public Smape(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + /// + /// Initializes the Smape instance by clearing the buffers. + /// + public override void Init() + { + base.Init(); + _actualBuffer.Clear(); + _predictedBuffer.Clear(); + } + + /// + /// Manages the state of the Smape instance based on whether new values are being processed. + /// + /// Indicates whether the current inputs are new values. + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + /// + /// Performs the Symmetric Mean Absolute Percentage Error calculation for the current period. + /// + /// + /// The calculated Symmetric Mean Absolute Percentage Error value for the current period. + /// + /// + /// This method calculates the Symmetric Mean Absolute Percentage Error using the formula: + /// SMAPE = (100% / n) * sum(2 * |actual - predicted| / (|actual| + |predicted|)) + /// where actual is each actual value, predicted is each predicted value, and n is the number of values. + /// + protected override double Calculation() + { + ManageState(Input.IsNew); + + double actual = Input.Value; + _actualBuffer.Add(actual, Input.IsNew); + + double predicted = double.IsNaN(Input2.Value) ? _actualBuffer.Average() : Input2.Value; + _predictedBuffer.Add(predicted, Input.IsNew); + + double smape = 0; + if (_actualBuffer.Count > 0) + { + var actualValues = _actualBuffer.GetSpan().ToArray(); + var predictedValues = _predictedBuffer.GetSpan().ToArray(); + + double sumSymmetricPercentageError = 0; + int validCount = 0; + + for (int i = 0; i < _actualBuffer.Count; i++) + { + double denominator = Math.Abs(actualValues[i]) + Math.Abs(predictedValues[i]); + if (denominator != 0) + { + sumSymmetricPercentageError += 2 * Math.Abs(actualValues[i] - predictedValues[i]) / denominator; + validCount++; + } + } + + if (validCount > 0) + { + smape = (100.0 / validCount) * sumSymmetricPercentageError; + } + } + + IsHot = _index >= WarmupPeriod; + return smape; + } + + /// + /// Calculates the Symmetric Mean Absolute Percentage Error for the given actual and predicted values. + /// + /// The actual value. + /// The predicted value. + /// The calculated Symmetric Mean Absolute Percentage Error. + public double Calc(double actual, double predicted) + { + Input = new TValue(DateTime.Now, actual); + Input2 = new TValue(DateTime.Now, predicted); + return Calculation(); + } +} diff --git a/lib/quantalib.csproj b/lib/quantalib.csproj index 2ca3d6ba..f7831c1f 100644 --- a/lib/quantalib.csproj +++ b/lib/quantalib.csproj @@ -1,9 +1,9 @@ - QuanTAlib - Library of TA Calculations, Charts and Strategies for Quantower - Quantitative Technical Analysis Library in C# for Quantower + QuanTAlib + Library of TA Calculations, Charts and Strategies for Quantower + Quantitative Technical Analysis Library in C# for Quantower git https://github.com/mihakralj/QuanTAlib true @@ -14,7 +14,7 @@ QuanTAlib True AnyCPU - False + False full True True @@ -27,10 +27,15 @@ Quantitative;Historical;Quotes; QuanTAlib2.png - https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png - True + https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png + True + false + + + + @@ -38,11 +43,11 @@ - ..\.github\TradingPlatform.BusinessLayer.dll + ..\.github\TradingPlatform.BusinessLayer.dll TradingPlatform.BusinessLayer.xml - \ No newline at end of file + diff --git a/lib/statistics/Curvature.cs b/lib/statistics/Curvature.cs index 838ffeab..59330724 100644 --- a/lib/statistics/Curvature.cs +++ b/lib/statistics/Curvature.cs @@ -4,15 +4,36 @@ namespace QuanTAlib; /// Calculates the rate of change of the slope over a specified period. /// Provides insights into trend acceleration or deceleration. /// +/// +/// Curvature is a second-order derivative that measures how quickly the slope (first-order derivative) is changing. +/// Positive curvature indicates accelerating uptrends or decelerating downtrends. +/// Negative curvature indicates decelerating uptrends or accelerating downtrends. +/// This indicator can be useful for identifying potential trend reversals or confirming trend strength. +/// public class Curvature : AbstractBase { private readonly int _period; private readonly Slope _slopeCalculator; private readonly CircularBuffer _slopeBuffer; + /// + /// Gets the y-intercept of the curvature line. + /// public double? Intercept { get; private set; } + + /// + /// Gets the standard deviation of the slope values used in the curvature calculation. + /// public double? StdDev { get; private set; } + + /// + /// Gets the R-squared value, indicating the goodness of fit of the curvature line. + /// public double? RSquared { get; private set; } + + /// + /// Gets the last calculated point on the curvature line. + /// public double? Line { get; private set; } /// @@ -153,4 +174,4 @@ public class Curvature : AbstractBase IsHot = _slopeBuffer.Count == _period; return curvature; } -} \ No newline at end of file +} diff --git a/lib/statistics/Entropy.cs b/lib/statistics/Entropy.cs index b0ed654c..7803f7e4 100644 --- a/lib/statistics/Entropy.cs +++ b/lib/statistics/Entropy.cs @@ -4,8 +4,20 @@ namespace QuanTAlib; /// Measures the unpredictability of data using Shannon's Entropy. /// Provides insights into the randomness or information content of the time series. /// +/// +/// Shannon's Entropy quantifies the average amount of information contained in a message. +/// In the context of time series analysis, it can be used to: +/// - Detect regime changes or structural breaks in the data. +/// - Assess the complexity or predictability of price movements. +/// - Identify periods of high uncertainty or information flow in the market. +/// The entropy value is normalized between 0 and 1, where 1 indicates maximum randomness +/// and 0 indicates perfect predictability. +/// public class Entropy : AbstractBase { + /// + /// The number of data points to consider for the entropy calculation. + /// private readonly int Period; private readonly CircularBuffer _buffer; @@ -24,7 +36,7 @@ public class Entropy : AbstractBase "Period must be greater than or equal to 2 for entropy calculation."); } Period = period; - WarmupPeriod = 2; + WarmupPeriod = 2; // Minimum number of points needed for entropy calculation _buffer = new CircularBuffer(period); Name = $"Entropy(period={period})"; Init(); @@ -110,4 +122,4 @@ public class Entropy : AbstractBase IsHot = _buffer.Count >= Period; return entropy; } -} \ No newline at end of file +} diff --git a/lib/statistics/Kurtosis.cs b/lib/statistics/Kurtosis.cs index 8e4a5658..cd32c92f 100644 --- a/lib/statistics/Kurtosis.cs +++ b/lib/statistics/Kurtosis.cs @@ -4,8 +4,25 @@ namespace QuanTAlib; /// Calculates excess kurtosis using the Sheskin Algorithm. /// Measures the "tailedness" of the probability distribution of a real-valued random variable. /// +/// +/// Kurtosis is a measure of the combined weight of a distribution's tails relative to the center of the distribution. +/// In financial time series analysis, kurtosis can provide insights into: +/// - The frequency and magnitude of extreme returns. +/// - The potential for outliers or "black swan" events. +/// - The shape of the return distribution compared to a normal distribution. +/// +/// Interpretation: +/// - Excess kurtosis > 0: Heavy-tailed distribution (more extreme values than a normal distribution) +/// - Excess kurtosis = 0: Normal distribution +/// - Excess kurtosis < 0: Light-tailed distribution (fewer extreme values than a normal distribution) +/// +/// High kurtosis in financial returns may indicate a higher risk of extreme events. +/// public class Kurtosis : AbstractBase { + /// + /// The number of data points to consider for the kurtosis calculation. + /// private readonly int Period; private readonly CircularBuffer _buffer; @@ -73,6 +90,11 @@ public class Kurtosis : AbstractBase /// /// Uses the Sheskin Algorithm for kurtosis calculation. /// Requires at least 4 data points for a valid calculation. + /// + /// Interpretation of results: + /// - Positive values indicate a distribution with heavier tails and a higher peak compared to a normal distribution. + /// - Negative values indicate a distribution with lighter tails and a lower peak compared to a normal distribution. + /// - A value close to 0 suggests a distribution similar to a normal distribution in terms of tailedness. /// protected override double Calculation() { diff --git a/lib/statistics/Max.cs b/lib/statistics/Max.cs index b0f72fa4..2f110a03 100644 --- a/lib/statistics/Max.cs +++ b/lib/statistics/Max.cs @@ -4,19 +4,57 @@ namespace QuanTAlib; /// Calculates the maximum value over a specified period, with an optional decay factor. /// Useful for tracking the highest point in a time series with the ability to gradually forget old peaks. /// +/// +/// The Max indicator is particularly useful in financial analysis for: +/// - Identifying resistance levels in price charts. +/// - Tracking the highest price over a given period. +/// - Implementing trailing stop-loss strategies. +/// +/// The decay factor allows the indicator to adapt to changing market conditions by +/// gradually reducing the influence of older maximum values. +/// public class Max : AbstractBase { + /// + /// The number of data points to consider for the maximum calculation. + /// private readonly int Period; + + /// + /// Circular buffer to store the most recent data points. + /// private readonly CircularBuffer _buffer; + + /// + /// The half-life decay factor used to gradually forget old peaks. + /// private readonly double _halfLife; - private double _currentMax, _p_currentMax; - private int _timeSinceNewMax, _p_timeSinceNewMax; + + /// + /// The current maximum value. + /// + private double _currentMax; + + /// + /// The previous maximum value. + /// + private double _p_currentMax; + + /// + /// The number of periods since a new maximum was set. + /// + private int _timeSinceNewMax; + + /// + /// The previous value of _timeSinceNewMax. + /// + private int _p_timeSinceNewMax; /// /// Initializes a new instance of the Max class. /// /// The number of data points to consider. Must be at least 1. - /// Half-life decay factor. Set to 0 for no decay, higher for faster forgetting. Default is 0. + /// Half-life decay factor. Set to 0 for no decay, higher for faster forgetting of old peaks. Default is 0. /// /// Thrown when the period is less than 1 or decay is negative. /// diff --git a/lib/statistics/Median.cs b/lib/statistics/Median.cs index 6518faba..c999099e 100644 --- a/lib/statistics/Median.cs +++ b/lib/statistics/Median.cs @@ -4,8 +4,20 @@ namespace QuanTAlib; /// Calculates the median value over a specified period. /// Provides a measure of central tendency that is robust to outliers. /// +/// +/// The Median indicator is particularly useful in financial analysis for: +/// - Providing a robust measure of central tendency that is less affected by extreme values than the mean. +/// - Identifying the middle value in a dataset, which can be helpful in understanding price distributions. +/// - Serving as a basis for other indicators or trading strategies that require a stable reference point. +/// +/// Unlike the mean, the median is not influenced by extreme outliers, making it valuable +/// in markets with occasional large price swings or in the presence of data anomalies. +/// public class Median : AbstractBase { + /// + /// The number of data points to consider for the median calculation. + /// private readonly int Period; private readonly CircularBuffer _buffer; @@ -41,6 +53,15 @@ public class Median : AbstractBase pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Resets the Median indicator to its initial state. + /// + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + /// /// Manages the state of the indicator. /// diff --git a/lib/statistics/Min.cs b/lib/statistics/Min.cs index 6eea3fad..5832bdc4 100644 --- a/lib/statistics/Min.cs +++ b/lib/statistics/Min.cs @@ -9,20 +9,52 @@ namespace QuanTAlib; /// The Min class uses a circular buffer to store values and calculates the minimum /// efficiently. It also implements a decay mechanism to adjust the minimum value over /// time, allowing for a more responsive indicator in changing market conditions. +/// +/// The decay factor allows the indicator to "forget" old minimum values gradually, +/// which can be useful in adapting to new price trends or market regimes. /// public class Min : AbstractBase { + /// + /// The number of data points to consider for the minimum calculation. + /// private readonly int Period; + + /// + /// Circular buffer to store the most recent data points. + /// private readonly CircularBuffer _buffer; + + /// + /// The half-life decay factor used to gradually forget old minimums. + /// private readonly double _halfLife; - private double _currentMin, _p_currentMin; - private int _timeSinceNewMin, _p_timeSinceNewMin; + + /// + /// The current minimum value. + /// + private double _currentMin; + + /// + /// The previous minimum value. + /// + private double _p_currentMin; + + /// + /// The number of periods since a new minimum was set. + /// + private int _timeSinceNewMin; + + /// + /// The previous value of _timeSinceNewMin. + /// + private int _p_timeSinceNewMin; /// /// Initializes a new instance of the Min class with the specified period and decay. /// /// The period over which to calculate the minimum value. - /// The decay factor to apply to older values (default is 0). + /// The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay). /// /// Thrown when period is less than 1 or decay is negative. /// @@ -49,7 +81,7 @@ public class Min : AbstractBase /// /// The source object to subscribe to for value updates. /// The period over which to calculate the minimum value. - /// The decay factor to apply to older values (default is 0). + /// The decay factor to apply to older values. Higher values cause faster forgetting of old minimums. Default is 0 (no decay). public Min(object source, int period, double decay = 0) : this(period, decay) { var pubEvent = source.GetType().GetEvent("Pub"); diff --git a/lib/statistics/Mode.cs b/lib/statistics/Mode.cs index 33438f7b..a12a1fd6 100644 --- a/lib/statistics/Mode.cs +++ b/lib/statistics/Mode.cs @@ -8,9 +8,17 @@ namespace QuanTAlib; /// The Mode class uses a circular buffer to store values and calculates the mode /// efficiently. Before the specified period is reached, it returns the average of /// the available values as an approximation. +/// +/// In financial analysis, the mode can be useful for: +/// - Identifying the most common price levels, which could indicate support or resistance. +/// - Analyzing the distribution of returns or other financial metrics. +/// - Detecting patterns in trading volume or other discrete financial data. /// public class Mode : AbstractBase { + /// + /// The number of data points to consider for the mode calculation. + /// private readonly int Period; private readonly CircularBuffer _buffer; @@ -45,6 +53,15 @@ public class Mode : AbstractBase pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } + /// + /// Resets the Mode indicator to its initial state. + /// + public override void Init() + { + base.Init(); + _buffer.Clear(); + } + /// /// Manages the state of the Mode instance based on whether a new value is being processed. /// diff --git a/lib/statistics/Percentile.cs b/lib/statistics/Percentile.cs index b15155c8..ad1bdba6 100644 --- a/lib/statistics/Percentile.cs +++ b/lib/statistics/Percentile.cs @@ -9,11 +9,25 @@ namespace QuanTAlib; /// percentile efficiently. It uses linear interpolation when the percentile falls /// between two data points. Before the specified period is reached, it returns the /// average of the available values as an approximation. +/// +/// In financial analysis, percentiles are useful for: +/// - Assessing the relative standing of a value within a distribution. +/// - Identifying outliers or extreme values in financial data. +/// - Creating risk measures, such as Value at Risk (VaR) calculations. +/// - Analyzing the distribution of returns, trading volumes, or other financial metrics. /// public class Percentile : AbstractBase { + /// + /// The number of data points to consider for the percentile calculation. + /// private readonly int Period; + + /// + /// The percentile to calculate (between 0 and 100). + /// private readonly double Percent; + private readonly CircularBuffer _buffer; /// @@ -36,7 +50,7 @@ public class Percentile : AbstractBase } Period = period; Percent = percent; - WarmupPeriod = 2; + WarmupPeriod = 2; // Minimum number of points needed for percentile calculation _buffer = new CircularBuffer(period); Name = $"Percentile(period={period}, percent={percent})"; Init(); @@ -125,4 +139,4 @@ public class Percentile : AbstractBase IsHot = _buffer.Count >= Period; return result; } -} \ No newline at end of file +} diff --git a/lib/statistics/Skew.cs b/lib/statistics/Skew.cs index 38e4e1be..75711882 100644 --- a/lib/statistics/Skew.cs +++ b/lib/statistics/Skew.cs @@ -9,9 +9,21 @@ namespace QuanTAlib; /// efficiently. It uses the adjusted Fisher-Pearson standardized moment coefficient /// for sample skewness calculation. A minimum of 3 data points is required for the /// calculation. +/// +/// In financial analysis, skewness is important for: +/// - Assessing the asymmetry of returns distribution. +/// - Evaluating the risk of extreme events in either direction. +/// - Complementing other risk measures like standard deviation. +/// - Informing investment decisions and risk management strategies. +/// +/// Positive skewness indicates a longer tail on the right side of the distribution, +/// while negative skewness indicates a longer tail on the left side. /// public class Skew : AbstractBase { + /// + /// The number of data points to consider for the skewness calculation. + /// private readonly int Period; private readonly CircularBuffer _buffer; @@ -79,6 +91,11 @@ public class Skew : AbstractBase /// to calculate the sample skewness. It requires at least 3 data points for the /// calculation. If there are fewer than 3 data points, or if the standard /// deviation is zero, the method returns 0. + /// + /// Interpretation of results: + /// - Positive values indicate right-skewed distribution (longer tail on the right side). + /// - Negative values indicate left-skewed distribution (longer tail on the left side). + /// - Values close to 0 suggest a relatively symmetric distribution. /// protected override double Calculation() { @@ -117,4 +134,4 @@ public class Skew : AbstractBase IsHot = _buffer.Count >= Period; return skew; } -} \ No newline at end of file +} diff --git a/lib/statistics/Slope.cs b/lib/statistics/Slope.cs index d235d83e..e8681069 100644 --- a/lib/statistics/Slope.cs +++ b/lib/statistics/Slope.cs @@ -7,15 +7,37 @@ namespace QuanTAlib; /// The Slope class calculates the slope of a linear regression line, along with other /// statistical measures such as intercept, standard deviation, R-squared, and the last /// point on the regression line. It uses the least squares method for calculation. +/// +/// In financial analysis, slope is important for: +/// - Identifying trends in price movements or other financial metrics. +/// - Measuring the rate of change in a financial time series. +/// - Assessing the strength and direction of relationships between variables. +/// - Supporting technical analysis indicators and trading strategies. /// public class Slope : AbstractBase { private readonly int _period; private readonly CircularBuffer _buffer; private readonly CircularBuffer _timeBuffer; + + /// + /// Gets the y-intercept of the regression line. + /// public double? Intercept { get; private set; } + + /// + /// Gets the standard deviation of the y-values. + /// public double? StdDev { get; private set; } + + /// + /// Gets the R-squared value, indicating the goodness of fit of the regression line. + /// public double? RSquared { get; private set; } + + /// + /// Gets the y-value of the last point on the regression line. + /// public double? Line { get; private set; } /// @@ -90,6 +112,13 @@ public class Slope : AbstractBase /// It also calculates and updates the Intercept, StdDev, RSquared, and Line properties. /// If there are fewer than 2 data points, or if the sum of squared x deviations is 0, /// the method returns 0 and sets the additional properties to null. + /// + /// Interpretation of results: + /// - Positive slope: Indicates an upward trend in the data. + /// - Negative slope: Indicates a downward trend in the data. + /// - Slope close to 0: Indicates a relatively flat or no clear trend in the data. + /// The magnitude of the slope represents the rate of change in the dependent variable + /// (y) for each unit change in the independent variable (x). /// protected override double Calculation() { diff --git a/lib/statistics/Stddev.cs b/lib/statistics/Stddev.cs index 247413dd..aae6fb20 100644 --- a/lib/statistics/Stddev.cs +++ b/lib/statistics/Stddev.cs @@ -8,10 +8,23 @@ namespace QuanTAlib; /// The Stddev class calculates either the population standard deviation or the sample /// standard deviation based on the isPopulation parameter. It uses a circular buffer /// to efficiently manage the data points within the specified period. +/// +/// In financial analysis, standard deviation is important for: +/// - Measuring volatility of financial instruments or portfolios. +/// - Assessing risk in investments. +/// - Calculating Sharpe ratios and other risk-adjusted performance measures. +/// - Identifying potential outliers or unusual market behavior. /// public class Stddev : AbstractBase { + /// + /// Indicates whether to calculate population (true) or sample (false) standard deviation. + /// private readonly bool IsPopulation; + + /// + /// Circular buffer to store the most recent data points. + /// private readonly CircularBuffer _buffer; /// @@ -87,6 +100,11 @@ public class Stddev : AbstractBase /// sqrt(sum((x - mean)^2) / (n - 1)) for sample, /// where x is each value, mean is the average of all values, and n is the number of values. /// If there's only one value in the buffer, the method returns 0. + /// + /// Interpretation of results: + /// - A low standard deviation indicates that the values tend to be close to the mean. + /// - A high standard deviation indicates that the values are spread out over a wider range. + /// - In financial contexts, higher standard deviation often implies higher volatility or risk. /// protected override double Calculation() { diff --git a/lib/statistics/Variance.cs b/lib/statistics/Variance.cs index a7c6f3e0..e1bc4c67 100644 --- a/lib/statistics/Variance.cs +++ b/lib/statistics/Variance.cs @@ -8,10 +8,23 @@ namespace QuanTAlib; /// The Variance class calculates either the population variance or the sample /// variance based on the isPopulation parameter. It uses a circular buffer /// to efficiently manage the data points within the specified period. +/// +/// In financial analysis, variance is important for: +/// - Measuring the dispersion of returns around the mean. +/// - Assessing risk and volatility in financial instruments or portfolios. +/// - Serving as a basis for other risk measures like standard deviation and beta. +/// - Contributing to portfolio optimization techniques, such as Modern Portfolio Theory. /// public class Variance : AbstractBase { + /// + /// Indicates whether to calculate population (true) or sample (false) variance. + /// private readonly bool IsPopulation; + + /// + /// Circular buffer to store the most recent data points. + /// private readonly CircularBuffer _buffer; /// @@ -87,6 +100,12 @@ public class Variance : AbstractBase /// sum((x - mean)^2) / (n - 1) for sample, /// where x is each value, mean is the average of all values, and n is the number of values. /// If there's only one value in the buffer, the method returns 0. + /// + /// Interpretation of results: + /// - A low variance indicates that the values tend to be close to the mean and to each other. + /// - A high variance indicates that the values are spread out over a wider range. + /// - In financial contexts, higher variance often implies higher volatility or risk. + /// - Variance is always non-negative, and its units are squared units of the original data. /// protected override double Calculation() { diff --git a/lib/statistics/Zscore.cs b/lib/statistics/Zscore.cs index 946a8e34..de94054c 100644 --- a/lib/statistics/Zscore.cs +++ b/lib/statistics/Zscore.cs @@ -8,10 +8,23 @@ namespace QuanTAlib; /// The Zscore class calculates the Z-score (also known as standard score) for /// the most recent value in a given period. It uses a circular buffer to /// efficiently manage the data points within the specified period. +/// +/// In financial analysis, Z-score is important for: +/// - Identifying outliers or unusual price movements. +/// - Normalizing data across different scales or time periods. +/// - Assessing the relative position of a value within its historical distribution. +/// - Supporting trading strategies based on mean reversion or momentum. /// public class Zscore : AbstractBase { + /// + /// The number of data points to consider for the Z-score calculation. + /// private readonly int Period; + + /// + /// Circular buffer to store the most recent data points. + /// private readonly CircularBuffer _buffer; /// @@ -78,6 +91,14 @@ public class Zscore : AbstractBase /// Z = (x - μ) / σ /// where x is the input value, μ is the mean of the period, and σ is the sample standard deviation. /// If there are fewer than 2 data points or if the standard deviation is 0, the method returns 0. + /// + /// Interpretation of results: + /// - A Z-score of 0 indicates that the data point is exactly on the mean. + /// - A positive Z-score indicates the data point is above the mean. + /// - A negative Z-score indicates the data point is below the mean. + /// - The magnitude of the Z-score represents how many standard deviations away from the mean the data point is. + /// - In a normal distribution, about 68% of the values have a Z-score between -1 and 1, + /// 95% between -2 and 2, and 99.7% between -3 and 3. /// protected override double Calculation() { @@ -104,4 +125,4 @@ public class Zscore : AbstractBase IsHot = _buffer.Count >= Period; return zScore; } -} \ No newline at end of file +} diff --git a/notebooks/means.dib b/notebooks/means.dib index 5e4455f2..3ac96ef8 100644 --- a/notebooks/means.dib +++ b/notebooks/means.dib @@ -11,8 +11,8 @@ QuanTAlib.Formatters.Initialize(); #!csharp TSeries input = new(); -Sma ma1 = new (6); -Sma ma2 = new (input, 6); +Beta ma1 = new (6); +Beta ma2 = new (input, 6); Random random = new Random(); diff --git a/quantower/Averages/AfirmaIndicator.cs b/quantower/Averages/AfirmaIndicator.cs index 44449832..3163b30b 100644 --- a/quantower/Averages/AfirmaIndicator.cs +++ b/quantower/Averages/AfirmaIndicator.cs @@ -9,8 +9,6 @@ public class AfirmaIndicator : IndicatorBase [InputParameter("Periods for lowpass cutoff", sortIndex: 2, 1, 2000, 1, 0)] public int Periods { get; set; } = 6; - - [InputParameter("Window Type", sortIndex: 3, variants: [ "Rectangular", Afirma.WindowType.Rectangular, "Hanning", Afirma.WindowType.Hanning1, @@ -32,6 +30,8 @@ public class AfirmaIndicator : IndicatorBase protected override void InitIndicator() { + base.InitIndicator(); ma = new Afirma(periods: Periods, taps: Taps, window: Window); } -} \ No newline at end of file + +} diff --git a/quantower/Averages/AlmaIndicator.cs b/quantower/Averages/AlmaIndicator.cs index f1daddab..7843a0c5 100644 --- a/quantower/Averages/AlmaIndicator.cs +++ b/quantower/Averages/AlmaIndicator.cs @@ -18,6 +18,8 @@ public class AlmaIndicator : IndicatorBase public AlmaIndicator() : base() { Name = "ALMA - Arnaud Legoux Moving Average"; + Description = "Arnaud Legoux Moving Average"; + } protected override void InitIndicator() diff --git a/quantower/Averages/Averages.csproj b/quantower/Averages/Averages.csproj index 29752926..692601e3 100644 --- a/quantower/Averages/Averages.csproj +++ b/quantower/Averages/Averages.csproj @@ -5,28 +5,26 @@ true true true + false - - lib\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - %(Filename)%(Extension) - + + + - ..\..\.github\TradingPlatform.BusinessLayer.dll + ..\..\.github\TradingPlatform.BusinessLayer.dll TradingPlatform.BusinessLayer.xml - \ No newline at end of file + + + + + diff --git a/quantower/Averages/DemaIndicator.cs b/quantower/Averages/DemaIndicator.cs index f943f289..f11d5ce4 100644 --- a/quantower/Averages/DemaIndicator.cs +++ b/quantower/Averages/DemaIndicator.cs @@ -12,6 +12,7 @@ public class DemaIndicator : IndicatorBase public DemaIndicator() : base() { Name = "DEMA - Double Exponential Moving Average"; + Description = "A faster-responding moving average that reduces lag by applying the EMA twice."; } protected override void InitIndicator() diff --git a/quantower/Averages/DsmaIndicator.cs b/quantower/Averages/DsmaIndicator.cs index e4259399..cc14789d 100644 --- a/quantower/Averages/DsmaIndicator.cs +++ b/quantower/Averages/DsmaIndicator.cs @@ -15,6 +15,7 @@ public class DsmaIndicator : IndicatorBase public DsmaIndicator() : base() { Name = "DSMA - Deviation Scaled Moving Average"; + Description = "A moving average that adjusts its responsiveness based on price deviations from the mean."; } protected override void InitIndicator() diff --git a/quantower/Averages/DwmaIndicator.cs b/quantower/Averages/DwmaIndicator.cs index 2126c023..a1f3be2b 100644 --- a/quantower/Averages/DwmaIndicator.cs +++ b/quantower/Averages/DwmaIndicator.cs @@ -10,10 +10,10 @@ public class DwmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"DWMA {Period} : {SourceName}"; - public DwmaIndicator() : base() { Name = "DWMA - Double Weighted Moving Average"; + Description = "A moving average that applies double weighting to recent prices for increased responsiveness."; } protected override void InitIndicator() diff --git a/quantower/Averages/EmaIndicator.cs b/quantower/Averages/EmaIndicator.cs index ab07c5b0..5d8fc937 100644 --- a/quantower/Averages/EmaIndicator.cs +++ b/quantower/Averages/EmaIndicator.cs @@ -16,7 +16,7 @@ public class EmaIndicator : IndicatorBase public EmaIndicator() : base() { Name = "EMA - Exponential Moving Average"; - Description = "Exponential Moving Average"; + Description = "Moving average that gives more weight to recent prices, reducing lag in trend following."; } protected override void InitIndicator() diff --git a/quantower/Averages/EpmaIndicator.cs b/quantower/Averages/EpmaIndicator.cs index 239fba4f..5fee343d 100644 --- a/quantower/Averages/EpmaIndicator.cs +++ b/quantower/Averages/EpmaIndicator.cs @@ -13,6 +13,7 @@ public class EpmaIndicator : IndicatorBase public EpmaIndicator() : base() { Name = "EPMA - Endpoint Moving Average"; + Description = "Moving average that emphasizes the most recent data point, useful for identifying trend changes."; } protected override void InitIndicator() diff --git a/quantower/Averages/FramaIndicator.cs b/quantower/Averages/FramaIndicator.cs index 0e5e8d1b..9a1959ca 100644 --- a/quantower/Averages/FramaIndicator.cs +++ b/quantower/Averages/FramaIndicator.cs @@ -10,10 +10,10 @@ public class FramaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"FRAMA {Period} : {SourceName}"; - public FramaIndicator() : base() { Name = "FRAMA - Fractal Adaptive Moving Average"; + Description = "Adaptive moving average that adjusts its smoothing based on market fractal dimension."; } protected override void InitIndicator() diff --git a/quantower/Averages/FwmaIndicator.cs b/quantower/Averages/FwmaIndicator.cs index 5d24849b..4dee3a20 100644 --- a/quantower/Averages/FwmaIndicator.cs +++ b/quantower/Averages/FwmaIndicator.cs @@ -10,10 +10,10 @@ public class FwmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"FWMA {Period} : {SourceName}"; - public FwmaIndicator() : base() { Name = "FWMA - Fibonacci-Weighted Moving Average"; + Description = "Moving average that uses Fibonacci sequence for weighting, emphasizing recent and key historical prices."; } protected override void InitIndicator() diff --git a/quantower/Averages/GmaIndicator.cs b/quantower/Averages/GmaIndicator.cs index 4d794534..cbbd2ad0 100644 --- a/quantower/Averages/GmaIndicator.cs +++ b/quantower/Averages/GmaIndicator.cs @@ -10,10 +10,10 @@ public class GmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"GMA {Period} : {SourceName}"; - public GmaIndicator() : base() { Name = "GMA - Gaussian-Weighted Moving Average"; + Description = "Moving average using Gaussian distribution for weighting, balancing recent and historical data."; } protected override void InitIndicator() diff --git a/quantower/Averages/HmaIndicator.cs b/quantower/Averages/HmaIndicator.cs index 2dd2b472..e255579c 100644 --- a/quantower/Averages/HmaIndicator.cs +++ b/quantower/Averages/HmaIndicator.cs @@ -10,10 +10,10 @@ public class HmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"HMA {Period} : {SourceName}"; - public HmaIndicator() : base() { Name = "HMA - Hull Moving Average"; + Description = "Responsive moving average that reduces lag while maintaining smoothness in price action."; } protected override void InitIndicator() diff --git a/quantower/Averages/HtitIndicator.cs b/quantower/Averages/HtitIndicator.cs index 8d0f4009..b619134f 100644 --- a/quantower/Averages/HtitIndicator.cs +++ b/quantower/Averages/HtitIndicator.cs @@ -10,6 +10,7 @@ public class HtitIndicator : IndicatorBase public HtitIndicator() : base() { Name = "HTIT - Hilbert Transform Instantaneous Trendline"; + Description = "Uses Hilbert Transform to identify the dominant cycle and generate a smooth, lag-free trendline."; } protected override void InitIndicator() diff --git a/quantower/Averages/HwmaIndicator.cs b/quantower/Averages/HwmaIndicator.cs index e777af3c..4c36f17a 100644 --- a/quantower/Averages/HwmaIndicator.cs +++ b/quantower/Averages/HwmaIndicator.cs @@ -16,17 +16,14 @@ public class HwmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"HWMA {nA:F2} : {nB:F2} : {nC:F2} : {SourceName}"; - public HwmaIndicator() : base() { Name = "HWMA - Holt-Winter Moving Average"; + Description = "Triple exponential moving average that accounts for level, trend, and seasonal components."; } protected override void InitIndicator() { - //nA = 2 / (1 + (double)Period); - //nB = 1 / (double)Period; - //nC = 1 / (double)Period; ma = new Hwma(nA: nA, nB: nB, nC: nC); base.InitIndicator(); } diff --git a/quantower/Averages/JmaIndicator.cs b/quantower/Averages/JmaIndicator.cs index 88a0a2ea..a80f9fc7 100644 --- a/quantower/Averages/JmaIndicator.cs +++ b/quantower/Averages/JmaIndicator.cs @@ -12,10 +12,10 @@ public class JmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"JMA {Period} : {Phase} : {SourceName}"; - public JmaIndicator() : base() { Name = "JMA - Jurik Moving Average"; + Description = "Adaptive moving average with reduced lag and noise, adjustable smoothness and phase shift."; } protected override void InitIndicator() diff --git a/quantower/Averages/KamaIndicator.cs b/quantower/Averages/KamaIndicator.cs index 21587ab1..028ca796 100644 --- a/quantower/Averages/KamaIndicator.cs +++ b/quantower/Averages/KamaIndicator.cs @@ -14,10 +14,10 @@ public class KamaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"KAMA {Period} : {Fast} : {Slow} : {SourceName}"; - public KamaIndicator() : base() { Name = "KAMA - Kaufman's Adaptive Moving Average"; + Description = "Adaptive moving average that adjusts to market volatility, reducing lag in trending markets."; } protected override void InitIndicator() diff --git a/quantower/Averages/LtmaIndicator.cs b/quantower/Averages/LtmaIndicator.cs index d786d0bb..ebf29dc5 100644 --- a/quantower/Averages/LtmaIndicator.cs +++ b/quantower/Averages/LtmaIndicator.cs @@ -13,6 +13,7 @@ public class LtmaIndicator : IndicatorBase public LtmaIndicator() : base() { Name = "LTMA - Laguerre Transform Moving Average"; + Description = "Moving average using Laguerre polynomials, offering adjustable smoothing and lag reduction."; } protected override void InitIndicator() diff --git a/quantower/Averages/MaafIndicator.cs b/quantower/Averages/MaafIndicator.cs index 4ab9e52d..49a94d01 100644 --- a/quantower/Averages/MaafIndicator.cs +++ b/quantower/Averages/MaafIndicator.cs @@ -7,7 +7,7 @@ public class MaafIndicator : IndicatorBase public int Period { get; set; } = 39; [InputParameter("Threshold", sortIndex: 5, minimum: 0, maximum: 1, increment: 0.001, decimalPlaces: 3)] - public double Threshold = 0.002; + private double Threshold { get; set; } = 0.002; private Maaf? ma; protected override AbstractBase QuanTAlib => ma!; @@ -16,11 +16,12 @@ public class MaafIndicator : IndicatorBase public MaafIndicator() : base() { Name = "MAAF - Median-Average Adaptive Filter"; + Description = "Adaptive filter combining median and average, reducing noise while preserving trend responsiveness."; } protected override void InitIndicator() { base.InitIndicator(); - ma = new Maaf(Period: Period, Threshold: Threshold); + ma = new Maaf(period: Period, threshold: Threshold); } } diff --git a/quantower/Averages/MamaIndicator.cs b/quantower/Averages/MamaIndicator.cs index d3e14f30..3f243e0a 100644 --- a/quantower/Averages/MamaIndicator.cs +++ b/quantower/Averages/MamaIndicator.cs @@ -11,10 +11,10 @@ public class MamaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"MAMA : {Fast} : {Slow} : {SourceName}"; - public MamaIndicator() : base() { Name = "MAMA - MESA Adaptive Moving Average"; + Description = "Adaptive moving average using MESA algorithm to adjust to market cycles and reduce lag."; } protected override void InitIndicator() diff --git a/quantower/Averages/MgdiIndicator.cs b/quantower/Averages/MgdiIndicator.cs index a9ed0bb0..525c449b 100644 --- a/quantower/Averages/MgdiIndicator.cs +++ b/quantower/Averages/MgdiIndicator.cs @@ -9,15 +9,14 @@ public class MgdiIndicator : IndicatorBase [InputParameter("k Factor", sortIndex: 2, minimum: 0.0, maximum: 1.0, increment: 0.1, decimalPlaces: 2)] public double kfactor { get; set; } = 0.6; - private Mgdi? ma; protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"MGDI {Period} : {kfactor:F2} : {SourceName}"; - public MgdiIndicator() : base() { Name = "MGDI - McGinley Dynamic Index"; + Description = "Adaptive moving average that adjusts to market speed, reducing whipsaws in trending markets."; } protected override void InitIndicator() diff --git a/quantower/Averages/MmaIndicator.cs b/quantower/Averages/MmaIndicator.cs index bf174265..b37cc874 100644 --- a/quantower/Averages/MmaIndicator.cs +++ b/quantower/Averages/MmaIndicator.cs @@ -13,6 +13,7 @@ public class MmaIndicator : IndicatorBase public MmaIndicator() : base() { Name = "MMA - Modified Moving Average"; + Description = "Variation of EMA that reduces lag and smooths price action, balancing responsiveness and stability."; } protected override void InitIndicator() diff --git a/quantower/Averages/PwmaIndicator.cs b/quantower/Averages/PwmaIndicator.cs index 29d7bdcc..fac30512 100644 --- a/quantower/Averages/PwmaIndicator.cs +++ b/quantower/Averages/PwmaIndicator.cs @@ -13,6 +13,7 @@ public class PwmaIndicator : IndicatorBase public PwmaIndicator() : base() { Name = "PWMA - Pascal's Weighted Moving Average"; + Description = "Moving average using Pascal's triangle coefficients, emphasizing recent data with smooth transitions."; } protected override void InitIndicator() diff --git a/quantower/Averages/QemaIndicator.cs b/quantower/Averages/QemaIndicator.cs index e6345c15..c4403eea 100644 --- a/quantower/Averages/QemaIndicator.cs +++ b/quantower/Averages/QemaIndicator.cs @@ -19,7 +19,7 @@ public class QemaIndicator : IndicatorBase public QemaIndicator() : base() { Name = "QEMA - Quad Exponential Moving Average"; - Description = "Quad Exponential Moving Average"; + Description = "Combines four EMAs with different smoothing factors to reduce lag and improve trend following."; } protected override void InitIndicator() diff --git a/quantower/Averages/RemaIndicator.cs b/quantower/Averages/RemaIndicator.cs index 26193da4..c8f50273 100644 --- a/quantower/Averages/RemaIndicator.cs +++ b/quantower/Averages/RemaIndicator.cs @@ -16,6 +16,7 @@ public class RemaIndicator : IndicatorBase public RemaIndicator() : base() { Name = "REMA - Regularized Exponential Moving Average"; + Description = "EMA variant with regularization to reduce noise and improve stability in volatile markets."; } protected override void InitIndicator() diff --git a/quantower/Averages/RmaIndicator.cs b/quantower/Averages/RmaIndicator.cs index a89e15a6..41b9e333 100644 --- a/quantower/Averages/RmaIndicator.cs +++ b/quantower/Averages/RmaIndicator.cs @@ -10,10 +10,10 @@ public class RmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"RMA {Period} : {SourceName}"; - public RmaIndicator() : base() { - Name = "RMA - wildeR Moving Average"; + Name = "RMA - Wilder's Moving Average"; + Description = "Smoothed moving average that reduces whipsaws, commonly used in RSI calculations."; } protected override void InitIndicator() diff --git a/quantower/Averages/SinemaIndicator.cs b/quantower/Averages/SinemaIndicator.cs index 2f3b231f..3ebeb128 100644 --- a/quantower/Averages/SinemaIndicator.cs +++ b/quantower/Averages/SinemaIndicator.cs @@ -13,6 +13,7 @@ public class SinemaIndicator : IndicatorBase public SinemaIndicator() : base() { Name = "SINEMA - Sine-Weighted Moving Average"; + Description = "Moving average using sine function for weighting, balancing recent and historical price data."; } protected override void InitIndicator() diff --git a/quantower/Averages/SmaIndicator.cs b/quantower/Averages/SmaIndicator.cs index 3cbf26fa..a90e30a5 100644 --- a/quantower/Averages/SmaIndicator.cs +++ b/quantower/Averages/SmaIndicator.cs @@ -10,10 +10,10 @@ public class SmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"SMA {Period} : {SourceName}"; - public SmaIndicator() : base() { Name = "SMA - Simple Moving Average"; + Description = "Basic moving average that calculates the arithmetic mean of prices over a specified period."; } protected override void InitIndicator() diff --git a/quantower/Averages/SmmaIndicator.cs b/quantower/Averages/SmmaIndicator.cs index a7516e22..49186bfe 100644 --- a/quantower/Averages/SmmaIndicator.cs +++ b/quantower/Averages/SmmaIndicator.cs @@ -10,10 +10,10 @@ public class SmmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"SMMA {Period} : {SourceName}"; - public SmmaIndicator() : base() { Name = "SMMA - Smoothed Moving Average"; + Description = "Moving average that gives more weight to recent data while retaining all historical data."; } protected override void InitIndicator() diff --git a/quantower/Averages/T3Indicator.cs b/quantower/Averages/T3Indicator.cs index 5b8f4766..81dac3f4 100644 --- a/quantower/Averages/T3Indicator.cs +++ b/quantower/Averages/T3Indicator.cs @@ -19,6 +19,7 @@ public class T3Indicator : IndicatorBase public T3Indicator() : base() { Name = "T3 - Tillson T3 Moving Average"; + Description = "Triple exponential moving average with reduced lag and smoothing, adjustable via volume factor."; } protected override void InitIndicator() diff --git a/quantower/Averages/TemaIndicator.cs b/quantower/Averages/TemaIndicator.cs index 57d50da3..236e893c 100644 --- a/quantower/Averages/TemaIndicator.cs +++ b/quantower/Averages/TemaIndicator.cs @@ -13,6 +13,7 @@ public class TemaIndicator : IndicatorBase public TemaIndicator() : base() { Name = "TEMA - Triple Exponential Moving Average"; + Description = "Moving average that applies EMA three times to reduce lag and improve responsiveness to trends."; } protected override void InitIndicator() diff --git a/quantower/Averages/TrimaIndicator.cs b/quantower/Averages/TrimaIndicator.cs index b0612356..2ac8d091 100644 --- a/quantower/Averages/TrimaIndicator.cs +++ b/quantower/Averages/TrimaIndicator.cs @@ -10,10 +10,10 @@ public class TrimaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"TRIMA {Period} : {SourceName}"; - public TrimaIndicator() : base() { Name = "TRIMA - Triangular Moving Average"; + Description = "Weighted moving average giving more importance to the middle of the period for smoother output."; } protected override void InitIndicator() diff --git a/quantower/Averages/VidyaIndicator.cs b/quantower/Averages/VidyaIndicator.cs index a9ed10ad..b45c117c 100644 --- a/quantower/Averages/VidyaIndicator.cs +++ b/quantower/Averages/VidyaIndicator.cs @@ -14,10 +14,10 @@ public class VidyaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"VIDYA {Period} : {SourceName}"; - public VidyaIndicator() : base() { Name = "VIDYA - Variable Index Dynamic Average"; + Description = "Adaptive moving average that adjusts based on market volatility for improved trend following."; } protected override void InitIndicator() diff --git a/quantower/Averages/WmaIndicator.cs b/quantower/Averages/WmaIndicator.cs index 5cc6a396..aec32407 100644 --- a/quantower/Averages/WmaIndicator.cs +++ b/quantower/Averages/WmaIndicator.cs @@ -10,10 +10,10 @@ public class WmaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"WMA {Period} : {SourceName}"; - public WmaIndicator() : base() { Name = "WMA - Weighted Moving Average"; + Description = "Moving average that assigns higher weights to recent data points for improved responsiveness."; } protected override void InitIndicator() diff --git a/quantower/Averages/ZlemaIndicator.cs b/quantower/Averages/ZlemaIndicator.cs index c42b045c..f89c8188 100644 --- a/quantower/Averages/ZlemaIndicator.cs +++ b/quantower/Averages/ZlemaIndicator.cs @@ -10,10 +10,10 @@ public class ZlemaIndicator : IndicatorBase protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"ZLEMA {Period} : {SourceName}"; - public ZlemaIndicator() : base() { - Name = "ZLEMA - Weighted Moving Average"; + Name = "ZLEMA - Zero-Lag Exponential Moving Average"; + Description = "EMA variant that reduces lag by using linear extrapolation, providing faster response to price changes."; } protected override void InitIndicator() diff --git a/quantower/Statistics/CurvatureIndicator.cs b/quantower/Statistics/CurvatureIndicator.cs index 53764aca..2ef393e0 100644 --- a/quantower/Statistics/CurvatureIndicator.cs +++ b/quantower/Statistics/CurvatureIndicator.cs @@ -13,6 +13,7 @@ public class CurvatureIndicator : IndicatorBase public CurvatureIndicator() { Name = "CURVATURE - Rate of Change of Slope"; + Description = "Measures the rate of change of the slope, indicating acceleration or deceleration in price movement."; SeparateWindow = true; } @@ -21,4 +22,4 @@ public class CurvatureIndicator : IndicatorBase curvature = new(Period); MinHistoryDepths = curvature.WarmupPeriod; } -} \ No newline at end of file +} diff --git a/quantower/Statistics/EntropyIndicator.cs b/quantower/Statistics/EntropyIndicator.cs index 8e141c01..5614c288 100644 --- a/quantower/Statistics/EntropyIndicator.cs +++ b/quantower/Statistics/EntropyIndicator.cs @@ -13,6 +13,7 @@ public class EntropyIndicator : IndicatorBase public EntropyIndicator() : base() { Name = "ENTROPY - Entropy"; + Description = "Measures the randomness or uncertainty in price movements, useful for identifying market phases."; SeparateWindow = true; } @@ -22,4 +23,4 @@ public class EntropyIndicator : IndicatorBase MinHistoryDepths = entropy.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/KurtosisIndicator.cs b/quantower/Statistics/KurtosisIndicator.cs index c9d78068..086bb61d 100644 --- a/quantower/Statistics/KurtosisIndicator.cs +++ b/quantower/Statistics/KurtosisIndicator.cs @@ -13,6 +13,7 @@ public class KurtosisIndicator : IndicatorBase public KurtosisIndicator() : base() { Name = "KURTOSIS - Relative Flatness"; + Description = "Measures the 'tailedness' of price distribution, indicating potential for extreme market movements."; SeparateWindow = true; } @@ -22,4 +23,4 @@ public class KurtosisIndicator : IndicatorBase MinHistoryDepths = kurtosis.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/MaxIndicator.cs b/quantower/Statistics/MaxIndicator.cs index e0b6200a..b8af5012 100644 --- a/quantower/Statistics/MaxIndicator.cs +++ b/quantower/Statistics/MaxIndicator.cs @@ -15,7 +15,8 @@ public class MaxIndicator : IndicatorBase public MaxIndicator() : base() { - Name = "MAX - Maximum value (with decay) "; + Name = "MAX - Maximum value (with decay)"; + Description = "Tracks the maximum value over a period, with a decay factor to gradually adjust to new highs."; } protected override void InitIndicator() diff --git a/quantower/Statistics/MedianIndicator.cs b/quantower/Statistics/MedianIndicator.cs index e2e29d98..05d500c4 100644 --- a/quantower/Statistics/MedianIndicator.cs +++ b/quantower/Statistics/MedianIndicator.cs @@ -12,6 +12,7 @@ public class MedianIndicator : IndicatorBase public MedianIndicator() : base() { Name = "MEDIAN - Median historical value"; + Description = "Calculates the middle value of price data over a specified period, less affected by outliers than mean."; } protected override void InitIndicator() @@ -20,4 +21,4 @@ public class MedianIndicator : IndicatorBase MinHistoryDepths = med.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/MinIndicator.cs b/quantower/Statistics/MinIndicator.cs index a4c14e9e..3399bdc7 100644 --- a/quantower/Statistics/MinIndicator.cs +++ b/quantower/Statistics/MinIndicator.cs @@ -15,6 +15,7 @@ public class MinIndicator : IndicatorBase public MinIndicator() : base() { Name = "MIN - Minimum value (with decay)"; + Description = "Tracks the minimum value over a period, with a decay factor to gradually adjust to new lows."; } protected override void InitIndicator() @@ -24,4 +25,4 @@ public class MinIndicator : IndicatorBase Source = 3; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/ModeIndicator.cs b/quantower/Statistics/ModeIndicator.cs index c293b643..e14b17ad 100644 --- a/quantower/Statistics/ModeIndicator.cs +++ b/quantower/Statistics/ModeIndicator.cs @@ -12,6 +12,7 @@ public class ModeIndicator : IndicatorBase public ModeIndicator() : base() { Name = "MODE - Most frequent historical value"; + Description = "Identifies the most frequently occurring price value over a specified period, indicating price clusters."; } protected override void InitIndicator() @@ -20,4 +21,4 @@ public class ModeIndicator : IndicatorBase MinHistoryDepths = mode.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/PercentileIndicator.cs b/quantower/Statistics/PercentileIndicator.cs index dacb2fd9..0e4a5e91 100644 --- a/quantower/Statistics/PercentileIndicator.cs +++ b/quantower/Statistics/PercentileIndicator.cs @@ -14,7 +14,8 @@ public class PercentileIndicator : IndicatorBase public PercentileIndicator() : base() { - Name = "PERCENTILE - n-th Percentile "; + Name = "PERCENTILE - n-th Percentile"; + Description = "Calculates the value below which a given percentage of observations falls within a specified period."; SeparateWindow = false; } @@ -24,5 +25,4 @@ public class PercentileIndicator : IndicatorBase MinHistoryDepths = percentile.WarmupPeriod; base.InitIndicator(); } - -} \ No newline at end of file +} diff --git a/quantower/Statistics/SkewIndicator.cs b/quantower/Statistics/SkewIndicator.cs index 7cce283e..7cf67cb9 100644 --- a/quantower/Statistics/SkewIndicator.cs +++ b/quantower/Statistics/SkewIndicator.cs @@ -1,4 +1,3 @@ - using TradingPlatform.BusinessLayer; namespace QuanTAlib; @@ -14,6 +13,7 @@ public class SkewIndicator : IndicatorBase public SkewIndicator() : base() { Name = "SKEW - Skewness"; + Description = "Measures the asymmetry of price distribution, indicating potential trend direction or reversal."; SeparateWindow = true; } @@ -23,4 +23,4 @@ public class SkewIndicator : IndicatorBase MinHistoryDepths = skew.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/SlopeIndicator.cs b/quantower/Statistics/SlopeIndicator.cs index 7ad50cf5..7d6b65db 100644 --- a/quantower/Statistics/SlopeIndicator.cs +++ b/quantower/Statistics/SlopeIndicator.cs @@ -13,6 +13,7 @@ public class SlopeIndicator : IndicatorBase public SlopeIndicator() { Name = "SLOPE - Trend Slope"; + Description = "Measures the rate of change in price over a specified period, indicating trend strength and direction."; SeparateWindow = true; } @@ -21,4 +22,4 @@ public class SlopeIndicator : IndicatorBase slope = new(Period); MinHistoryDepths = slope.WarmupPeriod; } -} \ No newline at end of file +} diff --git a/quantower/Statistics/Statistics.csproj b/quantower/Statistics/Statistics.csproj index 696cb2a3..402cf09d 100644 --- a/quantower/Statistics/Statistics.csproj +++ b/quantower/Statistics/Statistics.csproj @@ -5,28 +5,26 @@ true true true + false - - lib\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - %(Filename)%(Extension) - + + + - ..\..\.github\TradingPlatform.BusinessLayer.dll + ..\..\.github\TradingPlatform.BusinessLayer.dll TradingPlatform.BusinessLayer.xml - \ No newline at end of file + + + + + diff --git a/quantower/Statistics/StddevIndicator.cs b/quantower/Statistics/StddevIndicator.cs index 6c9648c2..6be198f9 100644 --- a/quantower/Statistics/StddevIndicator.cs +++ b/quantower/Statistics/StddevIndicator.cs @@ -15,6 +15,7 @@ public class StddevIndicator : IndicatorBase public StddevIndicator() : base() { Name = "STDDEV - Standard Deviation"; + Description = "Measures price volatility by calculating the dispersion of prices from their average over a period."; SeparateWindow = true; } @@ -24,4 +25,4 @@ public class StddevIndicator : IndicatorBase MinHistoryDepths = stddev.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/VarianceIndictor.cs b/quantower/Statistics/VarianceIndicator.cs similarity index 86% rename from quantower/Statistics/VarianceIndictor.cs rename to quantower/Statistics/VarianceIndicator.cs index de983873..d55552ad 100644 --- a/quantower/Statistics/VarianceIndictor.cs +++ b/quantower/Statistics/VarianceIndicator.cs @@ -15,6 +15,7 @@ public class VarianceIndicator : IndicatorBase public VarianceIndicator() : base() { Name = "VAR - Variance"; + Description = "Measures the spread of price data around its mean, indicating volatility and potential trend changes."; SeparateWindow = true; } @@ -25,4 +26,4 @@ public class VarianceIndicator : IndicatorBase MinHistoryDepths = variance.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Statistics/ZscoreIndicator.cs b/quantower/Statistics/ZscoreIndicator.cs index 19727511..f9840d62 100644 --- a/quantower/Statistics/ZscoreIndicator.cs +++ b/quantower/Statistics/ZscoreIndicator.cs @@ -13,6 +13,7 @@ public class ZScoreIndicator : IndicatorBase public ZScoreIndicator() : base() { Name = "ZSCORE - Standard Score"; + Description = "Measures how many standard deviations a price is from the mean, indicating overbought/oversold levels."; SeparateWindow = true; } @@ -22,5 +23,4 @@ public class ZScoreIndicator : IndicatorBase MinHistoryDepths = zScore.WarmupPeriod; base.InitIndicator(); } - -} \ No newline at end of file +} diff --git a/quantower/Volatility/AtrIndicator.cs b/quantower/Volatility/AtrIndicator.cs index adaff835..1d79ffff 100644 --- a/quantower/Volatility/AtrIndicator.cs +++ b/quantower/Volatility/AtrIndicator.cs @@ -12,6 +12,7 @@ public class AtrIndicator : IndicatorBarBase public AtrIndicator() { Name = "ATR - Average True Range"; + Description = "Measures market volatility by calculating the average range between high and low prices."; SeparateWindow = true; } @@ -20,4 +21,4 @@ public class AtrIndicator : IndicatorBarBase atr = new(Period); MinHistoryDepths = atr!.WarmupPeriod; } -} \ No newline at end of file +} diff --git a/quantower/Volatility/HistoricalIndicator.cs b/quantower/Volatility/HistoricalIndicator.cs index 9196da81..8465714f 100644 --- a/quantower/Volatility/HistoricalIndicator.cs +++ b/quantower/Volatility/HistoricalIndicator.cs @@ -16,6 +16,7 @@ public class HistoricalIndicator : IndicatorBase public HistoricalIndicator() : base() { Name = "HV - Historical Volatility"; + Description = "Measures price fluctuations over time, indicating market volatility based on past price movements."; SeparateWindow = true; } @@ -25,4 +26,4 @@ public class HistoricalIndicator : IndicatorBase MinHistoryDepths = historical.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Volatility/RealizedIndicator.cs b/quantower/Volatility/RealizedIndicator.cs index 44039edd..86d7e000 100644 --- a/quantower/Volatility/RealizedIndicator.cs +++ b/quantower/Volatility/RealizedIndicator.cs @@ -16,6 +16,7 @@ public class RealizedIndicator : IndicatorBase public RealizedIndicator() : base() { Name = "RV - Realized Volatility"; + Description = "Measures actual price volatility over a specific period, useful for risk assessment and forecasting."; SeparateWindow = true; } @@ -25,4 +26,4 @@ public class RealizedIndicator : IndicatorBase MinHistoryDepths = realized.WarmupPeriod; base.InitIndicator(); } -} \ No newline at end of file +} diff --git a/quantower/Volatility/RviIndicator.cs b/quantower/Volatility/RviIndicator.cs index 7e85d13a..9c8de27d 100644 --- a/quantower/Volatility/RviIndicator.cs +++ b/quantower/Volatility/RviIndicator.cs @@ -13,12 +13,9 @@ public class RviIndicator : IndicatorBase public RviIndicator() : base() { Name = "RVI - Relative Volatility Index"; + Description = "Measures the direction of volatility, helping to identify overbought or oversold conditions in price."; SeparateWindow = true; - - // Adding upper and lower reference lines - //AddLineSeries("UpperLevel", 80, System.Drawing.Color.Gray, 1, LineStyle.Dot); - //AddLineSeries("LowerLevel", 20, System.Drawing.Color.Gray, 1, LineStyle.Dot); - } + } protected override void InitIndicator() { diff --git a/quantower/Volatility/Volatility.csproj b/quantower/Volatility/Volatility.csproj index 3bf9b4dc..3b829bd6 100644 --- a/quantower/Volatility/Volatility.csproj +++ b/quantower/Volatility/Volatility.csproj @@ -5,28 +5,26 @@ true true true + false - - lib\%(RecursiveDir)%(Filename)%(Extension) - - - - - - - %(Filename)%(Extension) - + + + - ..\..\.github\TradingPlatform.BusinessLayer.dll + ..\..\.github\TradingPlatform.BusinessLayer.dll TradingPlatform.BusinessLayer.xml - \ No newline at end of file + + + + + diff --git a/quantower/_IndicatorBase.cs b/quantower/_IndicatorBase.cs index 3b0cae62..37d70d06 100644 --- a/quantower/_IndicatorBase.cs +++ b/quantower/_IndicatorBase.cs @@ -3,10 +3,11 @@ using TradingPlatform.BusinessLayer; using TradingPlatform.BusinessLayer.Chart; using System.Runtime.CompilerServices; using System.Drawing.Drawing2D; -using QuanTAlib; using System.Collections; using TradingPlatform.BusinessLayer.TimeSync; +namespace QuanTAlib; + #pragma warning disable CA1416 // Validate platform compatibility public abstract class IndicatorBase : Indicator, IWatchlistIndicator {