namespace QuanTAlib; /// /// Calculates excess kurtosis using the Sheskin Algorithm. /// Measures the "tailedness" of the probability distribution of a real-valued random variable. /// public class Kurtosis : AbstractBase { private readonly int Period; private readonly CircularBuffer _buffer; /// /// Initializes a new instance of the Kurtosis class. /// /// The number of data points to consider for calculation. /// /// Thrown when the period is less than 4. /// public Kurtosis(int period) { if (period < 4) { throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 4 for kurtosis calculation."); } Period = period; WarmupPeriod = Period - 1; _buffer = new CircularBuffer(period); Name = $"Kurtosis(period={period})"; Init(); } /// /// Initializes a new instance of the Kurtosis class with a data source. /// /// The source object that publishes data. /// The number of data points to consider. public Kurtosis(object source, int period) : this(period) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } /// /// Resets the Kurtosis indicator to its initial state. /// public override void Init() { base.Init(); _buffer.Clear(); } /// /// Manages the state of the indicator. /// /// Indicates if the current data point is new. protected override void ManageState(bool isNew) { if (isNew) { _lastValidValue = Input.Value; _index++; } } /// /// Performs the kurtosis calculation. /// /// /// The calculated excess kurtosis. Positive for heavy-tailed distributions, /// negative for light-tailed distributions. /// /// /// Uses the Sheskin Algorithm for kurtosis calculation. /// Requires at least 4 data points for a valid calculation. /// protected override double Calculation() { ManageState(Input.IsNew); _buffer.Add(Input.Value, Input.IsNew); double kurtosis = 0; if (_buffer.Count > 3) { var values = _buffer.GetSpan().ToArray(); double mean = values.Average(); double n = values.Length; double s2 = 0; double s4 = 0; for (int i = 0; i < values.Length; i++) { double diff = values[i] - mean; s2 += diff * diff; s4 += diff * diff * diff * diff; } double variance = s2 / (n - 1); // Sheskin Algorithm kurtosis = (n * (n + 1) * s4) / (variance * variance * (n - 3) * (n - 1) * (n - 2)) - (3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3))); } IsHot = _buffer.Count >= Period; return kurtosis; } }