using System.Buffers; using System.Runtime.CompilerServices; namespace QuanTAlib; /// /// ME: Mean Error (also known as Mean Bias Error) /// /// /// ME measures the average error between actual and predicted values, /// preserving the sign to indicate systematic bias in predictions. /// /// Formula: /// ME = (1/n) * Σ(actual - predicted) /// /// Key properties: /// - Can be positive or negative /// - Positive ME indicates under-prediction (actual > predicted) /// - Negative ME indicates over-prediction (actual < predicted) /// - ME = 0 indicates no systematic bias (but not necessarily accurate predictions) /// - Errors can cancel out, hiding large individual errors /// [SkipLocalsInit] public sealed class Me : BiInputIndicatorBase { /// /// Creates ME with specified period. /// /// Number of values to average (must be > 0) public Me(int period) : base(period, $"Me({period})") { } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override double ComputeError(double actual, double predicted) { // ME preserves sign: actual - predicted return actual - predicted; } /// /// Calculates ME for entire series. /// public static TSeries Batch(TSeries actual, TSeries predicted, int period) => CalculateImpl(actual, predicted, period, Batch); /// /// Batch calculation using signed error computation with rolling mean. /// public static void Batch(ReadOnlySpan actual, ReadOnlySpan predicted, Span output, int period) { ValidateBatchInputs(actual, predicted, output, period); int len = actual.Length; if (len == 0) { return; } const int StackAllocThreshold = 256; if (len <= StackAllocThreshold) { Span errors = stackalloc double[len]; ErrorHelpers.ComputeSignedErrors(actual, predicted, errors); ErrorHelpers.ApplyRollingMean(errors, output, period); } else { double[] rented = ArrayPool.Shared.Rent(len); try { Span errors = rented.AsSpan(0, len); ErrorHelpers.ComputeSignedErrors(actual, predicted, errors); ErrorHelpers.ApplyRollingMean(errors, output, period); } finally { ArrayPool.Shared.Return(rented); } } } public static (TSeries Results, Me Indicator) Calculate(TSeries actual, TSeries predicted, int period) { var indicator = new Me(period); TSeries results = Batch(actual, predicted, period); return (results, indicator); } }