// <auto-generated />
// Generated from bindings/c/include/wickra.h. Do not edit by hand.
#nullable enable
using System.Runtime.InteropServices;

namespace Wickra;

public readonly record struct AccelerationBandsOutput(double Upper, double Middle, double Lower);
public readonly record struct AdxOutput(double PlusDi, double MinusDi, double Adx);
public readonly record struct AlligatorOutput(double Jaw, double Teeth, double Lips);
public readonly record struct AndrewsPitchforkOutput(double Median, double Upper, double Lower);
public readonly record struct AroonOutput(double Up, double Down);
public readonly record struct AtrBandsOutput(double Upper, double Middle, double Lower);
public readonly record struct AtrRatchetOutput(double Value, double Direction);
public readonly record struct AutoFibOutput(double Level0, double Level236, double Level382, double Level500, double Level618, double Level786, double Level1000);
public readonly record struct BollingerOutput(double Upper, double Middle, double Lower, double Stddev);
public readonly record struct BomarBandsOutput(double Upper, double Middle, double Lower);
public readonly record struct CamarillaPivotsOutput(double Pp, double R1, double R2, double R3, double R4, double S1, double S2, double S3, double S4);
public readonly record struct Candle(double Open, double High, double Low, double Close, double Volume, double Timestamp);
public readonly record struct CandleVolumeOutput(double Body, double Width);
public readonly record struct CentralPivotRangeOutput(double Pivot, double Tc, double Bc);
public readonly record struct ChandeKrollStopOutput(double StopLong, double StopShort);
public readonly record struct ChandelierExitOutput(double LongStop, double ShortStop);
public readonly record struct ClassicPivotsOutput(double Pp, double R1, double R2, double R3, double S1, double S2, double S3);
public readonly record struct CointegrationOutput(double HedgeRatio, double Spread, double AdfStat);
public readonly record struct CompositeProfileOutput(double Poc, double Vah, double Val);
public readonly record struct DemarkPivotsOutput(double Pp, double R1, double S1);
public readonly record struct DollarBar(double Open, double High, double Low, double Close, double Volume, double Dollar);
public readonly record struct DonchianOutput(double Upper, double Middle, double Lower);
public readonly record struct DonchianStopOutput(double StopLong, double StopShort);
public readonly record struct DoubleBollingerOutput(double UpperOuter, double UpperInner, double Middle, double LowerInner, double LowerOuter);
public readonly record struct ElderRayOutput(double BullPower, double BearPower);
public readonly record struct ElderSafeZoneOutput(double Value, double Direction);
public readonly record struct EquivolumeOutput(double Height, double Width);
public readonly record struct FibArcsOutput(double Arc382, double Arc500, double Arc618);
public readonly record struct FibChannelOutput(double Base, double Level618, double Level1000, double Level1618);
public readonly record struct FibConfluenceOutput(double Price, double Strength);
public readonly record struct FibExtensionOutput(double Level1272, double Level1414, double Level1618, double Level2000, double Level2618);
public readonly record struct FibFanOutput(double Fan382, double Fan500, double Fan618);
public readonly record struct FibProjectionOutput(double Level618, double Level1000, double Level1618, double Level2618);
public readonly record struct FibRetracementOutput(double Level0, double Level236, double Level382, double Level500, double Level618, double Level786, double Level1000);
public readonly record struct FibTimeZonesOutput(double OnZone, double BarsToNext);
public readonly record struct FibonacciPivotsOutput(double Pp, double R1, double R2, double R3, double S1, double S2, double S3);
public readonly record struct FootprintLevel(double Price, double BidVol, double AskVol);
public readonly record struct FractalChaosBandsOutput(double Upper, double Lower);
public readonly record struct GatorOscillatorOutput(double Upper, double Lower);
public readonly record struct GoldenPocketOutput(double Low, double Mid, double High);
public readonly record struct HeikinAshiOutput(double Open, double High, double Low, double Close);
public readonly record struct HighLowVolumeNodesOutput(double Hvn, double Lvn);
public readonly record struct HtPhasorOutput(double Inphase, double Quadrature);
public readonly record struct HurstChannelOutput(double Upper, double Middle, double Lower);
public readonly record struct IchimokuOutput(double Tenkan, double Kijun, double SenkouA, double SenkouB, double Chikou);
public readonly record struct ImbalanceBar(double Open, double High, double Low, double Close, double Imbalance, double Direction);
public readonly record struct InitialBalanceOutput(double High, double Low);
public readonly record struct KagiBar(double Start, double End, double Direction);
public readonly record struct KalmanHedgeRatioOutput(double HedgeRatio, double Intercept, double Spread);
public readonly record struct KaseDevStopOutput(double Value, double Direction);
public readonly record struct KasePermissionStochasticOutput(double Fast, double Slow);
public readonly record struct KeltnerOutput(double Upper, double Middle, double Lower);
public readonly record struct KstOutput(double Kst, double Signal);
public readonly record struct LeadLagCrossCorrelationOutput(double Lag, double Correlation);
public readonly record struct LinRegChannelOutput(double Upper, double Middle, double Lower);
public readonly record struct LineBreakBar(double Open, double Close, double Direction);
public readonly record struct LiquidationFeaturesOutput(double Long, double Short, double Net, double Total, double Imbalance);
public readonly record struct MaEnvelopeOutput(double Upper, double Middle, double Lower);
public readonly record struct MacdOutput(double Macd, double Signal, double Histogram);
public readonly record struct MamaOutput(double Mama, double Fama);
public readonly record struct MedianChannelOutput(double Upper, double Middle, double Lower);
public readonly record struct ModifiedMaStopOutput(double Value, double Direction);
public readonly record struct MurreyMathLinesOutput(double Mm88, double Mm78, double Mm68, double Mm58, double Mm48, double Mm38, double Mm28, double Mm18, double Mm08);
public readonly record struct NrtrOutput(double Value, double Direction);
public readonly record struct OpeningRangeOutput(double High, double Low, double BreakoutDistance);
public readonly record struct OvernightIntradayReturnOutput(double Overnight, double Intraday);
public readonly record struct PnfColumn(double Direction, double High, double Low);
public readonly record struct ProjectionBandsOutput(double Upper, double Middle, double Lower);
public readonly record struct QqeOutput(double RsiMa, double TrailingLine);
public readonly record struct QuartileBandsOutput(double Upper, double Middle, double Lower);
public readonly record struct RangeBar(double Open, double Close, double Direction);
public readonly record struct RelativeStrengthOutput(double Ratio, double RatioMa, double RatioRsi);
public readonly record struct RenkoBrick(double Open, double Close, double Direction);
public readonly record struct RunBar(double Open, double High, double Low, double Close, double Length, double Direction);
public readonly record struct RwiOutput(double High, double Low);
public readonly record struct SessionHighLowOutput(double High, double Low);
public readonly record struct SessionRangeOutput(double Asia, double Eu, double Us);
public readonly record struct SmoothedHeikinAshiOutput(double Open, double High, double Low, double Close);
public readonly record struct SpreadBollingerBandsOutput(double Middle, double Upper, double Lower, double PercentB);
public readonly record struct StandardErrorBandsOutput(double Upper, double Middle, double Lower);
public readonly record struct StarcBandsOutput(double Upper, double Middle, double Lower);
public readonly record struct StochasticOutput(double K, double D);
public readonly record struct SuperTrendOutput(double Value, double Direction);
public readonly record struct TdLinesOutput(double Resistance, double Support);
public readonly record struct TdMovingAverageOutput(double St1, double St2);
public readonly record struct TdRangeProjectionOutput(double High, double Low);
public readonly record struct TdRiskLevelOutput(double BuyRisk, double SellRisk);
public readonly record struct TdSequentialOutput(double Setup, double Countdown, double Direction);
public readonly record struct TickBar(double Open, double High, double Low, double Close, double Volume);
public readonly record struct TpoProfileOutputScalars(double PriceLow, double PriceHigh, double[] Values);
public readonly record struct TtmSqueezeOutput(double Squeeze, double Momentum);
public readonly record struct ValueAreaOutput(double Poc, double Vah, double Val);
public readonly record struct VolatilityConeOutput(double Current, double Min, double Median, double Max, double Percentile);
public readonly record struct VolumeBar(double Open, double High, double Low, double Close, double Volume);
public readonly record struct VolumeProfileOutputScalars(double PriceLow, double PriceHigh, double[] Values);
public readonly record struct VolumeWeightedMacdOutput(double Macd, double Signal, double Histogram);
public readonly record struct VolumeWeightedSrOutput(double Support, double Resistance);
public readonly record struct VortexOutput(double Plus, double Minus);
public readonly record struct VwapStdDevBandsOutput(double Upper, double Middle, double Lower, double Stddev);
public readonly record struct WaveTrendOutput(double Wt1, double Wt2);
public readonly record struct WilliamsFractalsOutput(double Up, double Down);
public readonly record struct WoodiePivotsOutput(double Pp, double R1, double R2, double S1, double S2);
public readonly record struct ZeroLagMacdOutput(double Macd, double Signal, double Histogram);
public readonly record struct ZigZagOutput(double Swing, double Direction);

public sealed class AbandonedBaby : IDisposable
{
    private readonly WickraHandle _handle;

    public AbandonedBaby()
    {
        var ptr = NativeMethods.wickra_abandoned_baby_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AbandonedBaby parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_abandoned_baby_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_abandoned_baby_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_abandoned_baby_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_abandoned_baby_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_abandoned_baby_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_abandoned_baby_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_abandoned_baby_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Abcd : IDisposable
{
    private readonly WickraHandle _handle;

    public Abcd()
    {
        var ptr = NativeMethods.wickra_abcd_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Abcd parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_abcd_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_abcd_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_abcd_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_abcd_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_abcd_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_abcd_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_abcd_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AbsoluteBreadthIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public AbsoluteBreadthIndex()
    {
        var ptr = NativeMethods.wickra_absolute_breadth_index_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AbsoluteBreadthIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_absolute_breadth_index_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_absolute_breadth_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_absolute_breadth_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_absolute_breadth_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_absolute_breadth_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_absolute_breadth_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AccelerationBands : IDisposable
{
    private readonly WickraHandle _handle;

    public AccelerationBands(int period, double factor)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_acceleration_bands_new((nuint)period, factor);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AccelerationBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_acceleration_bands_free);
    }

    public AccelerationBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAccelerationBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_acceleration_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AccelerationBandsOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_acceleration_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_acceleration_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_acceleration_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_acceleration_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AcceleratorOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public AcceleratorOscillator(int aoFast, int aoSlow, int signalPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(aoFast);
        ArgumentOutOfRangeException.ThrowIfNegative(aoSlow);
        ArgumentOutOfRangeException.ThrowIfNegative(signalPeriod);
        var ptr = NativeMethods.wickra_accelerator_oscillator_new((nuint)aoFast, (nuint)aoSlow, (nuint)signalPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AcceleratorOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_accelerator_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_accelerator_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_accelerator_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_accelerator_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_accelerator_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_accelerator_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_accelerator_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public AdOscillator()
    {
        var ptr = NativeMethods.wickra_ad_oscillator_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ad_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_ad_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ad_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ad_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ad_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ad_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ad_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdVolumeLine : IDisposable
{
    private readonly WickraHandle _handle;

    public AdVolumeLine()
    {
        var ptr = NativeMethods.wickra_ad_volume_line_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdVolumeLine parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ad_volume_line_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_ad_volume_line_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ad_volume_line_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ad_volume_line_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ad_volume_line_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ad_volume_line_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdaptiveCci : IDisposable
{
    private readonly WickraHandle _handle;

    public AdaptiveCci(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_adaptive_cci_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdaptiveCci parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_cci_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_adaptive_cci_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_adaptive_cci_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_adaptive_cci_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_adaptive_cci_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_adaptive_cci_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_adaptive_cci_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdaptiveCycle : IDisposable
{
    private readonly WickraHandle _handle;

    public AdaptiveCycle()
    {
        var ptr = NativeMethods.wickra_adaptive_cycle_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdaptiveCycle parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_cycle_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_adaptive_cycle_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_adaptive_cycle_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_adaptive_cycle_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_adaptive_cycle_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_adaptive_cycle_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_adaptive_cycle_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdaptiveLaguerreFilter : IDisposable
{
    private readonly WickraHandle _handle;

    public AdaptiveLaguerreFilter(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_adaptive_laguerre_filter_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdaptiveLaguerreFilter parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_laguerre_filter_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_adaptive_laguerre_filter_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_adaptive_laguerre_filter_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_adaptive_laguerre_filter_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_adaptive_laguerre_filter_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_adaptive_laguerre_filter_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_adaptive_laguerre_filter_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdaptiveRsi : IDisposable
{
    private readonly WickraHandle _handle;

    public AdaptiveRsi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_adaptive_rsi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdaptiveRsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_adaptive_rsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_adaptive_rsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_adaptive_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_adaptive_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_adaptive_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_adaptive_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_adaptive_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Adl : IDisposable
{
    private readonly WickraHandle _handle;

    public Adl()
    {
        var ptr = NativeMethods.wickra_adl_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Adl parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_adl_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_adl_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_adl_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_adl_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_adl_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_adl_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_adl_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdvanceBlock : IDisposable
{
    private readonly WickraHandle _handle;

    public AdvanceBlock()
    {
        var ptr = NativeMethods.wickra_advance_block_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdvanceBlock parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_advance_block_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_advance_block_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_advance_block_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_advance_block_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_advance_block_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_advance_block_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_advance_block_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdvanceDecline : IDisposable
{
    private readonly WickraHandle _handle;

    public AdvanceDecline()
    {
        var ptr = NativeMethods.wickra_advance_decline_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdvanceDecline parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_advance_decline_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_advance_decline_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_advance_decline_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_advance_decline_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_advance_decline_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_advance_decline_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AdvanceDeclineRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public AdvanceDeclineRatio()
    {
        var ptr = NativeMethods.wickra_advance_decline_ratio_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AdvanceDeclineRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_advance_decline_ratio_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_advance_decline_ratio_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_advance_decline_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_advance_decline_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_advance_decline_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_advance_decline_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Adx : IDisposable
{
    private readonly WickraHandle _handle;

    public Adx(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_adx_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Adx parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_adx_free);
    }

    public AdxOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAdxOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_adx_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AdxOutput(native.plus_di, native.minus_di, native.adx) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_adx_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_adx_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_adx_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_adx_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Adxr : IDisposable
{
    private readonly WickraHandle _handle;

    public Adxr(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_adxr_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Adxr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_adxr_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_adxr_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_adxr_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_adxr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_adxr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_adxr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_adxr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Alligator : IDisposable
{
    private readonly WickraHandle _handle;

    public Alligator(int jawPeriod, int teethPeriod, int lipsPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(jawPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(teethPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(lipsPeriod);
        var ptr = NativeMethods.wickra_alligator_new((nuint)jawPeriod, (nuint)teethPeriod, (nuint)lipsPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Alligator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_alligator_free);
    }

    public AlligatorOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAlligatorOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_alligator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AlligatorOutput(native.jaw, native.teeth, native.lips) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_alligator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_alligator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_alligator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_alligator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Alma : IDisposable
{
    private readonly WickraHandle _handle;

    public Alma(int period, double offset, double sigma)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_alma_new((nuint)period, offset, sigma);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Alma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_alma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_alma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_alma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_alma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_alma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_alma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_alma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Alpha : IDisposable
{
    private readonly WickraHandle _handle;

    public Alpha(int period, double riskFree)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_alpha_new((nuint)period, riskFree);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Alpha parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_alpha_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_alpha_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_alpha_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_alpha_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_alpha_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_alpha_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_alpha_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AmihudIlliquidity : IDisposable
{
    private readonly WickraHandle _handle;

    public AmihudIlliquidity(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_amihud_illiquidity_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AmihudIlliquidity parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_amihud_illiquidity_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_amihud_illiquidity_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_amihud_illiquidity_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_amihud_illiquidity_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_amihud_illiquidity_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_amihud_illiquidity_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AnchoredRsi : IDisposable
{
    private readonly WickraHandle _handle;

    public AnchoredRsi()
    {
        var ptr = NativeMethods.wickra_anchored_rsi_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AnchoredRsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_anchored_rsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_anchored_rsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_anchored_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_anchored_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_anchored_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_anchored_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_anchored_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AnchoredVwap : IDisposable
{
    private readonly WickraHandle _handle;

    public AnchoredVwap()
    {
        var ptr = NativeMethods.wickra_anchored_vwap_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AnchoredVwap parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_anchored_vwap_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_anchored_vwap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_anchored_vwap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_anchored_vwap_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_anchored_vwap_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_anchored_vwap_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_anchored_vwap_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AndrewsPitchfork : IDisposable
{
    private readonly WickraHandle _handle;

    public AndrewsPitchfork(int strength)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(strength);
        var ptr = NativeMethods.wickra_andrews_pitchfork_new((nuint)strength);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AndrewsPitchfork parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_andrews_pitchfork_free);
    }

    public AndrewsPitchforkOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAndrewsPitchforkOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_andrews_pitchfork_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AndrewsPitchforkOutput(native.median, native.upper, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_andrews_pitchfork_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_andrews_pitchfork_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_andrews_pitchfork_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_andrews_pitchfork_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Apo : IDisposable
{
    private readonly WickraHandle _handle;

    public Apo(int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_apo_new((nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Apo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_apo_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_apo_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_apo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_apo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_apo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_apo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_apo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Aroon : IDisposable
{
    private readonly WickraHandle _handle;

    public Aroon(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_aroon_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Aroon parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_aroon_free);
    }

    public AroonOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAroonOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_aroon_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AroonOutput(native.up, native.down) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_aroon_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_aroon_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_aroon_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_aroon_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AroonOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public AroonOscillator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_aroon_oscillator_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AroonOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_aroon_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_aroon_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_aroon_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_aroon_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_aroon_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_aroon_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_aroon_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Atr : IDisposable
{
    private readonly WickraHandle _handle;

    public Atr(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_atr_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Atr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_atr_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_atr_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_atr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_atr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_atr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_atr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AtrBands : IDisposable
{
    private readonly WickraHandle _handle;

    public AtrBands(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_atr_bands_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AtrBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_bands_free);
    }

    public AtrBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAtrBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_atr_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AtrBandsOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_atr_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_atr_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_atr_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_atr_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AtrRatchet : IDisposable
{
    private readonly WickraHandle _handle;

    public AtrRatchet(int atrPeriod, double startMult, double increment)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        var ptr = NativeMethods.wickra_atr_ratchet_new((nuint)atrPeriod, startMult, increment);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AtrRatchet parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_ratchet_free);
    }

    public AtrRatchetOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAtrRatchetOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_atr_ratchet_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AtrRatchetOutput(native.@value, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_atr_ratchet_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_atr_ratchet_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_atr_ratchet_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_atr_ratchet_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AtrTrailingStop : IDisposable
{
    private readonly WickraHandle _handle;

    public AtrTrailingStop(int atrPeriod, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        var ptr = NativeMethods.wickra_atr_trailing_stop_new((nuint)atrPeriod, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AtrTrailingStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_atr_trailing_stop_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_atr_trailing_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_atr_trailing_stop_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_atr_trailing_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_atr_trailing_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_atr_trailing_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_atr_trailing_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AutoFib : IDisposable
{
    private readonly WickraHandle _handle;

    public AutoFib()
    {
        var ptr = NativeMethods.wickra_auto_fib_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AutoFib parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_auto_fib_free);
    }

    public AutoFibOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraAutoFibOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_auto_fib_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new AutoFibOutput(native.level_0, native.level_236, native.level_382, native.level_500, native.level_618, native.level_786, native.level_1000) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_auto_fib_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_auto_fib_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_auto_fib_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_auto_fib_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Autocorrelation : IDisposable
{
    private readonly WickraHandle _handle;

    public Autocorrelation(int period, int lag)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(lag);
        var ptr = NativeMethods.wickra_autocorrelation_new((nuint)period, (nuint)lag);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Autocorrelation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_autocorrelation_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_autocorrelation_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_autocorrelation_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_autocorrelation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_autocorrelation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_autocorrelation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_autocorrelation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AutocorrelationPeriodogram : IDisposable
{
    private readonly WickraHandle _handle;

    public AutocorrelationPeriodogram(int minPeriod, int maxPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(minPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(maxPeriod);
        var ptr = NativeMethods.wickra_autocorrelation_periodogram_new((nuint)minPeriod, (nuint)maxPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AutocorrelationPeriodogram parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_autocorrelation_periodogram_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_autocorrelation_periodogram_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_autocorrelation_periodogram_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_autocorrelation_periodogram_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_autocorrelation_periodogram_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_autocorrelation_periodogram_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_autocorrelation_periodogram_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AverageDailyRange : IDisposable
{
    private readonly WickraHandle _handle;

    public AverageDailyRange(int period, int utcOffsetMinutes)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_average_daily_range_new((nuint)period, utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AverageDailyRange parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_average_daily_range_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_average_daily_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_average_daily_range_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_average_daily_range_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_average_daily_range_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_average_daily_range_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_average_daily_range_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AverageDrawdown : IDisposable
{
    private readonly WickraHandle _handle;

    public AverageDrawdown(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_average_drawdown_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AverageDrawdown parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_average_drawdown_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_average_drawdown_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_average_drawdown_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_average_drawdown_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_average_drawdown_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_average_drawdown_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_average_drawdown_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AvgPrice : IDisposable
{
    private readonly WickraHandle _handle;

    public AvgPrice()
    {
        var ptr = NativeMethods.wickra_avg_price_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AvgPrice parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_avg_price_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_avg_price_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_avg_price_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_avg_price_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_avg_price_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_avg_price_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_avg_price_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AwesomeOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public AwesomeOscillator(int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_awesome_oscillator_new((nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AwesomeOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_awesome_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_awesome_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_awesome_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_awesome_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_awesome_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_awesome_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_awesome_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class AwesomeOscillatorHistogram : IDisposable
{
    private readonly WickraHandle _handle;

    public AwesomeOscillatorHistogram(int fast, int slow, int lookback)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(lookback);
        var ptr = NativeMethods.wickra_awesome_oscillator_histogram_new((nuint)fast, (nuint)slow, (nuint)lookback);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid AwesomeOscillatorHistogram parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_awesome_oscillator_histogram_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_awesome_oscillator_histogram_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_awesome_oscillator_histogram_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_awesome_oscillator_histogram_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_awesome_oscillator_histogram_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_awesome_oscillator_histogram_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_awesome_oscillator_histogram_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BalanceOfPower : IDisposable
{
    private readonly WickraHandle _handle;

    public BalanceOfPower()
    {
        var ptr = NativeMethods.wickra_balance_of_power_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BalanceOfPower parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_balance_of_power_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_balance_of_power_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_balance_of_power_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_balance_of_power_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_balance_of_power_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_balance_of_power_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_balance_of_power_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BandpassFilter : IDisposable
{
    private readonly WickraHandle _handle;

    public BandpassFilter(int period, double bandwidth)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_bandpass_filter_new((nuint)period, bandwidth);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BandpassFilter parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_bandpass_filter_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_bandpass_filter_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_bandpass_filter_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_bandpass_filter_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_bandpass_filter_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_bandpass_filter_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_bandpass_filter_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Bat : IDisposable
{
    private readonly WickraHandle _handle;

    public Bat()
    {
        var ptr = NativeMethods.wickra_bat_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Bat parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_bat_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_bat_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_bat_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_bat_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_bat_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_bat_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_bat_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BeltHold : IDisposable
{
    private readonly WickraHandle _handle;

    public BeltHold()
    {
        var ptr = NativeMethods.wickra_belt_hold_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BeltHold parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_belt_hold_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_belt_hold_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_belt_hold_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_belt_hold_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_belt_hold_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_belt_hold_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_belt_hold_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Beta : IDisposable
{
    private readonly WickraHandle _handle;

    public Beta(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_beta_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Beta parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_beta_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_beta_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_beta_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_beta_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_beta_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_beta_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_beta_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BetaNeutralSpread : IDisposable
{
    private readonly WickraHandle _handle;

    public BetaNeutralSpread(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_beta_neutral_spread_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BetaNeutralSpread parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_beta_neutral_spread_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_beta_neutral_spread_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_beta_neutral_spread_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_beta_neutral_spread_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_beta_neutral_spread_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_beta_neutral_spread_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_beta_neutral_spread_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BetterVolume : IDisposable
{
    private readonly WickraHandle _handle;

    public BetterVolume(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_better_volume_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BetterVolume parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_better_volume_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_better_volume_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_better_volume_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_better_volume_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_better_volume_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_better_volume_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_better_volume_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BipowerVariation : IDisposable
{
    private readonly WickraHandle _handle;

    public BipowerVariation(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_bipower_variation_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BipowerVariation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_bipower_variation_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_bipower_variation_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_bipower_variation_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_bipower_variation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_bipower_variation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_bipower_variation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_bipower_variation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BodySizePct : IDisposable
{
    private readonly WickraHandle _handle;

    public BodySizePct()
    {
        var ptr = NativeMethods.wickra_body_size_pct_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BodySizePct parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_body_size_pct_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_body_size_pct_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_body_size_pct_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_body_size_pct_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_body_size_pct_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_body_size_pct_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_body_size_pct_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BollingerBands : IDisposable
{
    private readonly WickraHandle _handle;

    public BollingerBands(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_bollinger_bands_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BollingerBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_bollinger_bands_free);
    }

    public BollingerOutput? Update(double @value)
    {
        WickraBollingerOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_bollinger_bands_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new BollingerOutput(native.upper, native.middle, native.lower, native.stddev) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_bollinger_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_bollinger_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_bollinger_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_bollinger_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BollingerBandwidth : IDisposable
{
    private readonly WickraHandle _handle;

    public BollingerBandwidth(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_bollinger_bandwidth_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BollingerBandwidth parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_bollinger_bandwidth_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_bollinger_bandwidth_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_bollinger_bandwidth_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_bollinger_bandwidth_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_bollinger_bandwidth_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_bollinger_bandwidth_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_bollinger_bandwidth_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BomarBands : IDisposable
{
    private readonly WickraHandle _handle;

    public BomarBands(int period, double coverage)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_bomar_bands_new((nuint)period, coverage);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BomarBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_bomar_bands_free);
    }

    public BomarBandsOutput? Update(double @value)
    {
        WickraBomarBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_bomar_bands_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new BomarBandsOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_bomar_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_bomar_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_bomar_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_bomar_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BreadthThrust : IDisposable
{
    private readonly WickraHandle _handle;

    public BreadthThrust(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_breadth_thrust_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BreadthThrust parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_breadth_thrust_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_breadth_thrust_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_breadth_thrust_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_breadth_thrust_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_breadth_thrust_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_breadth_thrust_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Breakaway : IDisposable
{
    private readonly WickraHandle _handle;

    public Breakaway()
    {
        var ptr = NativeMethods.wickra_breakaway_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Breakaway parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_breakaway_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_breakaway_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_breakaway_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_breakaway_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_breakaway_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_breakaway_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_breakaway_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BullishPercentIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public BullishPercentIndex()
    {
        var ptr = NativeMethods.wickra_bullish_percent_index_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BullishPercentIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_bullish_percent_index_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_bullish_percent_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_bullish_percent_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_bullish_percent_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_bullish_percent_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_bullish_percent_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class BurkeRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public BurkeRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_burke_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BurkeRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_burke_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_burke_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_burke_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_burke_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_burke_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_burke_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_burke_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Butterfly : IDisposable
{
    private readonly WickraHandle _handle;

    public Butterfly()
    {
        var ptr = NativeMethods.wickra_butterfly_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Butterfly parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_butterfly_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_butterfly_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_butterfly_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_butterfly_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_butterfly_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_butterfly_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_butterfly_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CalendarSpread : IDisposable
{
    private readonly WickraHandle _handle;

    public CalendarSpread()
    {
        var ptr = NativeMethods.wickra_calendar_spread_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CalendarSpread parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_calendar_spread_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_calendar_spread_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_calendar_spread_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_calendar_spread_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_calendar_spread_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_calendar_spread_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CalmarRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public CalmarRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_calmar_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CalmarRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_calmar_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_calmar_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_calmar_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_calmar_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_calmar_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_calmar_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_calmar_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Camarilla : IDisposable
{
    private readonly WickraHandle _handle;

    public Camarilla()
    {
        var ptr = NativeMethods.wickra_camarilla_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Camarilla parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_camarilla_free);
    }

    public CamarillaPivotsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraCamarillaPivotsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_camarilla_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new CamarillaPivotsOutput(native.pp, native.r1, native.r2, native.r3, native.r4, native.s1, native.s2, native.s3, native.s4) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_camarilla_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_camarilla_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_camarilla_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_camarilla_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CandleReader : IDisposable
{
    private readonly WickraHandle _handle;

    /// <summary>Parse a timestamp,open,high,low,close,volume CSV string (a
    /// leading UTF-8 BOM and field whitespace are tolerated).</summary>
    public CandleReader(string csv)
    {
        ArgumentNullException.ThrowIfNull(csv);
        var bytes = System.Text.Encoding.UTF8.GetBytes(csv);
        nint ptr;
        unsafe
        {
            fixed (byte* data = bytes)
            {
                ptr = NativeMethods.wickra_candle_reader_new(data, (nuint)bytes.Length);
            }
        }
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CandleReader CSV");
        }
        _handle = new WickraHandle(ptr, NativeMethods.wickra_candle_reader_free);
    }

    /// <summary>Every candle parsed from the CSV, in file order.</summary>
    public Candle[] Read()
    {
        var count = (long)NativeMethods.wickra_candle_reader_count(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<Candle>();
        }
        var buffer = new WickraCandle[count];
        unsafe
        {
            fixed (WickraCandle* ptr = buffer)
            {
                NativeMethods.wickra_candle_reader_read(_handle.DangerousGetHandle(), ptr, (nuint)count);
            }
        }
        GC.KeepAlive(_handle);
        var result = new Candle[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new Candle(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].volume, buffer[i].timestamp);
        }
        return result;
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CandleVolume : IDisposable
{
    private readonly WickraHandle _handle;

    public CandleVolume(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_candle_volume_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CandleVolume parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_candle_volume_free);
    }

    public CandleVolumeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraCandleVolumeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_candle_volume_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new CandleVolumeOutput(native.body, native.width) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_candle_volume_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_candle_volume_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_candle_volume_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_candle_volume_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Cci : IDisposable
{
    private readonly WickraHandle _handle;

    public Cci(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_cci_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Cci parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cci_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_cci_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_cci_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cci_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cci_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cci_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cci_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CenterOfGravity : IDisposable
{
    private readonly WickraHandle _handle;

    public CenterOfGravity(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_center_of_gravity_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CenterOfGravity parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_center_of_gravity_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_center_of_gravity_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_center_of_gravity_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_center_of_gravity_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_center_of_gravity_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_center_of_gravity_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_center_of_gravity_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CentralPivotRange : IDisposable
{
    private readonly WickraHandle _handle;

    public CentralPivotRange()
    {
        var ptr = NativeMethods.wickra_central_pivot_range_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CentralPivotRange parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_central_pivot_range_free);
    }

    public CentralPivotRangeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraCentralPivotRangeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_central_pivot_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new CentralPivotRangeOutput(native.pivot, native.tc, native.bc) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_central_pivot_range_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_central_pivot_range_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_central_pivot_range_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_central_pivot_range_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Cfo : IDisposable
{
    private readonly WickraHandle _handle;

    public Cfo(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_cfo_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Cfo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cfo_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_cfo_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_cfo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cfo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cfo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cfo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cfo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ChaikinMoneyFlow : IDisposable
{
    private readonly WickraHandle _handle;

    public ChaikinMoneyFlow(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_chaikin_money_flow_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ChaikinMoneyFlow parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_chaikin_money_flow_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_chaikin_money_flow_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_chaikin_money_flow_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_chaikin_money_flow_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_chaikin_money_flow_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_chaikin_money_flow_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_chaikin_money_flow_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ChaikinOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public ChaikinOscillator(int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_chaikin_oscillator_new((nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ChaikinOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_chaikin_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_chaikin_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_chaikin_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_chaikin_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_chaikin_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_chaikin_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_chaikin_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ChaikinVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public ChaikinVolatility(int emaPeriod, int rocPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(emaPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(rocPeriod);
        var ptr = NativeMethods.wickra_chaikin_volatility_new((nuint)emaPeriod, (nuint)rocPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ChaikinVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_chaikin_volatility_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_chaikin_volatility_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_chaikin_volatility_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_chaikin_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_chaikin_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_chaikin_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_chaikin_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ChandeKrollStop : IDisposable
{
    private readonly WickraHandle _handle;

    public ChandeKrollStop(int atrPeriod, double atrMultiplier, int stopPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(stopPeriod);
        var ptr = NativeMethods.wickra_chande_kroll_stop_new((nuint)atrPeriod, atrMultiplier, (nuint)stopPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ChandeKrollStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_chande_kroll_stop_free);
    }

    public ChandeKrollStopOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraChandeKrollStopOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_chande_kroll_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ChandeKrollStopOutput(native.stop_long, native.stop_short) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_chande_kroll_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_chande_kroll_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_chande_kroll_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_chande_kroll_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ChandelierExit : IDisposable
{
    private readonly WickraHandle _handle;

    public ChandelierExit(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_chandelier_exit_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ChandelierExit parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_chandelier_exit_free);
    }

    public ChandelierExitOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraChandelierExitOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_chandelier_exit_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ChandelierExitOutput(native.long_stop, native.short_stop) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_chandelier_exit_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_chandelier_exit_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_chandelier_exit_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_chandelier_exit_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ChoppinessIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public ChoppinessIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_choppiness_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ChoppinessIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_choppiness_index_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_choppiness_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_choppiness_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_choppiness_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_choppiness_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_choppiness_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_choppiness_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ClassicPivots : IDisposable
{
    private readonly WickraHandle _handle;

    public ClassicPivots()
    {
        var ptr = NativeMethods.wickra_classic_pivots_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ClassicPivots parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_classic_pivots_free);
    }

    public ClassicPivotsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraClassicPivotsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_classic_pivots_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ClassicPivotsOutput(native.pp, native.r1, native.r2, native.r3, native.s1, native.s2, native.s3) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_classic_pivots_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_classic_pivots_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_classic_pivots_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_classic_pivots_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CloseVsOpen : IDisposable
{
    private readonly WickraHandle _handle;

    public CloseVsOpen()
    {
        var ptr = NativeMethods.wickra_close_vs_open_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CloseVsOpen parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_close_vs_open_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_close_vs_open_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_close_vs_open_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_close_vs_open_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_close_vs_open_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_close_vs_open_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_close_vs_open_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ClosingMarubozu : IDisposable
{
    private readonly WickraHandle _handle;

    public ClosingMarubozu()
    {
        var ptr = NativeMethods.wickra_closing_marubozu_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ClosingMarubozu parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_closing_marubozu_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_closing_marubozu_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_closing_marubozu_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_closing_marubozu_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_closing_marubozu_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_closing_marubozu_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_closing_marubozu_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Cmo : IDisposable
{
    private readonly WickraHandle _handle;

    public Cmo(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_cmo_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Cmo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cmo_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_cmo_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_cmo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cmo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cmo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cmo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cmo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CoefficientOfVariation : IDisposable
{
    private readonly WickraHandle _handle;

    public CoefficientOfVariation(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_coefficient_of_variation_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CoefficientOfVariation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_coefficient_of_variation_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_coefficient_of_variation_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_coefficient_of_variation_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_coefficient_of_variation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_coefficient_of_variation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_coefficient_of_variation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_coefficient_of_variation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Cointegration : IDisposable
{
    private readonly WickraHandle _handle;

    public Cointegration(int period, int adfLags)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(adfLags);
        var ptr = NativeMethods.wickra_cointegration_new((nuint)period, (nuint)adfLags);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Cointegration parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cointegration_free);
    }

    public CointegrationOutput? Update(double x, double y)
    {
        WickraCointegrationOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_cointegration_update(_handle.DangerousGetHandle(), x, y, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new CointegrationOutput(native.hedge_ratio, native.spread, native.adf_stat) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cointegration_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cointegration_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cointegration_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cointegration_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CommonSenseRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public CommonSenseRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_common_sense_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CommonSenseRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_common_sense_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_common_sense_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_common_sense_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_common_sense_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_common_sense_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_common_sense_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_common_sense_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CompositeProfile : IDisposable
{
    private readonly WickraHandle _handle;

    public CompositeProfile(int period, int bins, double valueAreaPct)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(bins);
        var ptr = NativeMethods.wickra_composite_profile_new((nuint)period, (nuint)bins, valueAreaPct);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CompositeProfile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_composite_profile_free);
    }

    public CompositeProfileOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraCompositeProfileOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_composite_profile_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new CompositeProfileOutput(native.poc, native.vah, native.val) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_composite_profile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_composite_profile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_composite_profile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_composite_profile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ConcealingBabySwallow : IDisposable
{
    private readonly WickraHandle _handle;

    public ConcealingBabySwallow()
    {
        var ptr = NativeMethods.wickra_concealing_baby_swallow_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ConcealingBabySwallow parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_concealing_baby_swallow_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_concealing_baby_swallow_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_concealing_baby_swallow_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_concealing_baby_swallow_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_concealing_baby_swallow_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_concealing_baby_swallow_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_concealing_baby_swallow_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ConditionalValueAtRisk : IDisposable
{
    private readonly WickraHandle _handle;

    public ConditionalValueAtRisk(int period, double confidence)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_conditional_value_at_risk_new((nuint)period, confidence);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ConditionalValueAtRisk parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_conditional_value_at_risk_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_conditional_value_at_risk_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_conditional_value_at_risk_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_conditional_value_at_risk_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_conditional_value_at_risk_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_conditional_value_at_risk_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_conditional_value_at_risk_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ConnorsRsi : IDisposable
{
    private readonly WickraHandle _handle;

    public ConnorsRsi(int periodRsi, int periodStreak, int periodRank)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(periodRsi);
        ArgumentOutOfRangeException.ThrowIfNegative(periodStreak);
        ArgumentOutOfRangeException.ThrowIfNegative(periodRank);
        var ptr = NativeMethods.wickra_connors_rsi_new((nuint)periodRsi, (nuint)periodStreak, (nuint)periodRank);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ConnorsRsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_connors_rsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_connors_rsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_connors_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_connors_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_connors_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_connors_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_connors_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Coppock : IDisposable
{
    private readonly WickraHandle _handle;

    public Coppock(int rocLongPeriod, int rocShortPeriod, int wmaPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rocLongPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(rocShortPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(wmaPeriod);
        var ptr = NativeMethods.wickra_coppock_new((nuint)rocLongPeriod, (nuint)rocShortPeriod, (nuint)wmaPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Coppock parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_coppock_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_coppock_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_coppock_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_coppock_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_coppock_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_coppock_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_coppock_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CorrelationTrendIndicator : IDisposable
{
    private readonly WickraHandle _handle;

    public CorrelationTrendIndicator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_correlation_trend_indicator_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CorrelationTrendIndicator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_correlation_trend_indicator_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_correlation_trend_indicator_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_correlation_trend_indicator_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_correlation_trend_indicator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_correlation_trend_indicator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_correlation_trend_indicator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_correlation_trend_indicator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Counterattack : IDisposable
{
    private readonly WickraHandle _handle;

    public Counterattack()
    {
        var ptr = NativeMethods.wickra_counterattack_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Counterattack parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_counterattack_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_counterattack_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_counterattack_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_counterattack_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_counterattack_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_counterattack_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_counterattack_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Crab : IDisposable
{
    private readonly WickraHandle _handle;

    public Crab()
    {
        var ptr = NativeMethods.wickra_crab_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Crab parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_crab_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_crab_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_crab_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_crab_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_crab_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_crab_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_crab_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CumulativeVolumeDelta : IDisposable
{
    private readonly WickraHandle _handle;

    public CumulativeVolumeDelta()
    {
        var ptr = NativeMethods.wickra_cumulative_volume_delta_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CumulativeVolumeDelta parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cumulative_volume_delta_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_cumulative_volume_delta_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cumulative_volume_delta_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cumulative_volume_delta_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cumulative_volume_delta_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cumulative_volume_delta_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CumulativeVolumeIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public CumulativeVolumeIndex()
    {
        var ptr = NativeMethods.wickra_cumulative_volume_index_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CumulativeVolumeIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cumulative_volume_index_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_cumulative_volume_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cumulative_volume_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cumulative_volume_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cumulative_volume_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cumulative_volume_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CupAndHandle : IDisposable
{
    private readonly WickraHandle _handle;

    public CupAndHandle()
    {
        var ptr = NativeMethods.wickra_cup_and_handle_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CupAndHandle parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cup_and_handle_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_cup_and_handle_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_cup_and_handle_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cup_and_handle_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cup_and_handle_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cup_and_handle_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cup_and_handle_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class CyberneticCycle : IDisposable
{
    private readonly WickraHandle _handle;

    public CyberneticCycle(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_cybernetic_cycle_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid CyberneticCycle parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cybernetic_cycle_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_cybernetic_cycle_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_cybernetic_cycle_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cybernetic_cycle_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cybernetic_cycle_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cybernetic_cycle_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cybernetic_cycle_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Cypher : IDisposable
{
    private readonly WickraHandle _handle;

    public Cypher()
    {
        var ptr = NativeMethods.wickra_cypher_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Cypher parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_cypher_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_cypher_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_cypher_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_cypher_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_cypher_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_cypher_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_cypher_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DayOfWeekProfile : IDisposable
{
    private readonly WickraHandle _handle;
    private readonly int _valuesCapacity;

    public DayOfWeekProfile(int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_day_of_week_profile_new(utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DayOfWeekProfile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_day_of_week_profile_free);
        _valuesCapacity = 4096;
    }

    public double[]? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var values = new double[_valuesCapacity];
        long length;
        unsafe
        {
            fixed (double* valuesPtr = values)
            {
                length = (long)NativeMethods.wickra_day_of_week_profile_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, valuesPtr, (nuint)_valuesCapacity);
            }
        }

        GC.KeepAlive(_handle);
        if (length < 0)
        {
            return null;
        }

        if (length < values.Length)
        {
            Array.Resize(ref values, (int)length);
        }

        return values;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_day_of_week_profile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_day_of_week_profile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_day_of_week_profile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_day_of_week_profile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Decycler : IDisposable
{
    private readonly WickraHandle _handle;

    public Decycler(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_decycler_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Decycler parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_decycler_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_decycler_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_decycler_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_decycler_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_decycler_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_decycler_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_decycler_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DecyclerOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public DecyclerOscillator(int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_decycler_oscillator_new((nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DecyclerOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_decycler_oscillator_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_decycler_oscillator_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_decycler_oscillator_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_decycler_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_decycler_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_decycler_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_decycler_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Dema : IDisposable
{
    private readonly WickraHandle _handle;

    public Dema(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_dema_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Dema parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_dema_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_dema_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_dema_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_dema_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_dema_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_dema_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_dema_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DemandIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public DemandIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_demand_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DemandIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_demand_index_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_demand_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_demand_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_demand_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_demand_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_demand_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_demand_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DemarkPivots : IDisposable
{
    private readonly WickraHandle _handle;

    public DemarkPivots()
    {
        var ptr = NativeMethods.wickra_demark_pivots_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DemarkPivots parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_demark_pivots_free);
    }

    public DemarkPivotsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraDemarkPivotsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_demark_pivots_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new DemarkPivotsOutput(native.pp, native.r1, native.s1) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_demark_pivots_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_demark_pivots_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_demark_pivots_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_demark_pivots_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DepthSlope : IDisposable
{
    private readonly WickraHandle _handle;

    public DepthSlope()
    {
        var ptr = NativeMethods.wickra_depth_slope_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DepthSlope parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_depth_slope_free);
    }

    public double Update(ReadOnlySpan<double> bidPrice, ReadOnlySpan<double> bidSize, ReadOnlySpan<double> askPrice, ReadOnlySpan<double> askSize)
    {
        if (bidSize.Length != bidPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (askSize.Length != askPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* bidPricePtr = bidPrice)
            fixed (double* bidSizePtr = bidSize)
            fixed (double* askPricePtr = askPrice)
            fixed (double* askSizePtr = askSize)
            {
                result = NativeMethods.wickra_depth_slope_update(_handle.DangerousGetHandle(), bidPricePtr, bidSizePtr, (nuint)bidPrice.Length, askPricePtr, askSizePtr, (nuint)askPrice.Length);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_depth_slope_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_depth_slope_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_depth_slope_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_depth_slope_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DerivativeOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public DerivativeOscillator(int rsiPeriod, int smooth1, int smooth2, int signalPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rsiPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(smooth1);
        ArgumentOutOfRangeException.ThrowIfNegative(smooth2);
        ArgumentOutOfRangeException.ThrowIfNegative(signalPeriod);
        var ptr = NativeMethods.wickra_derivative_oscillator_new((nuint)rsiPeriod, (nuint)smooth1, (nuint)smooth2, (nuint)signalPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DerivativeOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_derivative_oscillator_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_derivative_oscillator_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_derivative_oscillator_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_derivative_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_derivative_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_derivative_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_derivative_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DetrendedStdDev : IDisposable
{
    private readonly WickraHandle _handle;

    public DetrendedStdDev(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_detrended_std_dev_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DetrendedStdDev parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_detrended_std_dev_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_detrended_std_dev_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_detrended_std_dev_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_detrended_std_dev_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_detrended_std_dev_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_detrended_std_dev_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_detrended_std_dev_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DisparityIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public DisparityIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_disparity_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DisparityIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_disparity_index_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_disparity_index_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_disparity_index_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_disparity_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_disparity_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_disparity_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_disparity_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DistanceSsd : IDisposable
{
    private readonly WickraHandle _handle;

    public DistanceSsd(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_distance_ssd_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DistanceSsd parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_distance_ssd_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_distance_ssd_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_distance_ssd_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_distance_ssd_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_distance_ssd_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_distance_ssd_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_distance_ssd_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Doji : IDisposable
{
    private readonly WickraHandle _handle;

    public Doji()
    {
        var ptr = NativeMethods.wickra_doji_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Doji parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_doji_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_doji_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_doji_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_doji_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_doji_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_doji_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_doji_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DojiStar : IDisposable
{
    private readonly WickraHandle _handle;

    public DojiStar()
    {
        var ptr = NativeMethods.wickra_doji_star_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DojiStar parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_doji_star_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_doji_star_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_doji_star_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_doji_star_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_doji_star_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_doji_star_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_doji_star_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DollarBars : IDisposable
{
    private readonly WickraHandle _handle;

    public DollarBars(double dollarPerBar)
    {
        var ptr = NativeMethods.wickra_dollar_bars_new(dollarPerBar);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DollarBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_dollar_bars_free);
    }

    public DollarBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraDollarBar> buffer = stackalloc WickraDollarBar[cap];
        long count;
        unsafe
        {
            fixed (WickraDollarBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_dollar_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<DollarBar>();
        }

        var result = new DollarBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new DollarBar(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].volume, buffer[i].dollar);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_dollar_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_dollar_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Donchian : IDisposable
{
    private readonly WickraHandle _handle;

    public Donchian(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_donchian_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Donchian parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_donchian_free);
    }

    public DonchianOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraDonchianOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_donchian_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new DonchianOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_donchian_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_donchian_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_donchian_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_donchian_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DonchianStop : IDisposable
{
    private readonly WickraHandle _handle;

    public DonchianStop(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_donchian_stop_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DonchianStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_donchian_stop_free);
    }

    public DonchianStopOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraDonchianStopOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_donchian_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new DonchianStopOutput(native.stop_long, native.stop_short) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_donchian_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_donchian_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_donchian_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_donchian_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DoubleBollinger : IDisposable
{
    private readonly WickraHandle _handle;

    public DoubleBollinger(int period, double kInner, double kOuter)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_double_bollinger_new((nuint)period, kInner, kOuter);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DoubleBollinger parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_double_bollinger_free);
    }

    public DoubleBollingerOutput? Update(double @value)
    {
        WickraDoubleBollingerOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_double_bollinger_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new DoubleBollingerOutput(native.upper_outer, native.upper_inner, native.middle, native.lower_inner, native.lower_outer) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_double_bollinger_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_double_bollinger_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_double_bollinger_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_double_bollinger_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DoubleTopBottom : IDisposable
{
    private readonly WickraHandle _handle;

    public DoubleTopBottom()
    {
        var ptr = NativeMethods.wickra_double_top_bottom_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DoubleTopBottom parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_double_top_bottom_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_double_top_bottom_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_double_top_bottom_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_double_top_bottom_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_double_top_bottom_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_double_top_bottom_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_double_top_bottom_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DownsideGapThreeMethods : IDisposable
{
    private readonly WickraHandle _handle;

    public DownsideGapThreeMethods()
    {
        var ptr = NativeMethods.wickra_downside_gap_three_methods_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DownsideGapThreeMethods parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_downside_gap_three_methods_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_downside_gap_three_methods_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_downside_gap_three_methods_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_downside_gap_three_methods_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_downside_gap_three_methods_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_downside_gap_three_methods_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_downside_gap_three_methods_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Dpo : IDisposable
{
    private readonly WickraHandle _handle;

    public Dpo(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_dpo_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Dpo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_dpo_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_dpo_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_dpo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_dpo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_dpo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_dpo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_dpo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DragonflyDoji : IDisposable
{
    private readonly WickraHandle _handle;

    public DragonflyDoji()
    {
        var ptr = NativeMethods.wickra_dragonfly_doji_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DragonflyDoji parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_dragonfly_doji_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_dragonfly_doji_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_dragonfly_doji_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_dragonfly_doji_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_dragonfly_doji_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_dragonfly_doji_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_dragonfly_doji_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DrawdownDuration : IDisposable
{
    private readonly WickraHandle _handle;

    public DrawdownDuration()
    {
        var ptr = NativeMethods.wickra_drawdown_duration_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DrawdownDuration parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_drawdown_duration_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_drawdown_duration_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_drawdown_duration_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_drawdown_duration_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_drawdown_duration_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_drawdown_duration_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_drawdown_duration_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DumplingTop : IDisposable
{
    private readonly WickraHandle _handle;

    public DumplingTop(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_dumpling_top_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DumplingTop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_dumpling_top_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_dumpling_top_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_dumpling_top_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_dumpling_top_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_dumpling_top_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_dumpling_top_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_dumpling_top_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Dx : IDisposable
{
    private readonly WickraHandle _handle;

    public Dx(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_dx_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Dx parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_dx_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_dx_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_dx_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_dx_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_dx_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_dx_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_dx_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class DynamicMomentumIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public DynamicMomentumIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_dynamic_momentum_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid DynamicMomentumIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_dynamic_momentum_index_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_dynamic_momentum_index_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_dynamic_momentum_index_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_dynamic_momentum_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_dynamic_momentum_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_dynamic_momentum_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_dynamic_momentum_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EaseOfMovement : IDisposable
{
    private readonly WickraHandle _handle;

    public EaseOfMovement(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ease_of_movement_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EaseOfMovement parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ease_of_movement_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_ease_of_movement_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ease_of_movement_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ease_of_movement_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ease_of_movement_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ease_of_movement_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ease_of_movement_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EffectiveSpread : IDisposable
{
    private readonly WickraHandle _handle;

    public EffectiveSpread()
    {
        var ptr = NativeMethods.wickra_effective_spread_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EffectiveSpread parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_effective_spread_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp, double mid)
    {
        var result = NativeMethods.wickra_effective_spread_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp, mid);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_effective_spread_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_effective_spread_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_effective_spread_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_effective_spread_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EhlersStochastic : IDisposable
{
    private readonly WickraHandle _handle;

    public EhlersStochastic(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ehlers_stochastic_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EhlersStochastic parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ehlers_stochastic_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ehlers_stochastic_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ehlers_stochastic_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ehlers_stochastic_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ehlers_stochastic_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ehlers_stochastic_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ehlers_stochastic_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Ehma : IDisposable
{
    private readonly WickraHandle _handle;

    public Ehma(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ehma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Ehma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ehma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ehma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ehma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ehma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ehma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ehma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ehma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ElderImpulse : IDisposable
{
    private readonly WickraHandle _handle;

    public ElderImpulse(int emaPeriod, int macdFast, int macdSlow, int macdSignal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(emaPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(macdFast);
        ArgumentOutOfRangeException.ThrowIfNegative(macdSlow);
        ArgumentOutOfRangeException.ThrowIfNegative(macdSignal);
        var ptr = NativeMethods.wickra_elder_impulse_new((nuint)emaPeriod, (nuint)macdFast, (nuint)macdSlow, (nuint)macdSignal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ElderImpulse parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_elder_impulse_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_elder_impulse_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_elder_impulse_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_elder_impulse_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_elder_impulse_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_elder_impulse_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_elder_impulse_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ElderRay : IDisposable
{
    private readonly WickraHandle _handle;

    public ElderRay(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_elder_ray_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ElderRay parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_elder_ray_free);
    }

    public ElderRayOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraElderRayOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_elder_ray_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ElderRayOutput(native.bull_power, native.bear_power) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_elder_ray_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_elder_ray_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_elder_ray_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_elder_ray_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ElderSafeZone : IDisposable
{
    private readonly WickraHandle _handle;

    public ElderSafeZone(int period, double coeff)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_elder_safe_zone_new((nuint)period, coeff);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ElderSafeZone parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_elder_safe_zone_free);
    }

    public ElderSafeZoneOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraElderSafeZoneOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_elder_safe_zone_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ElderSafeZoneOutput(native.@value, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_elder_safe_zone_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_elder_safe_zone_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_elder_safe_zone_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_elder_safe_zone_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Ema : IDisposable
{
    private readonly WickraHandle _handle;

    public Ema(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ema_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Ema parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ema_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ema_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ema_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ema_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ema_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ema_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ema_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EmpiricalModeDecomposition : IDisposable
{
    private readonly WickraHandle _handle;

    public EmpiricalModeDecomposition(int period, double fraction)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_empirical_mode_decomposition_new((nuint)period, fraction);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EmpiricalModeDecomposition parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_empirical_mode_decomposition_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_empirical_mode_decomposition_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_empirical_mode_decomposition_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_empirical_mode_decomposition_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_empirical_mode_decomposition_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_empirical_mode_decomposition_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_empirical_mode_decomposition_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Engulfing : IDisposable
{
    private readonly WickraHandle _handle;

    public Engulfing()
    {
        var ptr = NativeMethods.wickra_engulfing_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Engulfing parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_engulfing_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_engulfing_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_engulfing_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_engulfing_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_engulfing_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_engulfing_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_engulfing_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Equivolume : IDisposable
{
    private readonly WickraHandle _handle;

    public Equivolume(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_equivolume_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Equivolume parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_equivolume_free);
    }

    public EquivolumeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraEquivolumeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_equivolume_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new EquivolumeOutput(native.height, native.width) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_equivolume_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_equivolume_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_equivolume_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_equivolume_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EstimatedLeverageRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public EstimatedLeverageRatio()
    {
        var ptr = NativeMethods.wickra_estimated_leverage_ratio_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EstimatedLeverageRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_estimated_leverage_ratio_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_estimated_leverage_ratio_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_estimated_leverage_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_estimated_leverage_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_estimated_leverage_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_estimated_leverage_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EvenBetterSinewave : IDisposable
{
    private readonly WickraHandle _handle;

    public EvenBetterSinewave(int hpPeriod, int ssfLength)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(hpPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(ssfLength);
        var ptr = NativeMethods.wickra_even_better_sinewave_new((nuint)hpPeriod, (nuint)ssfLength);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EvenBetterSinewave parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_even_better_sinewave_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_even_better_sinewave_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_even_better_sinewave_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_even_better_sinewave_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_even_better_sinewave_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_even_better_sinewave_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_even_better_sinewave_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EveningDojiStar : IDisposable
{
    private readonly WickraHandle _handle;

    public EveningDojiStar()
    {
        var ptr = NativeMethods.wickra_evening_doji_star_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EveningDojiStar parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_evening_doji_star_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_evening_doji_star_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_evening_doji_star_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_evening_doji_star_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_evening_doji_star_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_evening_doji_star_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_evening_doji_star_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Evwma : IDisposable
{
    private readonly WickraHandle _handle;

    public Evwma(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_evwma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Evwma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_evwma_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_evwma_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_evwma_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_evwma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_evwma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_evwma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_evwma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class EwmaVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public EwmaVolatility(double lambda)
    {
        var ptr = NativeMethods.wickra_ewma_volatility_new(lambda);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid EwmaVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ewma_volatility_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ewma_volatility_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ewma_volatility_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ewma_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ewma_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ewma_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ewma_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Expectancy : IDisposable
{
    private readonly WickraHandle _handle;

    public Expectancy(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_expectancy_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Expectancy parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_expectancy_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_expectancy_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_expectancy_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_expectancy_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_expectancy_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_expectancy_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_expectancy_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FallingThreeMethods : IDisposable
{
    private readonly WickraHandle _handle;

    public FallingThreeMethods()
    {
        var ptr = NativeMethods.wickra_falling_three_methods_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FallingThreeMethods parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_falling_three_methods_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_falling_three_methods_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_falling_three_methods_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_falling_three_methods_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_falling_three_methods_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_falling_three_methods_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_falling_three_methods_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Fama : IDisposable
{
    private readonly WickraHandle _handle;

    public Fama(double fastLimit, double slowLimit)
    {
        var ptr = NativeMethods.wickra_fama_new(fastLimit, slowLimit);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Fama parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fama_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_fama_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_fama_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fama_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fama_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fama_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fama_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibArcs : IDisposable
{
    private readonly WickraHandle _handle;

    public FibArcs()
    {
        var ptr = NativeMethods.wickra_fib_arcs_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibArcs parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_arcs_free);
    }

    public FibArcsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibArcsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_arcs_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibArcsOutput(native.arc_382, native.arc_500, native.arc_618) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_arcs_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_arcs_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_arcs_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_arcs_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibChannel : IDisposable
{
    private readonly WickraHandle _handle;

    public FibChannel()
    {
        var ptr = NativeMethods.wickra_fib_channel_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibChannel parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_channel_free);
    }

    public FibChannelOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibChannelOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_channel_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibChannelOutput(native.@base, native.level_618, native.level_1000, native.level_1618) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_channel_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_channel_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_channel_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_channel_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibConfluence : IDisposable
{
    private readonly WickraHandle _handle;

    public FibConfluence()
    {
        var ptr = NativeMethods.wickra_fib_confluence_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibConfluence parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_confluence_free);
    }

    public FibConfluenceOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibConfluenceOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_confluence_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibConfluenceOutput(native.price, native.strength) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_confluence_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_confluence_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_confluence_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_confluence_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibExtension : IDisposable
{
    private readonly WickraHandle _handle;

    public FibExtension()
    {
        var ptr = NativeMethods.wickra_fib_extension_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibExtension parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_extension_free);
    }

    public FibExtensionOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibExtensionOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_extension_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibExtensionOutput(native.level_1272, native.level_1414, native.level_1618, native.level_2000, native.level_2618) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_extension_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_extension_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_extension_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_extension_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibFan : IDisposable
{
    private readonly WickraHandle _handle;

    public FibFan()
    {
        var ptr = NativeMethods.wickra_fib_fan_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibFan parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_fan_free);
    }

    public FibFanOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibFanOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_fan_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibFanOutput(native.fan_382, native.fan_500, native.fan_618) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_fan_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_fan_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_fan_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_fan_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibProjection : IDisposable
{
    private readonly WickraHandle _handle;

    public FibProjection()
    {
        var ptr = NativeMethods.wickra_fib_projection_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibProjection parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_projection_free);
    }

    public FibProjectionOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibProjectionOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_projection_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibProjectionOutput(native.level_618, native.level_1000, native.level_1618, native.level_2618) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_projection_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_projection_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_projection_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_projection_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibRetracement : IDisposable
{
    private readonly WickraHandle _handle;

    public FibRetracement()
    {
        var ptr = NativeMethods.wickra_fib_retracement_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibRetracement parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_retracement_free);
    }

    public FibRetracementOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibRetracementOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_retracement_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibRetracementOutput(native.level_0, native.level_236, native.level_382, native.level_500, native.level_618, native.level_786, native.level_1000) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_retracement_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_retracement_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_retracement_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_retracement_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibTimeZones : IDisposable
{
    private readonly WickraHandle _handle;

    public FibTimeZones()
    {
        var ptr = NativeMethods.wickra_fib_time_zones_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibTimeZones parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fib_time_zones_free);
    }

    public FibTimeZonesOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibTimeZonesOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fib_time_zones_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibTimeZonesOutput(native.on_zone, native.bars_to_next) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fib_time_zones_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fib_time_zones_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fib_time_zones_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fib_time_zones_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FibonacciPivots : IDisposable
{
    private readonly WickraHandle _handle;

    public FibonacciPivots()
    {
        var ptr = NativeMethods.wickra_fibonacci_pivots_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FibonacciPivots parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fibonacci_pivots_free);
    }

    public FibonacciPivotsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFibonacciPivotsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fibonacci_pivots_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FibonacciPivotsOutput(native.pp, native.r1, native.r2, native.r3, native.s1, native.s2, native.s3) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fibonacci_pivots_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fibonacci_pivots_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fibonacci_pivots_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fibonacci_pivots_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FisherRsi : IDisposable
{
    private readonly WickraHandle _handle;

    public FisherRsi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_fisher_rsi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FisherRsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fisher_rsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_fisher_rsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_fisher_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fisher_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fisher_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fisher_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fisher_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FisherTransform : IDisposable
{
    private readonly WickraHandle _handle;

    public FisherTransform(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_fisher_transform_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FisherTransform parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fisher_transform_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_fisher_transform_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_fisher_transform_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fisher_transform_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fisher_transform_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fisher_transform_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fisher_transform_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FlagPennant : IDisposable
{
    private readonly WickraHandle _handle;

    public FlagPennant()
    {
        var ptr = NativeMethods.wickra_flag_pennant_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FlagPennant parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_flag_pennant_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_flag_pennant_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_flag_pennant_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_flag_pennant_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_flag_pennant_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_flag_pennant_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_flag_pennant_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Footprint : IDisposable
{
    private readonly WickraHandle _handle;

    public Footprint(double tickSize)
    {
        var ptr = NativeMethods.wickra_footprint_new(tickSize);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Footprint parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_footprint_free);
    }

    public FootprintLevel[] Update(double price, double size, bool isBuy, long timestamp)
    {
        const int cap = 64;
        Span<WickraFootprintLevel> buffer = stackalloc WickraFootprintLevel[cap];
        long count;
        unsafe
        {
            fixed (WickraFootprintLevel* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_footprint_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<FootprintLevel>();
        }

        var result = new FootprintLevel[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new FootprintLevel(buffer[i].price, buffer[i].bid_vol, buffer[i].ask_vol);
        }

        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_footprint_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_footprint_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_footprint_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_footprint_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ForceIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public ForceIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_force_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ForceIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_force_index_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_force_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_force_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_force_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_force_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_force_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_force_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FractalChaosBands : IDisposable
{
    private readonly WickraHandle _handle;

    public FractalChaosBands(int k)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(k);
        var ptr = NativeMethods.wickra_fractal_chaos_bands_new((nuint)k);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FractalChaosBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fractal_chaos_bands_free);
    }

    public FractalChaosBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraFractalChaosBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_fractal_chaos_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new FractalChaosBandsOutput(native.upper, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fractal_chaos_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fractal_chaos_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fractal_chaos_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fractal_chaos_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Frama : IDisposable
{
    private readonly WickraHandle _handle;

    public Frama(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_frama_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Frama parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_frama_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_frama_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_frama_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_frama_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_frama_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_frama_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_frama_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FryPanBottom : IDisposable
{
    private readonly WickraHandle _handle;

    public FryPanBottom(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_fry_pan_bottom_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FryPanBottom parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_fry_pan_bottom_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_fry_pan_bottom_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_fry_pan_bottom_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_fry_pan_bottom_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_fry_pan_bottom_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_fry_pan_bottom_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_fry_pan_bottom_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FundingBasis : IDisposable
{
    private readonly WickraHandle _handle;

    public FundingBasis()
    {
        var ptr = NativeMethods.wickra_funding_basis_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FundingBasis parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_funding_basis_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_funding_basis_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_funding_basis_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_funding_basis_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_funding_basis_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_funding_basis_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FundingImpliedApr : IDisposable
{
    private readonly WickraHandle _handle;

    public FundingImpliedApr(double intervalsPerYear)
    {
        var ptr = NativeMethods.wickra_funding_implied_apr_new(intervalsPerYear);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FundingImpliedApr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_funding_implied_apr_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_funding_implied_apr_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_funding_implied_apr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_funding_implied_apr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_funding_implied_apr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_funding_implied_apr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FundingRate : IDisposable
{
    private readonly WickraHandle _handle;

    public FundingRate()
    {
        var ptr = NativeMethods.wickra_funding_rate_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FundingRate parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_funding_rate_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_funding_rate_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_funding_rate_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_funding_rate_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_funding_rate_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_funding_rate_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FundingRateMean : IDisposable
{
    private readonly WickraHandle _handle;

    public FundingRateMean(int window)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        var ptr = NativeMethods.wickra_funding_rate_mean_new((nuint)window);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FundingRateMean parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_funding_rate_mean_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_funding_rate_mean_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_funding_rate_mean_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_funding_rate_mean_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_funding_rate_mean_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_funding_rate_mean_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class FundingRateZScore : IDisposable
{
    private readonly WickraHandle _handle;

    public FundingRateZScore(int window)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        var ptr = NativeMethods.wickra_funding_rate_z_score_new((nuint)window);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid FundingRateZScore parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_funding_rate_z_score_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_funding_rate_z_score_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_funding_rate_z_score_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_funding_rate_z_score_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_funding_rate_z_score_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_funding_rate_z_score_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GainLossRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public GainLossRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_gain_loss_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GainLossRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_gain_loss_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_gain_loss_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_gain_loss_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_gain_loss_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_gain_loss_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_gain_loss_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_gain_loss_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GainToPainRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public GainToPainRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_gain_to_pain_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GainToPainRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_gain_to_pain_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_gain_to_pain_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_gain_to_pain_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_gain_to_pain_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_gain_to_pain_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_gain_to_pain_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_gain_to_pain_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GapSideBySideWhite : IDisposable
{
    private readonly WickraHandle _handle;

    public GapSideBySideWhite()
    {
        var ptr = NativeMethods.wickra_gap_side_by_side_white_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GapSideBySideWhite parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_gap_side_by_side_white_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_gap_side_by_side_white_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_gap_side_by_side_white_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_gap_side_by_side_white_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_gap_side_by_side_white_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_gap_side_by_side_white_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_gap_side_by_side_white_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Garch11 : IDisposable
{
    private readonly WickraHandle _handle;

    public Garch11(double omega, double alpha, double beta)
    {
        var ptr = NativeMethods.wickra_garch11_new(omega, alpha, beta);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Garch11 parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_garch11_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_garch11_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_garch11_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_garch11_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_garch11_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_garch11_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_garch11_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GarmanKlassVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public GarmanKlassVolatility(int period, int tradingPeriods)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(tradingPeriods);
        var ptr = NativeMethods.wickra_garman_klass_volatility_new((nuint)period, (nuint)tradingPeriods);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GarmanKlassVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_garman_klass_volatility_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_garman_klass_volatility_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_garman_klass_volatility_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_garman_klass_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_garman_klass_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_garman_klass_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_garman_klass_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Gartley : IDisposable
{
    private readonly WickraHandle _handle;

    public Gartley()
    {
        var ptr = NativeMethods.wickra_gartley_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Gartley parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_gartley_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_gartley_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_gartley_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_gartley_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_gartley_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_gartley_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_gartley_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GatorOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public GatorOscillator(int jawPeriod, int teethPeriod, int lipsPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(jawPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(teethPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(lipsPeriod);
        var ptr = NativeMethods.wickra_gator_oscillator_new((nuint)jawPeriod, (nuint)teethPeriod, (nuint)lipsPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GatorOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_gator_oscillator_free);
    }

    public GatorOscillatorOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraGatorOscillatorOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_gator_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new GatorOscillatorOutput(native.upper, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_gator_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_gator_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_gator_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_gator_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GeneralizedDema : IDisposable
{
    private readonly WickraHandle _handle;

    public GeneralizedDema(int period, double v)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_generalized_dema_new((nuint)period, v);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GeneralizedDema parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_generalized_dema_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_generalized_dema_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_generalized_dema_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_generalized_dema_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_generalized_dema_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_generalized_dema_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_generalized_dema_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GeometricMa : IDisposable
{
    private readonly WickraHandle _handle;

    public GeometricMa(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_geometric_ma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GeometricMa parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_geometric_ma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_geometric_ma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_geometric_ma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_geometric_ma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_geometric_ma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_geometric_ma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_geometric_ma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GoldenPocket : IDisposable
{
    private readonly WickraHandle _handle;

    public GoldenPocket()
    {
        var ptr = NativeMethods.wickra_golden_pocket_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GoldenPocket parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_golden_pocket_free);
    }

    public GoldenPocketOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraGoldenPocketOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_golden_pocket_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new GoldenPocketOutput(native.low, native.mid, native.high) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_golden_pocket_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_golden_pocket_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_golden_pocket_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_golden_pocket_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GrangerCausality : IDisposable
{
    private readonly WickraHandle _handle;

    public GrangerCausality(int period, int lag)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(lag);
        var ptr = NativeMethods.wickra_granger_causality_new((nuint)period, (nuint)lag);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GrangerCausality parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_granger_causality_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_granger_causality_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_granger_causality_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_granger_causality_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_granger_causality_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_granger_causality_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_granger_causality_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class GravestoneDoji : IDisposable
{
    private readonly WickraHandle _handle;

    public GravestoneDoji()
    {
        var ptr = NativeMethods.wickra_gravestone_doji_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid GravestoneDoji parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_gravestone_doji_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_gravestone_doji_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_gravestone_doji_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_gravestone_doji_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_gravestone_doji_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_gravestone_doji_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_gravestone_doji_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Hammer : IDisposable
{
    private readonly WickraHandle _handle;

    public Hammer()
    {
        var ptr = NativeMethods.wickra_hammer_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Hammer parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hammer_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_hammer_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hammer_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hammer_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hammer_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hammer_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hammer_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HangingMan : IDisposable
{
    private readonly WickraHandle _handle;

    public HangingMan()
    {
        var ptr = NativeMethods.wickra_hanging_man_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HangingMan parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hanging_man_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_hanging_man_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hanging_man_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hanging_man_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hanging_man_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hanging_man_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hanging_man_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Harami : IDisposable
{
    private readonly WickraHandle _handle;

    public Harami()
    {
        var ptr = NativeMethods.wickra_harami_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Harami parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_harami_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_harami_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_harami_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_harami_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_harami_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_harami_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_harami_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HaramiCross : IDisposable
{
    private readonly WickraHandle _handle;

    public HaramiCross()
    {
        var ptr = NativeMethods.wickra_harami_cross_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HaramiCross parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_harami_cross_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_harami_cross_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_harami_cross_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_harami_cross_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_harami_cross_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_harami_cross_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_harami_cross_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HasbrouckInformationShare : IDisposable
{
    private readonly WickraHandle _handle;

    public HasbrouckInformationShare(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_hasbrouck_information_share_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HasbrouckInformationShare parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hasbrouck_information_share_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_hasbrouck_information_share_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hasbrouck_information_share_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hasbrouck_information_share_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hasbrouck_information_share_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hasbrouck_information_share_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hasbrouck_information_share_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HeadAndShoulders : IDisposable
{
    private readonly WickraHandle _handle;

    public HeadAndShoulders()
    {
        var ptr = NativeMethods.wickra_head_and_shoulders_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HeadAndShoulders parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_head_and_shoulders_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_head_and_shoulders_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_head_and_shoulders_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_head_and_shoulders_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_head_and_shoulders_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_head_and_shoulders_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_head_and_shoulders_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HeikinAshi : IDisposable
{
    private readonly WickraHandle _handle;

    public HeikinAshi()
    {
        var ptr = NativeMethods.wickra_heikin_ashi_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HeikinAshi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_heikin_ashi_free);
    }

    public HeikinAshiOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraHeikinAshiOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_heikin_ashi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new HeikinAshiOutput(native.open, native.high, native.low, native.close) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_heikin_ashi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_heikin_ashi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_heikin_ashi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_heikin_ashi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HeikinAshiOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public HeikinAshiOscillator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_heikin_ashi_oscillator_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HeikinAshiOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_heikin_ashi_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_heikin_ashi_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_heikin_ashi_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_heikin_ashi_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_heikin_ashi_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_heikin_ashi_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_heikin_ashi_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HiLoActivator : IDisposable
{
    private readonly WickraHandle _handle;

    public HiLoActivator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_hi_lo_activator_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HiLoActivator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hi_lo_activator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_hi_lo_activator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hi_lo_activator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hi_lo_activator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hi_lo_activator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hi_lo_activator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hi_lo_activator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HighLowIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public HighLowIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_high_low_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HighLowIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_high_low_index_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_high_low_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_high_low_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_high_low_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_high_low_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_high_low_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HighLowRange : IDisposable
{
    private readonly WickraHandle _handle;

    public HighLowRange()
    {
        var ptr = NativeMethods.wickra_high_low_range_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HighLowRange parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_high_low_range_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_high_low_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_high_low_range_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_high_low_range_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_high_low_range_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_high_low_range_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_high_low_range_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HighLowVolumeNodes : IDisposable
{
    private readonly WickraHandle _handle;

    public HighLowVolumeNodes(int period, int bins)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(bins);
        var ptr = NativeMethods.wickra_high_low_volume_nodes_new((nuint)period, (nuint)bins);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HighLowVolumeNodes parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_high_low_volume_nodes_free);
    }

    public HighLowVolumeNodesOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraHighLowVolumeNodesOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_high_low_volume_nodes_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new HighLowVolumeNodesOutput(native.hvn, native.lvn) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_high_low_volume_nodes_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_high_low_volume_nodes_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_high_low_volume_nodes_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_high_low_volume_nodes_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HighWave : IDisposable
{
    private readonly WickraHandle _handle;

    public HighWave()
    {
        var ptr = NativeMethods.wickra_high_wave_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HighWave parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_high_wave_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_high_wave_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_high_wave_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_high_wave_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_high_wave_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_high_wave_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_high_wave_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HighpassFilter : IDisposable
{
    private readonly WickraHandle _handle;

    public HighpassFilter(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_highpass_filter_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HighpassFilter parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_highpass_filter_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_highpass_filter_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_highpass_filter_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_highpass_filter_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_highpass_filter_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_highpass_filter_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_highpass_filter_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Hikkake : IDisposable
{
    private readonly WickraHandle _handle;

    public Hikkake()
    {
        var ptr = NativeMethods.wickra_hikkake_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Hikkake parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hikkake_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_hikkake_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hikkake_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hikkake_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hikkake_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hikkake_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hikkake_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HikkakeModified : IDisposable
{
    private readonly WickraHandle _handle;

    public HikkakeModified()
    {
        var ptr = NativeMethods.wickra_hikkake_modified_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HikkakeModified parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hikkake_modified_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_hikkake_modified_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hikkake_modified_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hikkake_modified_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hikkake_modified_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hikkake_modified_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hikkake_modified_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HilbertDominantCycle : IDisposable
{
    private readonly WickraHandle _handle;

    public HilbertDominantCycle()
    {
        var ptr = NativeMethods.wickra_hilbert_dominant_cycle_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HilbertDominantCycle parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hilbert_dominant_cycle_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_hilbert_dominant_cycle_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hilbert_dominant_cycle_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hilbert_dominant_cycle_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hilbert_dominant_cycle_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hilbert_dominant_cycle_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hilbert_dominant_cycle_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HistoricalVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public HistoricalVolatility(int period, int tradingPeriods)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(tradingPeriods);
        var ptr = NativeMethods.wickra_historical_volatility_new((nuint)period, (nuint)tradingPeriods);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HistoricalVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_historical_volatility_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_historical_volatility_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_historical_volatility_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_historical_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_historical_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_historical_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_historical_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Hma : IDisposable
{
    private readonly WickraHandle _handle;

    public Hma(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_hma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Hma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_hma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HoltWinters : IDisposable
{
    private readonly WickraHandle _handle;

    public HoltWinters(double alpha, double beta)
    {
        var ptr = NativeMethods.wickra_holt_winters_new(alpha, beta);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HoltWinters parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_holt_winters_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_holt_winters_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_holt_winters_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_holt_winters_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_holt_winters_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_holt_winters_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_holt_winters_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HomingPigeon : IDisposable
{
    private readonly WickraHandle _handle;

    public HomingPigeon()
    {
        var ptr = NativeMethods.wickra_homing_pigeon_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HomingPigeon parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_homing_pigeon_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_homing_pigeon_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_homing_pigeon_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_homing_pigeon_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_homing_pigeon_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_homing_pigeon_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_homing_pigeon_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HtDcPhase : IDisposable
{
    private readonly WickraHandle _handle;

    public HtDcPhase()
    {
        var ptr = NativeMethods.wickra_ht_dc_phase_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HtDcPhase parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ht_dc_phase_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ht_dc_phase_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ht_dc_phase_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ht_dc_phase_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ht_dc_phase_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ht_dc_phase_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ht_dc_phase_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HtPhasor : IDisposable
{
    private readonly WickraHandle _handle;

    public HtPhasor()
    {
        var ptr = NativeMethods.wickra_ht_phasor_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HtPhasor parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ht_phasor_free);
    }

    public HtPhasorOutput? Update(double @value)
    {
        WickraHtPhasorOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_ht_phasor_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new HtPhasorOutput(native.inphase, native.quadrature) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ht_phasor_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ht_phasor_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ht_phasor_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ht_phasor_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HtTrendMode : IDisposable
{
    private readonly WickraHandle _handle;

    public HtTrendMode()
    {
        var ptr = NativeMethods.wickra_ht_trend_mode_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HtTrendMode parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ht_trend_mode_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ht_trend_mode_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ht_trend_mode_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ht_trend_mode_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ht_trend_mode_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ht_trend_mode_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ht_trend_mode_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HurstChannel : IDisposable
{
    private readonly WickraHandle _handle;

    public HurstChannel(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_hurst_channel_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HurstChannel parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hurst_channel_free);
    }

    public HurstChannelOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraHurstChannelOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_hurst_channel_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new HurstChannelOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hurst_channel_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hurst_channel_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hurst_channel_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hurst_channel_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class HurstExponent : IDisposable
{
    private readonly WickraHandle _handle;

    public HurstExponent(int period, int chunks)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(chunks);
        var ptr = NativeMethods.wickra_hurst_exponent_new((nuint)period, (nuint)chunks);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid HurstExponent parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_hurst_exponent_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_hurst_exponent_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_hurst_exponent_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_hurst_exponent_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_hurst_exponent_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_hurst_exponent_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_hurst_exponent_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Ichimoku : IDisposable
{
    private readonly WickraHandle _handle;

    public Ichimoku(int tenkanPeriod, int kijunPeriod, int senkouBPeriod, int displacement)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(tenkanPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(kijunPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(senkouBPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(displacement);
        var ptr = NativeMethods.wickra_ichimoku_new((nuint)tenkanPeriod, (nuint)kijunPeriod, (nuint)senkouBPeriod, (nuint)displacement);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Ichimoku parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ichimoku_free);
    }

    public IchimokuOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraIchimokuOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_ichimoku_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new IchimokuOutput(native.tenkan, native.kijun, native.senkou_a, native.senkou_b, native.chikou) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ichimoku_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ichimoku_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ichimoku_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ichimoku_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class IdenticalThreeCrows : IDisposable
{
    private readonly WickraHandle _handle;

    public IdenticalThreeCrows()
    {
        var ptr = NativeMethods.wickra_identical_three_crows_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid IdenticalThreeCrows parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_identical_three_crows_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_identical_three_crows_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_identical_three_crows_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_identical_three_crows_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_identical_three_crows_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_identical_three_crows_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_identical_three_crows_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ImbalanceBars : IDisposable
{
    private readonly WickraHandle _handle;

    public ImbalanceBars(double threshold)
    {
        var ptr = NativeMethods.wickra_imbalance_bars_new(threshold);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ImbalanceBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_imbalance_bars_free);
    }

    public ImbalanceBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraImbalanceBar> buffer = stackalloc WickraImbalanceBar[cap];
        long count;
        unsafe
        {
            fixed (WickraImbalanceBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_imbalance_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<ImbalanceBar>();
        }

        var result = new ImbalanceBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new ImbalanceBar(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].imbalance, buffer[i].direction);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_imbalance_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_imbalance_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class InNeck : IDisposable
{
    private readonly WickraHandle _handle;

    public InNeck()
    {
        var ptr = NativeMethods.wickra_in_neck_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid InNeck parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_in_neck_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_in_neck_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_in_neck_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_in_neck_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_in_neck_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_in_neck_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_in_neck_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Inertia : IDisposable
{
    private readonly WickraHandle _handle;

    public Inertia(int rviPeriod, int linregPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rviPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(linregPeriod);
        var ptr = NativeMethods.wickra_inertia_new((nuint)rviPeriod, (nuint)linregPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Inertia parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_inertia_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_inertia_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_inertia_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_inertia_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_inertia_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_inertia_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_inertia_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class InformationRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public InformationRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_information_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid InformationRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_information_ratio_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_information_ratio_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_information_ratio_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_information_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_information_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_information_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_information_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class InitialBalance : IDisposable
{
    private readonly WickraHandle _handle;

    public InitialBalance(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_initial_balance_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid InitialBalance parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_initial_balance_free);
    }

    public InitialBalanceOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraInitialBalanceOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_initial_balance_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new InitialBalanceOutput(native.high, native.low) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_initial_balance_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_initial_balance_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_initial_balance_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_initial_balance_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class InstantaneousTrendline : IDisposable
{
    private readonly WickraHandle _handle;

    public InstantaneousTrendline(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_instantaneous_trendline_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid InstantaneousTrendline parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_instantaneous_trendline_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_instantaneous_trendline_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_instantaneous_trendline_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_instantaneous_trendline_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_instantaneous_trendline_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_instantaneous_trendline_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_instantaneous_trendline_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class IntradayIntensity : IDisposable
{
    private readonly WickraHandle _handle;

    public IntradayIntensity()
    {
        var ptr = NativeMethods.wickra_intraday_intensity_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid IntradayIntensity parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_intraday_intensity_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_intraday_intensity_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_intraday_intensity_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_intraday_intensity_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_intraday_intensity_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_intraday_intensity_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_intraday_intensity_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class IntradayMomentumIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public IntradayMomentumIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_intraday_momentum_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid IntradayMomentumIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_intraday_momentum_index_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_intraday_momentum_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_intraday_momentum_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_intraday_momentum_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_intraday_momentum_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_intraday_momentum_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_intraday_momentum_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class IntradayVolatilityProfile : IDisposable
{
    private readonly WickraHandle _handle;
    private readonly int _valuesCapacity;

    public IntradayVolatilityProfile(int buckets, int utcOffsetMinutes)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(buckets);
        var ptr = NativeMethods.wickra_intraday_volatility_profile_new((nuint)buckets, utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid IntradayVolatilityProfile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_intraday_volatility_profile_free);
        _valuesCapacity = buckets;
    }

    public double[]? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var values = new double[_valuesCapacity];
        long length;
        unsafe
        {
            fixed (double* valuesPtr = values)
            {
                length = (long)NativeMethods.wickra_intraday_volatility_profile_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, valuesPtr, (nuint)_valuesCapacity);
            }
        }

        GC.KeepAlive(_handle);
        if (length < 0)
        {
            return null;
        }

        if (length < values.Length)
        {
            Array.Resize(ref values, (int)length);
        }

        return values;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_intraday_volatility_profile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_intraday_volatility_profile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_intraday_volatility_profile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_intraday_volatility_profile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class InverseFisherTransform : IDisposable
{
    private readonly WickraHandle _handle;

    public InverseFisherTransform(double scale)
    {
        var ptr = NativeMethods.wickra_inverse_fisher_transform_new(scale);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid InverseFisherTransform parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_inverse_fisher_transform_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_inverse_fisher_transform_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_inverse_fisher_transform_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_inverse_fisher_transform_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_inverse_fisher_transform_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_inverse_fisher_transform_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_inverse_fisher_transform_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class InvertedHammer : IDisposable
{
    private readonly WickraHandle _handle;

    public InvertedHammer()
    {
        var ptr = NativeMethods.wickra_inverted_hammer_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid InvertedHammer parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_inverted_hammer_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_inverted_hammer_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_inverted_hammer_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_inverted_hammer_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_inverted_hammer_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_inverted_hammer_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_inverted_hammer_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class JarqueBera : IDisposable
{
    private readonly WickraHandle _handle;

    public JarqueBera(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_jarque_bera_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid JarqueBera parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_jarque_bera_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_jarque_bera_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_jarque_bera_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_jarque_bera_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_jarque_bera_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_jarque_bera_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_jarque_bera_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Jma : IDisposable
{
    private readonly WickraHandle _handle;

    public Jma(int period, double phase, uint power)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_jma_new((nuint)period, phase, power);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Jma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_jma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_jma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_jma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_jma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_jma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_jma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_jma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class JumpIndicator : IDisposable
{
    private readonly WickraHandle _handle;

    public JumpIndicator(int period, double threshold)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_jump_indicator_new((nuint)period, threshold);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid JumpIndicator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_jump_indicator_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_jump_indicator_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_jump_indicator_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_jump_indicator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_jump_indicator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_jump_indicator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_jump_indicator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public KRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_k_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_k_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_k_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_k_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_k_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_k_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_k_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_k_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KagiBars : IDisposable
{
    private readonly WickraHandle _handle;

    public KagiBars(double reversal)
    {
        var ptr = NativeMethods.wickra_kagi_bars_new(reversal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KagiBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kagi_bars_free);
    }

    public KagiBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraKagiBar> buffer = stackalloc WickraKagiBar[cap];
        long count;
        unsafe
        {
            fixed (WickraKagiBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_kagi_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<KagiBar>();
        }

        var result = new KagiBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new KagiBar(buffer[i].start, buffer[i].end, buffer[i].direction);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kagi_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kagi_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KalmanHedgeRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public KalmanHedgeRatio(double delta, double observationVar)
    {
        var ptr = NativeMethods.wickra_kalman_hedge_ratio_new(delta, observationVar);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KalmanHedgeRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kalman_hedge_ratio_free);
    }

    public KalmanHedgeRatioOutput? Update(double x, double y)
    {
        WickraKalmanHedgeRatioOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_kalman_hedge_ratio_update(_handle.DangerousGetHandle(), x, y, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new KalmanHedgeRatioOutput(native.hedge_ratio, native.intercept, native.spread) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kalman_hedge_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kalman_hedge_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kalman_hedge_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kalman_hedge_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Kama : IDisposable
{
    private readonly WickraHandle _handle;

    public Kama(int erPeriod, int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(erPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_kama_new((nuint)erPeriod, (nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Kama parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kama_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_kama_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_kama_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kama_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kama_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kama_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kama_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KaseDevStop : IDisposable
{
    private readonly WickraHandle _handle;

    public KaseDevStop(int period, double dev)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_kase_dev_stop_new((nuint)period, dev);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KaseDevStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kase_dev_stop_free);
    }

    public KaseDevStopOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraKaseDevStopOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_kase_dev_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new KaseDevStopOutput(native.@value, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kase_dev_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kase_dev_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kase_dev_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kase_dev_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KasePermissionStochastic : IDisposable
{
    private readonly WickraHandle _handle;

    public KasePermissionStochastic(int length, int smooth)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(length);
        ArgumentOutOfRangeException.ThrowIfNegative(smooth);
        var ptr = NativeMethods.wickra_kase_permission_stochastic_new((nuint)length, (nuint)smooth);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KasePermissionStochastic parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kase_permission_stochastic_free);
    }

    public KasePermissionStochasticOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraKasePermissionStochasticOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_kase_permission_stochastic_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new KasePermissionStochasticOutput(native.fast, native.slow) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kase_permission_stochastic_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kase_permission_stochastic_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kase_permission_stochastic_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kase_permission_stochastic_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KellyCriterion : IDisposable
{
    private readonly WickraHandle _handle;

    public KellyCriterion(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_kelly_criterion_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KellyCriterion parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kelly_criterion_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_kelly_criterion_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_kelly_criterion_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kelly_criterion_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kelly_criterion_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kelly_criterion_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kelly_criterion_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Keltner : IDisposable
{
    private readonly WickraHandle _handle;

    public Keltner(int emaPeriod, int atrPeriod, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(emaPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        var ptr = NativeMethods.wickra_keltner_new((nuint)emaPeriod, (nuint)atrPeriod, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Keltner parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_keltner_free);
    }

    public KeltnerOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraKeltnerOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_keltner_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new KeltnerOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_keltner_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_keltner_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_keltner_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_keltner_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KendallTau : IDisposable
{
    private readonly WickraHandle _handle;

    public KendallTau(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_kendall_tau_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KendallTau parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kendall_tau_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_kendall_tau_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_kendall_tau_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kendall_tau_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kendall_tau_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kendall_tau_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kendall_tau_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Kicking : IDisposable
{
    private readonly WickraHandle _handle;

    public Kicking()
    {
        var ptr = NativeMethods.wickra_kicking_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Kicking parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kicking_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_kicking_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_kicking_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kicking_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kicking_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kicking_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kicking_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KickingByLength : IDisposable
{
    private readonly WickraHandle _handle;

    public KickingByLength()
    {
        var ptr = NativeMethods.wickra_kicking_by_length_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KickingByLength parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kicking_by_length_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_kicking_by_length_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_kicking_by_length_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kicking_by_length_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kicking_by_length_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kicking_by_length_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kicking_by_length_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Kst : IDisposable
{
    private readonly WickraHandle _handle;

    public Kst(int roc1, int roc2, int roc3, int roc4, int sma1, int sma2, int sma3, int sma4, int signal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(roc1);
        ArgumentOutOfRangeException.ThrowIfNegative(roc2);
        ArgumentOutOfRangeException.ThrowIfNegative(roc3);
        ArgumentOutOfRangeException.ThrowIfNegative(roc4);
        ArgumentOutOfRangeException.ThrowIfNegative(sma1);
        ArgumentOutOfRangeException.ThrowIfNegative(sma2);
        ArgumentOutOfRangeException.ThrowIfNegative(sma3);
        ArgumentOutOfRangeException.ThrowIfNegative(sma4);
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_kst_new((nuint)roc1, (nuint)roc2, (nuint)roc3, (nuint)roc4, (nuint)sma1, (nuint)sma2, (nuint)sma3, (nuint)sma4, (nuint)signal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Kst parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kst_free);
    }

    public KstOutput? Update(double @value)
    {
        WickraKstOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_kst_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new KstOutput(native.kst, native.signal) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kst_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kst_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kst_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kst_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Kurtosis : IDisposable
{
    private readonly WickraHandle _handle;

    public Kurtosis(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_kurtosis_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Kurtosis parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kurtosis_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_kurtosis_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_kurtosis_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kurtosis_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kurtosis_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kurtosis_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kurtosis_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Kvo : IDisposable
{
    private readonly WickraHandle _handle;

    public Kvo(int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_kvo_new((nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Kvo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kvo_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_kvo_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_kvo_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kvo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kvo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kvo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kvo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class KylesLambda : IDisposable
{
    private readonly WickraHandle _handle;

    public KylesLambda(int window)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        var ptr = NativeMethods.wickra_kyles_lambda_new((nuint)window);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid KylesLambda parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_kyles_lambda_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp, double mid)
    {
        var result = NativeMethods.wickra_kyles_lambda_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp, mid);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_kyles_lambda_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_kyles_lambda_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_kyles_lambda_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_kyles_lambda_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LadderBottom : IDisposable
{
    private readonly WickraHandle _handle;

    public LadderBottom()
    {
        var ptr = NativeMethods.wickra_ladder_bottom_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LadderBottom parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ladder_bottom_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_ladder_bottom_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ladder_bottom_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ladder_bottom_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ladder_bottom_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ladder_bottom_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ladder_bottom_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LaguerreRsi : IDisposable
{
    private readonly WickraHandle _handle;

    public LaguerreRsi(double gamma)
    {
        var ptr = NativeMethods.wickra_laguerre_rsi_new(gamma);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LaguerreRsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_laguerre_rsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_laguerre_rsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_laguerre_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_laguerre_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_laguerre_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_laguerre_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_laguerre_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LeadLagCrossCorrelation : IDisposable
{
    private readonly WickraHandle _handle;

    public LeadLagCrossCorrelation(int window, int maxLag)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        ArgumentOutOfRangeException.ThrowIfNegative(maxLag);
        var ptr = NativeMethods.wickra_lead_lag_cross_correlation_new((nuint)window, (nuint)maxLag);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LeadLagCrossCorrelation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_lead_lag_cross_correlation_free);
    }

    public LeadLagCrossCorrelationOutput? Update(double x, double y)
    {
        WickraLeadLagCrossCorrelationOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_lead_lag_cross_correlation_update(_handle.DangerousGetHandle(), x, y, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new LeadLagCrossCorrelationOutput(native.lag, native.correlation) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_lead_lag_cross_correlation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_lead_lag_cross_correlation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_lead_lag_cross_correlation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_lead_lag_cross_correlation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LinRegAngle : IDisposable
{
    private readonly WickraHandle _handle;

    public LinRegAngle(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_lin_reg_angle_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LinRegAngle parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_lin_reg_angle_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_lin_reg_angle_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_lin_reg_angle_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_lin_reg_angle_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_lin_reg_angle_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_lin_reg_angle_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_lin_reg_angle_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LinRegChannel : IDisposable
{
    private readonly WickraHandle _handle;

    public LinRegChannel(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_lin_reg_channel_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LinRegChannel parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_lin_reg_channel_free);
    }

    public LinRegChannelOutput? Update(double @value)
    {
        WickraLinRegChannelOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_lin_reg_channel_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new LinRegChannelOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_lin_reg_channel_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_lin_reg_channel_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_lin_reg_channel_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_lin_reg_channel_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LinRegIntercept : IDisposable
{
    private readonly WickraHandle _handle;

    public LinRegIntercept(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_lin_reg_intercept_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LinRegIntercept parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_lin_reg_intercept_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_lin_reg_intercept_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_lin_reg_intercept_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_lin_reg_intercept_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_lin_reg_intercept_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_lin_reg_intercept_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_lin_reg_intercept_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LinRegSlope : IDisposable
{
    private readonly WickraHandle _handle;

    public LinRegSlope(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_lin_reg_slope_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LinRegSlope parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_lin_reg_slope_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_lin_reg_slope_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_lin_reg_slope_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_lin_reg_slope_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_lin_reg_slope_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_lin_reg_slope_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_lin_reg_slope_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LinearRegression : IDisposable
{
    private readonly WickraHandle _handle;

    public LinearRegression(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_linear_regression_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LinearRegression parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_linear_regression_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_linear_regression_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_linear_regression_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_linear_regression_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_linear_regression_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_linear_regression_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_linear_regression_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LiquidationFeatures : IDisposable
{
    private readonly WickraHandle _handle;

    public LiquidationFeatures()
    {
        var ptr = NativeMethods.wickra_liquidation_features_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LiquidationFeatures parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_liquidation_features_free);
    }

    public LiquidationFeaturesOutput? Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        WickraLiquidationFeaturesOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_liquidation_features_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new LiquidationFeaturesOutput(native.long_, native.short_, native.net, native.total, native.imbalance) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_liquidation_features_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_liquidation_features_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_liquidation_features_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_liquidation_features_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LogReturn : IDisposable
{
    private readonly WickraHandle _handle;

    public LogReturn(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_log_return_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LogReturn parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_log_return_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_log_return_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_log_return_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_log_return_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_log_return_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_log_return_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_log_return_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LongLeggedDoji : IDisposable
{
    private readonly WickraHandle _handle;

    public LongLeggedDoji()
    {
        var ptr = NativeMethods.wickra_long_legged_doji_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LongLeggedDoji parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_long_legged_doji_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_long_legged_doji_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_long_legged_doji_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_long_legged_doji_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_long_legged_doji_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_long_legged_doji_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_long_legged_doji_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LongLine : IDisposable
{
    private readonly WickraHandle _handle;

    public LongLine()
    {
        var ptr = NativeMethods.wickra_long_line_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LongLine parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_long_line_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_long_line_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_long_line_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_long_line_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_long_line_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_long_line_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_long_line_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class LongShortRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public LongShortRatio()
    {
        var ptr = NativeMethods.wickra_long_short_ratio_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid LongShortRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_long_short_ratio_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_long_short_ratio_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_long_short_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_long_short_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_long_short_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_long_short_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class M2Measure : IDisposable
{
    private readonly WickraHandle _handle;

    public M2Measure(int period, double riskFree, double benchmarkStddev)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_m2_measure_new((nuint)period, riskFree, benchmarkStddev);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid M2Measure parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_m2_measure_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_m2_measure_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_m2_measure_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_m2_measure_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_m2_measure_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_m2_measure_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_m2_measure_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MaEnvelope : IDisposable
{
    private readonly WickraHandle _handle;

    public MaEnvelope(int period, double percent)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ma_envelope_new((nuint)period, percent);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MaEnvelope parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ma_envelope_free);
    }

    public MaEnvelopeOutput? Update(double @value)
    {
        WickraMaEnvelopeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_ma_envelope_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new MaEnvelopeOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ma_envelope_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ma_envelope_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ma_envelope_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ma_envelope_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MacdExt : IDisposable
{
    private readonly WickraHandle _handle;

    public MacdExt(int fast, byte fastType, int slow, byte slowType, int signal, byte signalType)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_macd_ext_new((nuint)fast, fastType, (nuint)slow, slowType, (nuint)signal, signalType);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MacdExt parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_macd_ext_free);
    }

    public MacdOutput? Update(double @value)
    {
        WickraMacdOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_macd_ext_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new MacdOutput(native.macd, native.signal, native.histogram) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_macd_ext_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_macd_ext_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_macd_ext_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_macd_ext_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MacdFix : IDisposable
{
    private readonly WickraHandle _handle;

    public MacdFix(int signal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_macd_fix_new((nuint)signal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MacdFix parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_macd_fix_free);
    }

    public MacdOutput? Update(double @value)
    {
        WickraMacdOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_macd_fix_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new MacdOutput(native.macd, native.signal, native.histogram) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_macd_fix_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_macd_fix_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_macd_fix_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_macd_fix_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MacdHistogram : IDisposable
{
    private readonly WickraHandle _handle;

    public MacdHistogram(int fast, int slow, int signal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_macd_histogram_new((nuint)fast, (nuint)slow, (nuint)signal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MacdHistogram parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_macd_histogram_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_macd_histogram_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_macd_histogram_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_macd_histogram_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_macd_histogram_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_macd_histogram_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_macd_histogram_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MacdIndicator : IDisposable
{
    private readonly WickraHandle _handle;

    public MacdIndicator(int fast, int slow, int signal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_macd_indicator_new((nuint)fast, (nuint)slow, (nuint)signal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MacdIndicator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_macd_indicator_free);
    }

    public MacdOutput? Update(double @value)
    {
        WickraMacdOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_macd_indicator_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new MacdOutput(native.macd, native.signal, native.histogram) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_macd_indicator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_macd_indicator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_macd_indicator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_macd_indicator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Mama : IDisposable
{
    private readonly WickraHandle _handle;

    public Mama(double fastLimit, double slowLimit)
    {
        var ptr = NativeMethods.wickra_mama_new(fastLimit, slowLimit);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Mama parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mama_free);
    }

    public MamaOutput? Update(double @value)
    {
        WickraMamaOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_mama_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new MamaOutput(native.mama, native.fama) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mama_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mama_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mama_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mama_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MarketFacilitationIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public MarketFacilitationIndex()
    {
        var ptr = NativeMethods.wickra_market_facilitation_index_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MarketFacilitationIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_market_facilitation_index_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_market_facilitation_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_market_facilitation_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_market_facilitation_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_market_facilitation_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_market_facilitation_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_market_facilitation_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MartinRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public MartinRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_martin_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MartinRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_martin_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_martin_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_martin_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_martin_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_martin_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_martin_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_martin_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Marubozu : IDisposable
{
    private readonly WickraHandle _handle;

    public Marubozu()
    {
        var ptr = NativeMethods.wickra_marubozu_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Marubozu parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_marubozu_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_marubozu_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_marubozu_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_marubozu_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_marubozu_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_marubozu_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_marubozu_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MassIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public MassIndex(int emaPeriod, int sumPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(emaPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(sumPeriod);
        var ptr = NativeMethods.wickra_mass_index_new((nuint)emaPeriod, (nuint)sumPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MassIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mass_index_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_mass_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_mass_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mass_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mass_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mass_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mass_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MatHold : IDisposable
{
    private readonly WickraHandle _handle;

    public MatHold()
    {
        var ptr = NativeMethods.wickra_mat_hold_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MatHold parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mat_hold_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_mat_hold_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_mat_hold_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mat_hold_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mat_hold_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mat_hold_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mat_hold_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MatchingLow : IDisposable
{
    private readonly WickraHandle _handle;

    public MatchingLow()
    {
        var ptr = NativeMethods.wickra_matching_low_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MatchingLow parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_matching_low_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_matching_low_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_matching_low_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_matching_low_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_matching_low_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_matching_low_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_matching_low_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MaxDrawdown : IDisposable
{
    private readonly WickraHandle _handle;

    public MaxDrawdown(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_max_drawdown_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MaxDrawdown parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_max_drawdown_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_max_drawdown_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_max_drawdown_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_max_drawdown_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_max_drawdown_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_max_drawdown_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_max_drawdown_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class McClellanOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public McClellanOscillator()
    {
        var ptr = NativeMethods.wickra_mc_clellan_oscillator_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid McClellanOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mc_clellan_oscillator_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_mc_clellan_oscillator_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mc_clellan_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mc_clellan_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mc_clellan_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mc_clellan_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class McClellanSummationIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public McClellanSummationIndex()
    {
        var ptr = NativeMethods.wickra_mc_clellan_summation_index_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid McClellanSummationIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mc_clellan_summation_index_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_mc_clellan_summation_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mc_clellan_summation_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mc_clellan_summation_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mc_clellan_summation_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mc_clellan_summation_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class McGinleyDynamic : IDisposable
{
    private readonly WickraHandle _handle;

    public McGinleyDynamic(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_mc_ginley_dynamic_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid McGinleyDynamic parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mc_ginley_dynamic_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_mc_ginley_dynamic_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_mc_ginley_dynamic_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mc_ginley_dynamic_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mc_ginley_dynamic_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mc_ginley_dynamic_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mc_ginley_dynamic_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MedianAbsoluteDeviation : IDisposable
{
    private readonly WickraHandle _handle;

    public MedianAbsoluteDeviation(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_median_absolute_deviation_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MedianAbsoluteDeviation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_median_absolute_deviation_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_median_absolute_deviation_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_median_absolute_deviation_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_median_absolute_deviation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_median_absolute_deviation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_median_absolute_deviation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_median_absolute_deviation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MedianChannel : IDisposable
{
    private readonly WickraHandle _handle;

    public MedianChannel(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_median_channel_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MedianChannel parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_median_channel_free);
    }

    public MedianChannelOutput? Update(double @value)
    {
        WickraMedianChannelOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_median_channel_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new MedianChannelOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_median_channel_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_median_channel_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_median_channel_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_median_channel_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MedianMa : IDisposable
{
    private readonly WickraHandle _handle;

    public MedianMa(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_median_ma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MedianMa parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_median_ma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_median_ma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_median_ma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_median_ma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_median_ma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_median_ma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_median_ma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MedianPrice : IDisposable
{
    private readonly WickraHandle _handle;

    public MedianPrice()
    {
        var ptr = NativeMethods.wickra_median_price_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MedianPrice parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_median_price_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_median_price_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_median_price_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_median_price_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_median_price_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_median_price_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_median_price_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Mfi : IDisposable
{
    private readonly WickraHandle _handle;

    public Mfi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_mfi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Mfi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mfi_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_mfi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_mfi_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mfi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mfi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mfi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mfi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Microprice : IDisposable
{
    private readonly WickraHandle _handle;

    public Microprice()
    {
        var ptr = NativeMethods.wickra_microprice_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Microprice parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_microprice_free);
    }

    public double Update(ReadOnlySpan<double> bidPrice, ReadOnlySpan<double> bidSize, ReadOnlySpan<double> askPrice, ReadOnlySpan<double> askSize)
    {
        if (bidSize.Length != bidPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (askSize.Length != askPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* bidPricePtr = bidPrice)
            fixed (double* bidSizePtr = bidSize)
            fixed (double* askPricePtr = askPrice)
            fixed (double* askSizePtr = askSize)
            {
                result = NativeMethods.wickra_microprice_update(_handle.DangerousGetHandle(), bidPricePtr, bidSizePtr, (nuint)bidPrice.Length, askPricePtr, askSizePtr, (nuint)askPrice.Length);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_microprice_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_microprice_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_microprice_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_microprice_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MidPoint : IDisposable
{
    private readonly WickraHandle _handle;

    public MidPoint(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_mid_point_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MidPoint parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mid_point_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_mid_point_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_mid_point_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mid_point_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mid_point_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mid_point_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mid_point_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MidPrice : IDisposable
{
    private readonly WickraHandle _handle;

    public MidPrice(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_mid_price_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MidPrice parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mid_price_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_mid_price_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_mid_price_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mid_price_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mid_price_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mid_price_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mid_price_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MinusDi : IDisposable
{
    private readonly WickraHandle _handle;

    public MinusDi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_minus_di_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MinusDi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_minus_di_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_minus_di_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_minus_di_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_minus_di_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_minus_di_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_minus_di_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_minus_di_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MinusDm : IDisposable
{
    private readonly WickraHandle _handle;

    public MinusDm(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_minus_dm_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MinusDm parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_minus_dm_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_minus_dm_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_minus_dm_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_minus_dm_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_minus_dm_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_minus_dm_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_minus_dm_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ModifiedMaStop : IDisposable
{
    private readonly WickraHandle _handle;

    public ModifiedMaStop(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_modified_ma_stop_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ModifiedMaStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_modified_ma_stop_free);
    }

    public ModifiedMaStopOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraModifiedMaStopOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_modified_ma_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ModifiedMaStopOutput(native.@value, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_modified_ma_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_modified_ma_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_modified_ma_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_modified_ma_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Mom : IDisposable
{
    private readonly WickraHandle _handle;

    public Mom(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_mom_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Mom parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_mom_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_mom_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_mom_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_mom_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_mom_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_mom_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_mom_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MorningDojiStar : IDisposable
{
    private readonly WickraHandle _handle;

    public MorningDojiStar()
    {
        var ptr = NativeMethods.wickra_morning_doji_star_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MorningDojiStar parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_morning_doji_star_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_morning_doji_star_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_morning_doji_star_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_morning_doji_star_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_morning_doji_star_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_morning_doji_star_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_morning_doji_star_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MorningEveningStar : IDisposable
{
    private readonly WickraHandle _handle;

    public MorningEveningStar()
    {
        var ptr = NativeMethods.wickra_morning_evening_star_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MorningEveningStar parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_morning_evening_star_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_morning_evening_star_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_morning_evening_star_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_morning_evening_star_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_morning_evening_star_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_morning_evening_star_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_morning_evening_star_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class MurreyMathLines : IDisposable
{
    private readonly WickraHandle _handle;

    public MurreyMathLines(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_murrey_math_lines_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid MurreyMathLines parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_murrey_math_lines_free);
    }

    public MurreyMathLinesOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraMurreyMathLinesOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_murrey_math_lines_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new MurreyMathLinesOutput(native.mm8_8, native.mm7_8, native.mm6_8, native.mm5_8, native.mm4_8, native.mm3_8, native.mm2_8, native.mm1_8, native.mm0_8) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_murrey_math_lines_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_murrey_math_lines_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_murrey_math_lines_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_murrey_math_lines_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class NakedPoc : IDisposable
{
    private readonly WickraHandle _handle;

    public NakedPoc(int sessionLen, int bins)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(sessionLen);
        ArgumentOutOfRangeException.ThrowIfNegative(bins);
        var ptr = NativeMethods.wickra_naked_poc_new((nuint)sessionLen, (nuint)bins);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid NakedPoc parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_naked_poc_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_naked_poc_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_naked_poc_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_naked_poc_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_naked_poc_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_naked_poc_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_naked_poc_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Natr : IDisposable
{
    private readonly WickraHandle _handle;

    public Natr(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_natr_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Natr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_natr_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_natr_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_natr_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_natr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_natr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_natr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_natr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class NewHighsNewLows : IDisposable
{
    private readonly WickraHandle _handle;

    public NewHighsNewLows()
    {
        var ptr = NativeMethods.wickra_new_highs_new_lows_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid NewHighsNewLows parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_new_highs_new_lows_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_new_highs_new_lows_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_new_highs_new_lows_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_new_highs_new_lows_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_new_highs_new_lows_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_new_highs_new_lows_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class NewPriceLines : IDisposable
{
    private readonly WickraHandle _handle;

    public NewPriceLines(int count)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(count);
        var ptr = NativeMethods.wickra_new_price_lines_new((nuint)count);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid NewPriceLines parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_new_price_lines_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_new_price_lines_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_new_price_lines_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_new_price_lines_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_new_price_lines_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_new_price_lines_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_new_price_lines_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Nrtr : IDisposable
{
    private readonly WickraHandle _handle;

    public Nrtr(double pct)
    {
        var ptr = NativeMethods.wickra_nrtr_new(pct);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Nrtr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_nrtr_free);
    }

    public NrtrOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraNrtrOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_nrtr_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new NrtrOutput(native.@value, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_nrtr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_nrtr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_nrtr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_nrtr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Nvi : IDisposable
{
    private readonly WickraHandle _handle;

    public Nvi()
    {
        var ptr = NativeMethods.wickra_nvi_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Nvi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_nvi_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_nvi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_nvi_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_nvi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_nvi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_nvi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_nvi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Obv : IDisposable
{
    private readonly WickraHandle _handle;

    public Obv()
    {
        var ptr = NativeMethods.wickra_obv_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Obv parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_obv_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_obv_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_obv_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_obv_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_obv_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_obv_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_obv_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OIPriceDivergence : IDisposable
{
    private readonly WickraHandle _handle;

    public OIPriceDivergence(int window)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        var ptr = NativeMethods.wickra_oi_price_divergence_new((nuint)window);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OIPriceDivergence parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_oi_price_divergence_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_oi_price_divergence_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_oi_price_divergence_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_oi_price_divergence_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_oi_price_divergence_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_oi_price_divergence_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OiToVolumeRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public OiToVolumeRatio()
    {
        var ptr = NativeMethods.wickra_oi_to_volume_ratio_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OiToVolumeRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_oi_to_volume_ratio_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_oi_to_volume_ratio_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_oi_to_volume_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_oi_to_volume_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_oi_to_volume_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_oi_to_volume_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OIWeighted : IDisposable
{
    private readonly WickraHandle _handle;

    public OIWeighted()
    {
        var ptr = NativeMethods.wickra_oi_weighted_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OIWeighted parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_oi_weighted_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_oi_weighted_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_oi_weighted_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_oi_weighted_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_oi_weighted_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_oi_weighted_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OmegaRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public OmegaRatio(int period, double threshold)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_omega_ratio_new((nuint)period, threshold);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OmegaRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_omega_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_omega_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_omega_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_omega_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_omega_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_omega_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_omega_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OnNeck : IDisposable
{
    private readonly WickraHandle _handle;

    public OnNeck()
    {
        var ptr = NativeMethods.wickra_on_neck_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OnNeck parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_on_neck_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_on_neck_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_on_neck_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_on_neck_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_on_neck_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_on_neck_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_on_neck_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OpenInterestDelta : IDisposable
{
    private readonly WickraHandle _handle;

    public OpenInterestDelta()
    {
        var ptr = NativeMethods.wickra_open_interest_delta_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OpenInterestDelta parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_open_interest_delta_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_open_interest_delta_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_open_interest_delta_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_open_interest_delta_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_open_interest_delta_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_open_interest_delta_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OpenInterestMomentum : IDisposable
{
    private readonly WickraHandle _handle;

    public OpenInterestMomentum(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_open_interest_momentum_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OpenInterestMomentum parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_open_interest_momentum_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_open_interest_momentum_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_open_interest_momentum_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_open_interest_momentum_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_open_interest_momentum_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_open_interest_momentum_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OpeningMarubozu : IDisposable
{
    private readonly WickraHandle _handle;

    public OpeningMarubozu()
    {
        var ptr = NativeMethods.wickra_opening_marubozu_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OpeningMarubozu parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_opening_marubozu_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_opening_marubozu_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_opening_marubozu_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_opening_marubozu_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_opening_marubozu_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_opening_marubozu_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_opening_marubozu_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OpeningRange : IDisposable
{
    private readonly WickraHandle _handle;

    public OpeningRange(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_opening_range_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OpeningRange parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_opening_range_free);
    }

    public OpeningRangeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraOpeningRangeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_opening_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new OpeningRangeOutput(native.high, native.low, native.breakout_distance) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_opening_range_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_opening_range_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_opening_range_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_opening_range_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OrderBookImbalanceFull : IDisposable
{
    private readonly WickraHandle _handle;

    public OrderBookImbalanceFull()
    {
        var ptr = NativeMethods.wickra_order_book_imbalance_full_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OrderBookImbalanceFull parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_order_book_imbalance_full_free);
    }

    public double Update(ReadOnlySpan<double> bidPrice, ReadOnlySpan<double> bidSize, ReadOnlySpan<double> askPrice, ReadOnlySpan<double> askSize)
    {
        if (bidSize.Length != bidPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (askSize.Length != askPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* bidPricePtr = bidPrice)
            fixed (double* bidSizePtr = bidSize)
            fixed (double* askPricePtr = askPrice)
            fixed (double* askSizePtr = askSize)
            {
                result = NativeMethods.wickra_order_book_imbalance_full_update(_handle.DangerousGetHandle(), bidPricePtr, bidSizePtr, (nuint)bidPrice.Length, askPricePtr, askSizePtr, (nuint)askPrice.Length);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_order_book_imbalance_full_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_order_book_imbalance_full_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_order_book_imbalance_full_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_order_book_imbalance_full_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OrderBookImbalanceTop1 : IDisposable
{
    private readonly WickraHandle _handle;

    public OrderBookImbalanceTop1()
    {
        var ptr = NativeMethods.wickra_order_book_imbalance_top1_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OrderBookImbalanceTop1 parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_order_book_imbalance_top1_free);
    }

    public double Update(ReadOnlySpan<double> bidPrice, ReadOnlySpan<double> bidSize, ReadOnlySpan<double> askPrice, ReadOnlySpan<double> askSize)
    {
        if (bidSize.Length != bidPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (askSize.Length != askPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* bidPricePtr = bidPrice)
            fixed (double* bidSizePtr = bidSize)
            fixed (double* askPricePtr = askPrice)
            fixed (double* askSizePtr = askSize)
            {
                result = NativeMethods.wickra_order_book_imbalance_top1_update(_handle.DangerousGetHandle(), bidPricePtr, bidSizePtr, (nuint)bidPrice.Length, askPricePtr, askSizePtr, (nuint)askPrice.Length);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_order_book_imbalance_top1_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_order_book_imbalance_top1_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_order_book_imbalance_top1_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_order_book_imbalance_top1_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OrderBookImbalanceTopN : IDisposable
{
    private readonly WickraHandle _handle;

    public OrderBookImbalanceTopN(int levels)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(levels);
        var ptr = NativeMethods.wickra_order_book_imbalance_top_n_new((nuint)levels);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OrderBookImbalanceTopN parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_order_book_imbalance_top_n_free);
    }

    public double Update(ReadOnlySpan<double> bidPrice, ReadOnlySpan<double> bidSize, ReadOnlySpan<double> askPrice, ReadOnlySpan<double> askSize)
    {
        if (bidSize.Length != bidPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (askSize.Length != askPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* bidPricePtr = bidPrice)
            fixed (double* bidSizePtr = bidSize)
            fixed (double* askPricePtr = askPrice)
            fixed (double* askSizePtr = askSize)
            {
                result = NativeMethods.wickra_order_book_imbalance_top_n_update(_handle.DangerousGetHandle(), bidPricePtr, bidSizePtr, (nuint)bidPrice.Length, askPricePtr, askSizePtr, (nuint)askPrice.Length);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_order_book_imbalance_top_n_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_order_book_imbalance_top_n_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_order_book_imbalance_top_n_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_order_book_imbalance_top_n_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OrderFlowImbalance : IDisposable
{
    private readonly WickraHandle _handle;

    public OrderFlowImbalance(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_order_flow_imbalance_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OrderFlowImbalance parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_order_flow_imbalance_free);
    }

    public double Update(ReadOnlySpan<double> bidPrice, ReadOnlySpan<double> bidSize, ReadOnlySpan<double> askPrice, ReadOnlySpan<double> askSize)
    {
        if (bidSize.Length != bidPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (askSize.Length != askPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* bidPricePtr = bidPrice)
            fixed (double* bidSizePtr = bidSize)
            fixed (double* askPricePtr = askPrice)
            fixed (double* askSizePtr = askSize)
            {
                result = NativeMethods.wickra_order_flow_imbalance_update(_handle.DangerousGetHandle(), bidPricePtr, bidSizePtr, (nuint)bidPrice.Length, askPricePtr, askSizePtr, (nuint)askPrice.Length);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_order_flow_imbalance_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_order_flow_imbalance_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_order_flow_imbalance_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_order_flow_imbalance_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OuHalfLife : IDisposable
{
    private readonly WickraHandle _handle;

    public OuHalfLife(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ou_half_life_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OuHalfLife parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ou_half_life_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_ou_half_life_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ou_half_life_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ou_half_life_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ou_half_life_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ou_half_life_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ou_half_life_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OvernightGap : IDisposable
{
    private readonly WickraHandle _handle;

    public OvernightGap(int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_overnight_gap_new(utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OvernightGap parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_overnight_gap_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_overnight_gap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_overnight_gap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_overnight_gap_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_overnight_gap_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_overnight_gap_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_overnight_gap_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class OvernightIntradayReturn : IDisposable
{
    private readonly WickraHandle _handle;

    public OvernightIntradayReturn(int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_overnight_intraday_return_new(utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid OvernightIntradayReturn parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_overnight_intraday_return_free);
    }

    public OvernightIntradayReturnOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraOvernightIntradayReturnOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_overnight_intraday_return_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new OvernightIntradayReturnOutput(native.overnight, native.intraday) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_overnight_intraday_return_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_overnight_intraday_return_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_overnight_intraday_return_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_overnight_intraday_return_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PainIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public PainIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_pain_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PainIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pain_index_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_pain_index_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pain_index_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pain_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pain_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pain_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pain_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PairSpreadZScore : IDisposable
{
    private readonly WickraHandle _handle;

    public PairSpreadZScore(int betaPeriod, int zPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(betaPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(zPeriod);
        var ptr = NativeMethods.wickra_pair_spread_z_score_new((nuint)betaPeriod, (nuint)zPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PairSpreadZScore parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pair_spread_z_score_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_pair_spread_z_score_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pair_spread_z_score_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pair_spread_z_score_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pair_spread_z_score_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pair_spread_z_score_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pair_spread_z_score_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PairwiseBeta : IDisposable
{
    private readonly WickraHandle _handle;

    public PairwiseBeta(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_pairwise_beta_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PairwiseBeta parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pairwise_beta_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_pairwise_beta_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pairwise_beta_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pairwise_beta_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pairwise_beta_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pairwise_beta_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pairwise_beta_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ParkinsonVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public ParkinsonVolatility(int period, int tradingPeriods)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(tradingPeriods);
        var ptr = NativeMethods.wickra_parkinson_volatility_new((nuint)period, (nuint)tradingPeriods);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ParkinsonVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_parkinson_volatility_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_parkinson_volatility_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_parkinson_volatility_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_parkinson_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_parkinson_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_parkinson_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_parkinson_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PearsonCorrelation : IDisposable
{
    private readonly WickraHandle _handle;

    public PearsonCorrelation(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_pearson_correlation_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PearsonCorrelation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pearson_correlation_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_pearson_correlation_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pearson_correlation_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pearson_correlation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pearson_correlation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pearson_correlation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pearson_correlation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PercentAboveMa : IDisposable
{
    private readonly WickraHandle _handle;

    public PercentAboveMa()
    {
        var ptr = NativeMethods.wickra_percent_above_ma_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PercentAboveMa parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_percent_above_ma_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_percent_above_ma_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_percent_above_ma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_percent_above_ma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_percent_above_ma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_percent_above_ma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PercentB : IDisposable
{
    private readonly WickraHandle _handle;

    public PercentB(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_percent_b_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PercentB parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_percent_b_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_percent_b_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_percent_b_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_percent_b_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_percent_b_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_percent_b_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_percent_b_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PercentageTrailingStop : IDisposable
{
    private readonly WickraHandle _handle;

    public PercentageTrailingStop(double percent)
    {
        var ptr = NativeMethods.wickra_percentage_trailing_stop_new(percent);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PercentageTrailingStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_percentage_trailing_stop_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_percentage_trailing_stop_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_percentage_trailing_stop_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_percentage_trailing_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_percentage_trailing_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_percentage_trailing_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_percentage_trailing_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PerpetualPremiumIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public PerpetualPremiumIndex()
    {
        var ptr = NativeMethods.wickra_perpetual_premium_index_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PerpetualPremiumIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_perpetual_premium_index_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_perpetual_premium_index_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_perpetual_premium_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_perpetual_premium_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_perpetual_premium_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_perpetual_premium_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Pgo : IDisposable
{
    private readonly WickraHandle _handle;

    public Pgo(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_pgo_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Pgo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pgo_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_pgo_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pgo_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pgo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pgo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pgo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pgo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PiercingDarkCloud : IDisposable
{
    private readonly WickraHandle _handle;

    public PiercingDarkCloud()
    {
        var ptr = NativeMethods.wickra_piercing_dark_cloud_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PiercingDarkCloud parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_piercing_dark_cloud_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_piercing_dark_cloud_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_piercing_dark_cloud_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_piercing_dark_cloud_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_piercing_dark_cloud_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_piercing_dark_cloud_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_piercing_dark_cloud_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Pin : IDisposable
{
    private readonly WickraHandle _handle;

    public Pin(int window)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        var ptr = NativeMethods.wickra_pin_new((nuint)window);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Pin parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pin_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_pin_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pin_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pin_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pin_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pin_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PivotReversal : IDisposable
{
    private readonly WickraHandle _handle;

    public PivotReversal(int left, int right)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(left);
        ArgumentOutOfRangeException.ThrowIfNegative(right);
        var ptr = NativeMethods.wickra_pivot_reversal_new((nuint)left, (nuint)right);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PivotReversal parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pivot_reversal_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_pivot_reversal_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pivot_reversal_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pivot_reversal_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pivot_reversal_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pivot_reversal_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pivot_reversal_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PlusDi : IDisposable
{
    private readonly WickraHandle _handle;

    public PlusDi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_plus_di_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PlusDi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_plus_di_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_plus_di_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_plus_di_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_plus_di_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_plus_di_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_plus_di_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_plus_di_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PlusDm : IDisposable
{
    private readonly WickraHandle _handle;

    public PlusDm(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_plus_dm_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PlusDm parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_plus_dm_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_plus_dm_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_plus_dm_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_plus_dm_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_plus_dm_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_plus_dm_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_plus_dm_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Pmo : IDisposable
{
    private readonly WickraHandle _handle;

    public Pmo(int smoothing1, int smoothing2)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(smoothing1);
        ArgumentOutOfRangeException.ThrowIfNegative(smoothing2);
        var ptr = NativeMethods.wickra_pmo_new((nuint)smoothing1, (nuint)smoothing2);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Pmo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pmo_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_pmo_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pmo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pmo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pmo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pmo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pmo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PointAndFigureBars : IDisposable
{
    private readonly WickraHandle _handle;

    public PointAndFigureBars(double boxSize, int reversal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(reversal);
        var ptr = NativeMethods.wickra_point_and_figure_bars_new(boxSize, (nuint)reversal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PointAndFigureBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_point_and_figure_bars_free);
    }

    public PnfColumn[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraPnfColumn> buffer = stackalloc WickraPnfColumn[cap];
        long count;
        unsafe
        {
            fixed (WickraPnfColumn* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_point_and_figure_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<PnfColumn>();
        }

        var result = new PnfColumn[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new PnfColumn(buffer[i].direction, buffer[i].high, buffer[i].low);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_point_and_figure_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_point_and_figure_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PolarizedFractalEfficiency : IDisposable
{
    private readonly WickraHandle _handle;

    public PolarizedFractalEfficiency(int period, int smoothing)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(smoothing);
        var ptr = NativeMethods.wickra_polarized_fractal_efficiency_new((nuint)period, (nuint)smoothing);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PolarizedFractalEfficiency parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_polarized_fractal_efficiency_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_polarized_fractal_efficiency_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_polarized_fractal_efficiency_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_polarized_fractal_efficiency_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_polarized_fractal_efficiency_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_polarized_fractal_efficiency_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_polarized_fractal_efficiency_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Ppo : IDisposable
{
    private readonly WickraHandle _handle;

    public Ppo(int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_ppo_new((nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Ppo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ppo_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ppo_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ppo_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ppo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ppo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ppo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ppo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class PpoHistogram : IDisposable
{
    private readonly WickraHandle _handle;

    public PpoHistogram(int fast, int slow, int signal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_ppo_histogram_new((nuint)fast, (nuint)slow, (nuint)signal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid PpoHistogram parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ppo_histogram_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ppo_histogram_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ppo_histogram_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ppo_histogram_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ppo_histogram_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ppo_histogram_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ppo_histogram_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ProfileShape : IDisposable
{
    private readonly WickraHandle _handle;

    public ProfileShape(int period, int bins)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(bins);
        var ptr = NativeMethods.wickra_profile_shape_new((nuint)period, (nuint)bins);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ProfileShape parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_profile_shape_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_profile_shape_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_profile_shape_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_profile_shape_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_profile_shape_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_profile_shape_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_profile_shape_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ProfitFactor : IDisposable
{
    private readonly WickraHandle _handle;

    public ProfitFactor(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_profit_factor_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ProfitFactor parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_profit_factor_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_profit_factor_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_profit_factor_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_profit_factor_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_profit_factor_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_profit_factor_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_profit_factor_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ProjectionBands : IDisposable
{
    private readonly WickraHandle _handle;

    public ProjectionBands(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_projection_bands_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ProjectionBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_projection_bands_free);
    }

    public ProjectionBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraProjectionBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_projection_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ProjectionBandsOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_projection_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_projection_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_projection_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_projection_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ProjectionOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public ProjectionOscillator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_projection_oscillator_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ProjectionOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_projection_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_projection_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_projection_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_projection_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_projection_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_projection_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_projection_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Psar : IDisposable
{
    private readonly WickraHandle _handle;

    public Psar(double afStart, double afStep, double afMax)
    {
        var ptr = NativeMethods.wickra_psar_new(afStart, afStep, afMax);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Psar parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_psar_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_psar_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_psar_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_psar_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_psar_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_psar_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_psar_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Pvi : IDisposable
{
    private readonly WickraHandle _handle;

    public Pvi()
    {
        var ptr = NativeMethods.wickra_pvi_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Pvi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_pvi_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_pvi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_pvi_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_pvi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_pvi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_pvi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_pvi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Qqe : IDisposable
{
    private readonly WickraHandle _handle;

    public Qqe(int rsiPeriod, int smoothing, double factor)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rsiPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(smoothing);
        var ptr = NativeMethods.wickra_qqe_new((nuint)rsiPeriod, (nuint)smoothing, factor);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Qqe parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_qqe_free);
    }

    public QqeOutput? Update(double @value)
    {
        WickraQqeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_qqe_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new QqeOutput(native.rsi_ma, native.trailing_line) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_qqe_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_qqe_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_qqe_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_qqe_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Qstick : IDisposable
{
    private readonly WickraHandle _handle;

    public Qstick(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_qstick_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Qstick parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_qstick_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_qstick_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_qstick_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_qstick_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_qstick_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_qstick_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_qstick_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class QuartileBands : IDisposable
{
    private readonly WickraHandle _handle;

    public QuartileBands(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_quartile_bands_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid QuartileBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_quartile_bands_free);
    }

    public QuartileBandsOutput? Update(double @value)
    {
        WickraQuartileBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_quartile_bands_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new QuartileBandsOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_quartile_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_quartile_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_quartile_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_quartile_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class QuotedSpread : IDisposable
{
    private readonly WickraHandle _handle;

    public QuotedSpread()
    {
        var ptr = NativeMethods.wickra_quoted_spread_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid QuotedSpread parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_quoted_spread_free);
    }

    public double Update(ReadOnlySpan<double> bidPrice, ReadOnlySpan<double> bidSize, ReadOnlySpan<double> askPrice, ReadOnlySpan<double> askSize)
    {
        if (bidSize.Length != bidPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (askSize.Length != askPrice.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* bidPricePtr = bidPrice)
            fixed (double* bidSizePtr = bidSize)
            fixed (double* askPricePtr = askPrice)
            fixed (double* askSizePtr = askSize)
            {
                result = NativeMethods.wickra_quoted_spread_update(_handle.DangerousGetHandle(), bidPricePtr, bidSizePtr, (nuint)bidPrice.Length, askPricePtr, askSizePtr, (nuint)askPrice.Length);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_quoted_spread_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_quoted_spread_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_quoted_spread_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_quoted_spread_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RSquared : IDisposable
{
    private readonly WickraHandle _handle;

    public RSquared(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_r_squared_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RSquared parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_r_squared_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_r_squared_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_r_squared_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_r_squared_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_r_squared_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_r_squared_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_r_squared_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RangeBars : IDisposable
{
    private readonly WickraHandle _handle;

    public RangeBars(double range)
    {
        var ptr = NativeMethods.wickra_range_bars_new(range);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RangeBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_range_bars_free);
    }

    public RangeBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraRangeBar> buffer = stackalloc WickraRangeBar[cap];
        long count;
        unsafe
        {
            fixed (WickraRangeBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_range_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<RangeBar>();
        }

        var result = new RangeBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new RangeBar(buffer[i].open, buffer[i].close, buffer[i].direction);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_range_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_range_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RealizedSpread : IDisposable
{
    private readonly WickraHandle _handle;

    public RealizedSpread(int horizon)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(horizon);
        var ptr = NativeMethods.wickra_realized_spread_new((nuint)horizon);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RealizedSpread parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_realized_spread_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp, double mid)
    {
        var result = NativeMethods.wickra_realized_spread_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp, mid);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_realized_spread_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_realized_spread_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_realized_spread_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_realized_spread_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RealizedVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public RealizedVolatility(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_realized_volatility_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RealizedVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_realized_volatility_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_realized_volatility_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_realized_volatility_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_realized_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_realized_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_realized_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_realized_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RecoveryFactor : IDisposable
{
    private readonly WickraHandle _handle;

    public RecoveryFactor()
    {
        var ptr = NativeMethods.wickra_recovery_factor_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RecoveryFactor parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_recovery_factor_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_recovery_factor_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_recovery_factor_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_recovery_factor_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_recovery_factor_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_recovery_factor_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_recovery_factor_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RectangleRange : IDisposable
{
    private readonly WickraHandle _handle;

    public RectangleRange()
    {
        var ptr = NativeMethods.wickra_rectangle_range_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RectangleRange parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rectangle_range_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_rectangle_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rectangle_range_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rectangle_range_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rectangle_range_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rectangle_range_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rectangle_range_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Reflex : IDisposable
{
    private readonly WickraHandle _handle;

    public Reflex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_reflex_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Reflex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_reflex_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_reflex_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_reflex_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_reflex_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_reflex_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_reflex_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_reflex_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RegimeLabel : IDisposable
{
    private readonly WickraHandle _handle;

    public RegimeLabel(int volPeriod, int lookback)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(volPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(lookback);
        var ptr = NativeMethods.wickra_regime_label_new((nuint)volPeriod, (nuint)lookback);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RegimeLabel parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_regime_label_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_regime_label_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_regime_label_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_regime_label_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_regime_label_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_regime_label_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_regime_label_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RelativeStrengthAB : IDisposable
{
    private readonly WickraHandle _handle;

    public RelativeStrengthAB(int maPeriod, int rsiPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(maPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(rsiPeriod);
        var ptr = NativeMethods.wickra_relative_strength_ab_new((nuint)maPeriod, (nuint)rsiPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RelativeStrengthAB parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_relative_strength_ab_free);
    }

    public RelativeStrengthOutput? Update(double x, double y)
    {
        WickraRelativeStrengthOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_relative_strength_ab_update(_handle.DangerousGetHandle(), x, y, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new RelativeStrengthOutput(native.ratio, native.ratio_ma, native.ratio_rsi) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_relative_strength_ab_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_relative_strength_ab_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_relative_strength_ab_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_relative_strength_ab_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RenkoBars : IDisposable
{
    private readonly WickraHandle _handle;

    public RenkoBars(double boxSize)
    {
        var ptr = NativeMethods.wickra_renko_bars_new(boxSize);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RenkoBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_renko_bars_free);
    }

    public RenkoBrick[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraRenkoBrick> buffer = stackalloc WickraRenkoBrick[cap];
        long count;
        unsafe
        {
            fixed (WickraRenkoBrick* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_renko_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<RenkoBrick>();
        }

        var result = new RenkoBrick[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new RenkoBrick(buffer[i].open, buffer[i].close, buffer[i].direction);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_renko_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_renko_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RenkoTrailingStop : IDisposable
{
    private readonly WickraHandle _handle;

    public RenkoTrailingStop(double blockSize)
    {
        var ptr = NativeMethods.wickra_renko_trailing_stop_new(blockSize);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RenkoTrailingStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_renko_trailing_stop_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_renko_trailing_stop_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_renko_trailing_stop_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_renko_trailing_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_renko_trailing_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_renko_trailing_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_renko_trailing_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Resampler : IDisposable
{
    private readonly WickraHandle _handle;

    public Resampler(long timeframe)
    {
        var ptr = NativeMethods.wickra_resampler_new(timeframe);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Resampler parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_resampler_free);
    }

    public Candle? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraCandle native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_resampler_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new Candle(native.open, native.high, native.low, native.close, native.volume, native.timestamp) : null;
    }

    /// <summary>Emit the final, still-open candle (null if none is pending).</summary>
    public Candle? Flush()
    {
        WickraCandle native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_resampler_flush(_handle.DangerousGetHandle(), &native);
        }
        GC.KeepAlive(_handle);
        return ok ? new Candle(native.open, native.high, native.low, native.close, native.volume, native.timestamp) : null;
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RickshawMan : IDisposable
{
    private readonly WickraHandle _handle;

    public RickshawMan()
    {
        var ptr = NativeMethods.wickra_rickshaw_man_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RickshawMan parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rickshaw_man_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_rickshaw_man_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rickshaw_man_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rickshaw_man_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rickshaw_man_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rickshaw_man_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rickshaw_man_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RisingThreeMethods : IDisposable
{
    private readonly WickraHandle _handle;

    public RisingThreeMethods()
    {
        var ptr = NativeMethods.wickra_rising_three_methods_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RisingThreeMethods parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rising_three_methods_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_rising_three_methods_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rising_three_methods_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rising_three_methods_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rising_three_methods_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rising_three_methods_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rising_three_methods_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rmi : IDisposable
{
    private readonly WickraHandle _handle;

    public Rmi(int period, int momentum)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(momentum);
        var ptr = NativeMethods.wickra_rmi_new((nuint)period, (nuint)momentum);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rmi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rmi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rmi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rmi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rmi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rmi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rmi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rmi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Roc : IDisposable
{
    private readonly WickraHandle _handle;

    public Roc(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_roc_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Roc parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_roc_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_roc_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_roc_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_roc_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_roc_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_roc_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_roc_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rocp : IDisposable
{
    private readonly WickraHandle _handle;

    public Rocp(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rocp_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rocp parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rocp_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rocp_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rocp_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rocp_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rocp_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rocp_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rocp_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rocr : IDisposable
{
    private readonly WickraHandle _handle;

    public Rocr(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rocr_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rocr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rocr_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rocr_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rocr_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rocr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rocr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rocr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rocr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rocr100 : IDisposable
{
    private readonly WickraHandle _handle;

    public Rocr100(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rocr100_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rocr100 parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rocr100_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rocr100_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rocr100_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rocr100_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rocr100_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rocr100_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rocr100_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RogersSatchellVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public RogersSatchellVolatility(int period, int tradingPeriods)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(tradingPeriods);
        var ptr = NativeMethods.wickra_rogers_satchell_volatility_new((nuint)period, (nuint)tradingPeriods);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RogersSatchellVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rogers_satchell_volatility_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_rogers_satchell_volatility_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rogers_satchell_volatility_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rogers_satchell_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rogers_satchell_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rogers_satchell_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rogers_satchell_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollMeasure : IDisposable
{
    private readonly WickraHandle _handle;

    public RollMeasure(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_roll_measure_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollMeasure parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_roll_measure_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_roll_measure_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_roll_measure_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_roll_measure_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_roll_measure_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_roll_measure_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollingCorrelation : IDisposable
{
    private readonly WickraHandle _handle;

    public RollingCorrelation(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rolling_correlation_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollingCorrelation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rolling_correlation_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_rolling_correlation_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rolling_correlation_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rolling_correlation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rolling_correlation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rolling_correlation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rolling_correlation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollingCovariance : IDisposable
{
    private readonly WickraHandle _handle;

    public RollingCovariance(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rolling_covariance_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollingCovariance parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rolling_covariance_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_rolling_covariance_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rolling_covariance_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rolling_covariance_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rolling_covariance_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rolling_covariance_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rolling_covariance_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollingIqr : IDisposable
{
    private readonly WickraHandle _handle;

    public RollingIqr(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rolling_iqr_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollingIqr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rolling_iqr_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rolling_iqr_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rolling_iqr_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rolling_iqr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rolling_iqr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rolling_iqr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rolling_iqr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollingMinMaxScaler : IDisposable
{
    private readonly WickraHandle _handle;

    public RollingMinMaxScaler(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rolling_min_max_scaler_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollingMinMaxScaler parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rolling_min_max_scaler_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rolling_min_max_scaler_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rolling_min_max_scaler_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rolling_min_max_scaler_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rolling_min_max_scaler_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rolling_min_max_scaler_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rolling_min_max_scaler_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollingPercentileRank : IDisposable
{
    private readonly WickraHandle _handle;

    public RollingPercentileRank(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rolling_percentile_rank_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollingPercentileRank parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rolling_percentile_rank_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rolling_percentile_rank_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rolling_percentile_rank_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rolling_percentile_rank_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rolling_percentile_rank_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rolling_percentile_rank_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rolling_percentile_rank_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollingQuantile : IDisposable
{
    private readonly WickraHandle _handle;

    public RollingQuantile(int period, double quantile)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rolling_quantile_new((nuint)period, quantile);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollingQuantile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rolling_quantile_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rolling_quantile_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rolling_quantile_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rolling_quantile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rolling_quantile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rolling_quantile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rolling_quantile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RollingVwap : IDisposable
{
    private readonly WickraHandle _handle;

    public RollingVwap(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rolling_vwap_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RollingVwap parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rolling_vwap_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_rolling_vwap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rolling_vwap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rolling_vwap_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rolling_vwap_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rolling_vwap_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rolling_vwap_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RoofingFilter : IDisposable
{
    private readonly WickraHandle _handle;

    public RoofingFilter(int lpPeriod, int hpPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(lpPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(hpPeriod);
        var ptr = NativeMethods.wickra_roofing_filter_new((nuint)lpPeriod, (nuint)hpPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RoofingFilter parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_roofing_filter_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_roofing_filter_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_roofing_filter_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_roofing_filter_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_roofing_filter_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_roofing_filter_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_roofing_filter_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rsi : IDisposable
{
    private readonly WickraHandle _handle;

    public Rsi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rsi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rsx : IDisposable
{
    private readonly WickraHandle _handle;

    public Rsx(int length)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(length);
        var ptr = NativeMethods.wickra_rsx_new((nuint)length);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rsx parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rsx_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rsx_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rsx_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rsx_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rsx_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rsx_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rsx_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RunBars : IDisposable
{
    private readonly WickraHandle _handle;

    public RunBars(int runLength)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(runLength);
        var ptr = NativeMethods.wickra_run_bars_new((nuint)runLength);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RunBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_run_bars_free);
    }

    public RunBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraRunBar> buffer = stackalloc WickraRunBar[cap];
        long count;
        unsafe
        {
            fixed (WickraRunBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_run_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<RunBar>();
        }

        var result = new RunBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new RunBar(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].length, buffer[i].direction);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_run_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_run_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rvi : IDisposable
{
    private readonly WickraHandle _handle;

    public Rvi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rvi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rvi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rvi_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_rvi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rvi_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rvi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rvi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rvi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rvi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class RviVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public RviVolatility(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rvi_volatility_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid RviVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rvi_volatility_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_rvi_volatility_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_rvi_volatility_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rvi_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rvi_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rvi_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rvi_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Rwi : IDisposable
{
    private readonly WickraHandle _handle;

    public Rwi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_rwi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Rwi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_rwi_free);
    }

    public RwiOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraRwiOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_rwi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new RwiOutput(native.high, native.low) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_rwi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_rwi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_rwi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_rwi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SampleEntropy : IDisposable
{
    private readonly WickraHandle _handle;

    public SampleEntropy(int period, int m, double rFactor)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(m);
        var ptr = NativeMethods.wickra_sample_entropy_new((nuint)period, (nuint)m, rFactor);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SampleEntropy parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sample_entropy_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_sample_entropy_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sample_entropy_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sample_entropy_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sample_entropy_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sample_entropy_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sample_entropy_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SarExt : IDisposable
{
    private readonly WickraHandle _handle;

    public SarExt(double startValue, double offsetOnReverse, double accelInitLong, double accelLong, double accelMaxLong, double accelInitShort, double accelShort, double accelMaxShort)
    {
        var ptr = NativeMethods.wickra_sar_ext_new(startValue, offsetOnReverse, accelInitLong, accelLong, accelMaxLong, accelInitShort, accelShort, accelMaxShort);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SarExt parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sar_ext_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_sar_ext_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sar_ext_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sar_ext_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sar_ext_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sar_ext_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sar_ext_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SeasonalZScore : IDisposable
{
    private readonly WickraHandle _handle;

    public SeasonalZScore(int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_seasonal_z_score_new(utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SeasonalZScore parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_seasonal_z_score_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_seasonal_z_score_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_seasonal_z_score_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_seasonal_z_score_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_seasonal_z_score_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_seasonal_z_score_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_seasonal_z_score_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SeparatingLines : IDisposable
{
    private readonly WickraHandle _handle;

    public SeparatingLines()
    {
        var ptr = NativeMethods.wickra_separating_lines_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SeparatingLines parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_separating_lines_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_separating_lines_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_separating_lines_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_separating_lines_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_separating_lines_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_separating_lines_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_separating_lines_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SessionHighLow : IDisposable
{
    private readonly WickraHandle _handle;

    public SessionHighLow(int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_session_high_low_new(utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SessionHighLow parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_session_high_low_free);
    }

    public SessionHighLowOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraSessionHighLowOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_session_high_low_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new SessionHighLowOutput(native.high, native.low) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_session_high_low_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_session_high_low_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_session_high_low_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_session_high_low_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SessionRange : IDisposable
{
    private readonly WickraHandle _handle;

    public SessionRange(int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_session_range_new(utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SessionRange parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_session_range_free);
    }

    public SessionRangeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraSessionRangeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_session_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new SessionRangeOutput(native.asia, native.eu, native.us) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_session_range_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_session_range_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_session_range_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_session_range_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SessionVwap : IDisposable
{
    private readonly WickraHandle _handle;

    public SessionVwap(int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_session_vwap_new(utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SessionVwap parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_session_vwap_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_session_vwap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_session_vwap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_session_vwap_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_session_vwap_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_session_vwap_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_session_vwap_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ShannonEntropy : IDisposable
{
    private readonly WickraHandle _handle;

    public ShannonEntropy(int period, int bins)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(bins);
        var ptr = NativeMethods.wickra_shannon_entropy_new((nuint)period, (nuint)bins);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ShannonEntropy parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_shannon_entropy_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_shannon_entropy_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_shannon_entropy_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_shannon_entropy_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_shannon_entropy_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_shannon_entropy_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_shannon_entropy_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Shark : IDisposable
{
    private readonly WickraHandle _handle;

    public Shark()
    {
        var ptr = NativeMethods.wickra_shark_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Shark parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_shark_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_shark_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_shark_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_shark_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_shark_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_shark_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_shark_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SharpeRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public SharpeRatio(int period, double riskFree)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_sharpe_ratio_new((nuint)period, riskFree);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SharpeRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sharpe_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_sharpe_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sharpe_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sharpe_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sharpe_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sharpe_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sharpe_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ShootingStar : IDisposable
{
    private readonly WickraHandle _handle;

    public ShootingStar()
    {
        var ptr = NativeMethods.wickra_shooting_star_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ShootingStar parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_shooting_star_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_shooting_star_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_shooting_star_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_shooting_star_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_shooting_star_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_shooting_star_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_shooting_star_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ShortLine : IDisposable
{
    private readonly WickraHandle _handle;

    public ShortLine()
    {
        var ptr = NativeMethods.wickra_short_line_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ShortLine parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_short_line_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_short_line_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_short_line_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_short_line_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_short_line_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_short_line_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_short_line_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SignedVolume : IDisposable
{
    private readonly WickraHandle _handle;

    public SignedVolume()
    {
        var ptr = NativeMethods.wickra_signed_volume_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SignedVolume parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_signed_volume_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_signed_volume_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_signed_volume_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_signed_volume_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_signed_volume_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_signed_volume_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SineWave : IDisposable
{
    private readonly WickraHandle _handle;

    public SineWave()
    {
        var ptr = NativeMethods.wickra_sine_wave_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SineWave parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sine_wave_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_sine_wave_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sine_wave_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sine_wave_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sine_wave_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sine_wave_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sine_wave_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SineWeightedMa : IDisposable
{
    private readonly WickraHandle _handle;

    public SineWeightedMa(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_sine_weighted_ma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SineWeightedMa parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sine_weighted_ma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_sine_weighted_ma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sine_weighted_ma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sine_weighted_ma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sine_weighted_ma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sine_weighted_ma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sine_weighted_ma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SinglePrints : IDisposable
{
    private readonly WickraHandle _handle;

    public SinglePrints(int period, int bins)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(bins);
        var ptr = NativeMethods.wickra_single_prints_new((nuint)period, (nuint)bins);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SinglePrints parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_single_prints_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_single_prints_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_single_prints_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_single_prints_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_single_prints_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_single_prints_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_single_prints_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Skewness : IDisposable
{
    private readonly WickraHandle _handle;

    public Skewness(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_skewness_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Skewness parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_skewness_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_skewness_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_skewness_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_skewness_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_skewness_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_skewness_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_skewness_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Sma : IDisposable
{
    private readonly WickraHandle _handle;

    public Sma(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_sma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Sma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_sma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Smi : IDisposable
{
    private readonly WickraHandle _handle;

    public Smi(int period, int dPeriod, int d2Period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(dPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(d2Period);
        var ptr = NativeMethods.wickra_smi_new((nuint)period, (nuint)dPeriod, (nuint)d2Period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Smi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_smi_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_smi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_smi_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_smi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_smi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_smi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_smi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Smma : IDisposable
{
    private readonly WickraHandle _handle;

    public Smma(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_smma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Smma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_smma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_smma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_smma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_smma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_smma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_smma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_smma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SmoothedHeikinAshi : IDisposable
{
    private readonly WickraHandle _handle;

    public SmoothedHeikinAshi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_smoothed_heikin_ashi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SmoothedHeikinAshi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_smoothed_heikin_ashi_free);
    }

    public SmoothedHeikinAshiOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraSmoothedHeikinAshiOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_smoothed_heikin_ashi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new SmoothedHeikinAshiOutput(native.open, native.high, native.low, native.close) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_smoothed_heikin_ashi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_smoothed_heikin_ashi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_smoothed_heikin_ashi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_smoothed_heikin_ashi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SortinoRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public SortinoRatio(int period, double mar)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_sortino_ratio_new((nuint)period, mar);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SortinoRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sortino_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_sortino_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sortino_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sortino_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sortino_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sortino_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sortino_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SpearmanCorrelation : IDisposable
{
    private readonly WickraHandle _handle;

    public SpearmanCorrelation(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_spearman_correlation_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SpearmanCorrelation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_spearman_correlation_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_spearman_correlation_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_spearman_correlation_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_spearman_correlation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_spearman_correlation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_spearman_correlation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_spearman_correlation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SpinningTop : IDisposable
{
    private readonly WickraHandle _handle;

    public SpinningTop()
    {
        var ptr = NativeMethods.wickra_spinning_top_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SpinningTop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_spinning_top_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_spinning_top_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_spinning_top_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_spinning_top_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_spinning_top_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_spinning_top_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_spinning_top_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SpreadAr1Coefficient : IDisposable
{
    private readonly WickraHandle _handle;

    public SpreadAr1Coefficient(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_spread_ar1_coefficient_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SpreadAr1Coefficient parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_spread_ar1_coefficient_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_spread_ar1_coefficient_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_spread_ar1_coefficient_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_spread_ar1_coefficient_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_spread_ar1_coefficient_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_spread_ar1_coefficient_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_spread_ar1_coefficient_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SpreadBollingerBands : IDisposable
{
    private readonly WickraHandle _handle;

    public SpreadBollingerBands(int period, double numStd)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_spread_bollinger_bands_new((nuint)period, numStd);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SpreadBollingerBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_spread_bollinger_bands_free);
    }

    public SpreadBollingerBandsOutput? Update(double x, double y)
    {
        WickraSpreadBollingerBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_spread_bollinger_bands_update(_handle.DangerousGetHandle(), x, y, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new SpreadBollingerBandsOutput(native.middle, native.upper, native.lower, native.percent_b) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_spread_bollinger_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_spread_bollinger_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_spread_bollinger_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_spread_bollinger_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SpreadHurst : IDisposable
{
    private readonly WickraHandle _handle;

    public SpreadHurst(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_spread_hurst_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SpreadHurst parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_spread_hurst_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_spread_hurst_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_spread_hurst_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_spread_hurst_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_spread_hurst_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_spread_hurst_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_spread_hurst_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StalledPattern : IDisposable
{
    private readonly WickraHandle _handle;

    public StalledPattern()
    {
        var ptr = NativeMethods.wickra_stalled_pattern_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StalledPattern parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_stalled_pattern_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_stalled_pattern_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_stalled_pattern_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_stalled_pattern_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_stalled_pattern_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_stalled_pattern_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_stalled_pattern_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StandardError : IDisposable
{
    private readonly WickraHandle _handle;

    public StandardError(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_standard_error_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StandardError parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_standard_error_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_standard_error_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_standard_error_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_standard_error_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_standard_error_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_standard_error_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_standard_error_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StandardErrorBands : IDisposable
{
    private readonly WickraHandle _handle;

    public StandardErrorBands(int period, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_standard_error_bands_new((nuint)period, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StandardErrorBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_standard_error_bands_free);
    }

    public StandardErrorBandsOutput? Update(double @value)
    {
        WickraStandardErrorBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_standard_error_bands_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new StandardErrorBandsOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_standard_error_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_standard_error_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_standard_error_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_standard_error_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StarcBands : IDisposable
{
    private readonly WickraHandle _handle;

    public StarcBands(int smaPeriod, int atrPeriod, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(smaPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        var ptr = NativeMethods.wickra_starc_bands_new((nuint)smaPeriod, (nuint)atrPeriod, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StarcBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_starc_bands_free);
    }

    public StarcBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraStarcBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_starc_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new StarcBandsOutput(native.upper, native.middle, native.lower) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_starc_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_starc_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_starc_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_starc_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Stc : IDisposable
{
    private readonly WickraHandle _handle;

    public Stc(int fast, int slow, int schaffPeriod, double factor)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(schaffPeriod);
        var ptr = NativeMethods.wickra_stc_new((nuint)fast, (nuint)slow, (nuint)schaffPeriod, factor);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Stc parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_stc_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_stc_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_stc_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_stc_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_stc_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_stc_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_stc_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StdDev : IDisposable
{
    private readonly WickraHandle _handle;

    public StdDev(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_std_dev_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StdDev parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_std_dev_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_std_dev_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_std_dev_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_std_dev_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_std_dev_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_std_dev_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_std_dev_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StepTrailingStop : IDisposable
{
    private readonly WickraHandle _handle;

    public StepTrailingStop(double stepSize)
    {
        var ptr = NativeMethods.wickra_step_trailing_stop_new(stepSize);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StepTrailingStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_step_trailing_stop_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_step_trailing_stop_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_step_trailing_stop_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_step_trailing_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_step_trailing_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_step_trailing_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_step_trailing_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SterlingRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public SterlingRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_sterling_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SterlingRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_sterling_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_sterling_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_sterling_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_sterling_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_sterling_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_sterling_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_sterling_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StickSandwich : IDisposable
{
    private readonly WickraHandle _handle;

    public StickSandwich()
    {
        var ptr = NativeMethods.wickra_stick_sandwich_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StickSandwich parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_stick_sandwich_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_stick_sandwich_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_stick_sandwich_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_stick_sandwich_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_stick_sandwich_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_stick_sandwich_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_stick_sandwich_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StochRsi : IDisposable
{
    private readonly WickraHandle _handle;

    public StochRsi(int rsiPeriod, int stochPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rsiPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(stochPeriod);
        var ptr = NativeMethods.wickra_stoch_rsi_new((nuint)rsiPeriod, (nuint)stochPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StochRsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_stoch_rsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_stoch_rsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_stoch_rsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_stoch_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_stoch_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_stoch_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_stoch_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Stochastic : IDisposable
{
    private readonly WickraHandle _handle;

    public Stochastic(int kPeriod, int dPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(kPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(dPeriod);
        var ptr = NativeMethods.wickra_stochastic_new((nuint)kPeriod, (nuint)dPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Stochastic parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_stochastic_free);
    }

    public StochasticOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraStochasticOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_stochastic_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new StochasticOutput(native.k, native.d) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_stochastic_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_stochastic_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_stochastic_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_stochastic_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class StochasticCci : IDisposable
{
    private readonly WickraHandle _handle;

    public StochasticCci(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_stochastic_cci_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid StochasticCci parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_stochastic_cci_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_stochastic_cci_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_stochastic_cci_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_stochastic_cci_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_stochastic_cci_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_stochastic_cci_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_stochastic_cci_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SuperSmoother : IDisposable
{
    private readonly WickraHandle _handle;

    public SuperSmoother(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_super_smoother_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SuperSmoother parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_super_smoother_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_super_smoother_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_super_smoother_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_super_smoother_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_super_smoother_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_super_smoother_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_super_smoother_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class SuperTrend : IDisposable
{
    private readonly WickraHandle _handle;

    public SuperTrend(int atrPeriod, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        var ptr = NativeMethods.wickra_super_trend_new((nuint)atrPeriod, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid SuperTrend parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_super_trend_free);
    }

    public SuperTrendOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraSuperTrendOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_super_trend_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new SuperTrendOutput(native.@value, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_super_trend_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_super_trend_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_super_trend_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_super_trend_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class T3 : IDisposable
{
    private readonly WickraHandle _handle;

    public T3(int period, double v)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_t3_new((nuint)period, v);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid T3 parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_t3_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_t3_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_t3_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_t3_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_t3_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_t3_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_t3_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TailRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public TailRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_tail_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TailRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tail_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_tail_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tail_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tail_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tail_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tail_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tail_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TakerBuySellRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public TakerBuySellRatio()
    {
        var ptr = NativeMethods.wickra_taker_buy_sell_ratio_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TakerBuySellRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_taker_buy_sell_ratio_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_taker_buy_sell_ratio_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_taker_buy_sell_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_taker_buy_sell_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_taker_buy_sell_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_taker_buy_sell_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Takuri : IDisposable
{
    private readonly WickraHandle _handle;

    public Takuri()
    {
        var ptr = NativeMethods.wickra_takuri_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Takuri parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_takuri_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_takuri_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_takuri_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_takuri_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_takuri_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_takuri_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_takuri_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TasukiGap : IDisposable
{
    private readonly WickraHandle _handle;

    public TasukiGap()
    {
        var ptr = NativeMethods.wickra_tasuki_gap_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TasukiGap parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tasuki_gap_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_tasuki_gap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tasuki_gap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tasuki_gap_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tasuki_gap_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tasuki_gap_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tasuki_gap_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdCamouflage : IDisposable
{
    private readonly WickraHandle _handle;

    public TdCamouflage()
    {
        var ptr = NativeMethods.wickra_td_camouflage_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdCamouflage parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_camouflage_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_camouflage_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_camouflage_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_camouflage_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_camouflage_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_camouflage_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_camouflage_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdClop : IDisposable
{
    private readonly WickraHandle _handle;

    public TdClop()
    {
        var ptr = NativeMethods.wickra_td_clop_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdClop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_clop_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_clop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_clop_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_clop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_clop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_clop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_clop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdClopwin : IDisposable
{
    private readonly WickraHandle _handle;

    public TdClopwin()
    {
        var ptr = NativeMethods.wickra_td_clopwin_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdClopwin parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_clopwin_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_clopwin_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_clopwin_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_clopwin_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_clopwin_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_clopwin_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_clopwin_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdCombo : IDisposable
{
    private readonly WickraHandle _handle;

    public TdCombo(int setupLookback, int setupTarget, int countdownLookback, int countdownTarget)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(setupLookback);
        ArgumentOutOfRangeException.ThrowIfNegative(setupTarget);
        ArgumentOutOfRangeException.ThrowIfNegative(countdownLookback);
        ArgumentOutOfRangeException.ThrowIfNegative(countdownTarget);
        var ptr = NativeMethods.wickra_td_combo_new((nuint)setupLookback, (nuint)setupTarget, (nuint)countdownLookback, (nuint)countdownTarget);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdCombo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_combo_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_combo_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_combo_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_combo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_combo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_combo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_combo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdCountdown : IDisposable
{
    private readonly WickraHandle _handle;

    public TdCountdown(int setupLookback, int setupTarget, int countdownLookback, int countdownTarget)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(setupLookback);
        ArgumentOutOfRangeException.ThrowIfNegative(setupTarget);
        ArgumentOutOfRangeException.ThrowIfNegative(countdownLookback);
        ArgumentOutOfRangeException.ThrowIfNegative(countdownTarget);
        var ptr = NativeMethods.wickra_td_countdown_new((nuint)setupLookback, (nuint)setupTarget, (nuint)countdownLookback, (nuint)countdownTarget);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdCountdown parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_countdown_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_countdown_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_countdown_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_countdown_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_countdown_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_countdown_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_countdown_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdDWave : IDisposable
{
    private readonly WickraHandle _handle;

    public TdDWave(int strength)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(strength);
        var ptr = NativeMethods.wickra_td_d_wave_new((nuint)strength);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdDWave parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_d_wave_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_d_wave_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_d_wave_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_d_wave_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_d_wave_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_d_wave_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_d_wave_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdDeMarker : IDisposable
{
    private readonly WickraHandle _handle;

    public TdDeMarker(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_td_de_marker_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdDeMarker parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_de_marker_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_de_marker_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_de_marker_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_de_marker_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_de_marker_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_de_marker_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_de_marker_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdDifferential : IDisposable
{
    private readonly WickraHandle _handle;

    public TdDifferential()
    {
        var ptr = NativeMethods.wickra_td_differential_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdDifferential parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_differential_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_differential_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_differential_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_differential_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_differential_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_differential_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_differential_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdLines : IDisposable
{
    private readonly WickraHandle _handle;

    public TdLines(int lookback, int target)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(lookback);
        ArgumentOutOfRangeException.ThrowIfNegative(target);
        var ptr = NativeMethods.wickra_td_lines_new((nuint)lookback, (nuint)target);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdLines parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_lines_free);
    }

    public TdLinesOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraTdLinesOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_td_lines_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new TdLinesOutput(native.resistance, native.support) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_lines_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_lines_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_lines_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_lines_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdMovingAverage : IDisposable
{
    private readonly WickraHandle _handle;

    public TdMovingAverage(int periodSt1, int periodSt2)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(periodSt1);
        ArgumentOutOfRangeException.ThrowIfNegative(periodSt2);
        var ptr = NativeMethods.wickra_td_moving_average_new((nuint)periodSt1, (nuint)periodSt2);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdMovingAverage parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_moving_average_free);
    }

    public TdMovingAverageOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraTdMovingAverageOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_td_moving_average_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new TdMovingAverageOutput(native.st1, native.st2) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_moving_average_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_moving_average_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_moving_average_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_moving_average_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdOpen : IDisposable
{
    private readonly WickraHandle _handle;

    public TdOpen()
    {
        var ptr = NativeMethods.wickra_td_open_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdOpen parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_open_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_open_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_open_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_open_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_open_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_open_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_open_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdPressure : IDisposable
{
    private readonly WickraHandle _handle;

    public TdPressure(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_td_pressure_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdPressure parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_pressure_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_pressure_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_pressure_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_pressure_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_pressure_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_pressure_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_pressure_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdPropulsion : IDisposable
{
    private readonly WickraHandle _handle;

    public TdPropulsion()
    {
        var ptr = NativeMethods.wickra_td_propulsion_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdPropulsion parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_propulsion_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_propulsion_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_propulsion_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_propulsion_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_propulsion_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_propulsion_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_propulsion_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdRangeProjection : IDisposable
{
    private readonly WickraHandle _handle;

    public TdRangeProjection()
    {
        var ptr = NativeMethods.wickra_td_range_projection_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdRangeProjection parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_range_projection_free);
    }

    public TdRangeProjectionOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraTdRangeProjectionOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_td_range_projection_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new TdRangeProjectionOutput(native.high, native.low) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_range_projection_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_range_projection_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_range_projection_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_range_projection_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdRei : IDisposable
{
    private readonly WickraHandle _handle;

    public TdRei(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_td_rei_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdRei parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_rei_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_rei_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_rei_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_rei_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_rei_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_rei_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_rei_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdRiskLevel : IDisposable
{
    private readonly WickraHandle _handle;

    public TdRiskLevel(int lookback, int target)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(lookback);
        ArgumentOutOfRangeException.ThrowIfNegative(target);
        var ptr = NativeMethods.wickra_td_risk_level_new((nuint)lookback, (nuint)target);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdRiskLevel parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_risk_level_free);
    }

    public TdRiskLevelOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraTdRiskLevelOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_td_risk_level_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new TdRiskLevelOutput(native.buy_risk, native.sell_risk) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_risk_level_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_risk_level_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_risk_level_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_risk_level_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdSequential : IDisposable
{
    private readonly WickraHandle _handle;

    public TdSequential(int setupLookback, int setupTarget, int countdownLookback, int countdownTarget)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(setupLookback);
        ArgumentOutOfRangeException.ThrowIfNegative(setupTarget);
        ArgumentOutOfRangeException.ThrowIfNegative(countdownLookback);
        ArgumentOutOfRangeException.ThrowIfNegative(countdownTarget);
        var ptr = NativeMethods.wickra_td_sequential_new((nuint)setupLookback, (nuint)setupTarget, (nuint)countdownLookback, (nuint)countdownTarget);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdSequential parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_sequential_free);
    }

    public TdSequentialOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraTdSequentialOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_td_sequential_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new TdSequentialOutput(native.setup, native.countdown, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_sequential_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_sequential_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_sequential_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_sequential_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdSetup : IDisposable
{
    private readonly WickraHandle _handle;

    public TdSetup(int lookback, int target)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(lookback);
        ArgumentOutOfRangeException.ThrowIfNegative(target);
        var ptr = NativeMethods.wickra_td_setup_new((nuint)lookback, (nuint)target);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdSetup parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_setup_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_setup_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_setup_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_setup_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_setup_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_setup_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_setup_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TdTrap : IDisposable
{
    private readonly WickraHandle _handle;

    public TdTrap()
    {
        var ptr = NativeMethods.wickra_td_trap_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TdTrap parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_td_trap_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_td_trap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_td_trap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_td_trap_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_td_trap_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_td_trap_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_td_trap_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Tema : IDisposable
{
    private readonly WickraHandle _handle;

    public Tema(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_tema_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Tema parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tema_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_tema_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tema_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tema_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tema_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tema_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tema_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TermStructureBasis : IDisposable
{
    private readonly WickraHandle _handle;

    public TermStructureBasis()
    {
        var ptr = NativeMethods.wickra_term_structure_basis_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TermStructureBasis parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_term_structure_basis_free);
    }

    public double Update(double fundingRate, double markPrice, double indexPrice, double futuresPrice, double openInterest, double longSize, double shortSize, double takerBuyVolume, double takerSellVolume, double longLiquidation, double shortLiquidation, long timestamp)
    {
        var result = NativeMethods.wickra_term_structure_basis_update(_handle.DangerousGetHandle(), fundingRate, markPrice, indexPrice, futuresPrice, openInterest, longSize, shortSize, takerBuyVolume, takerSellVolume, longLiquidation, shortLiquidation, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_term_structure_basis_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_term_structure_basis_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_term_structure_basis_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_term_structure_basis_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeDrives : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeDrives()
    {
        var ptr = NativeMethods.wickra_three_drives_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeDrives parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_drives_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_three_drives_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_three_drives_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_three_drives_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_three_drives_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_drives_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_drives_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeInside : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeInside()
    {
        var ptr = NativeMethods.wickra_three_inside_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeInside parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_inside_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_three_inside_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_three_inside_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_three_inside_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_three_inside_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_inside_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_inside_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeLineBreak : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeLineBreak(int lines)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(lines);
        var ptr = NativeMethods.wickra_three_line_break_new((nuint)lines);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeLineBreak parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_line_break_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_three_line_break_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_three_line_break_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_three_line_break_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_three_line_break_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_line_break_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_line_break_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeLineBreakBars : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeLineBreakBars(int lines)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(lines);
        var ptr = NativeMethods.wickra_three_line_break_bars_new((nuint)lines);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeLineBreakBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_line_break_bars_free);
    }

    public LineBreakBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraLineBreakBar> buffer = stackalloc WickraLineBreakBar[cap];
        long count;
        unsafe
        {
            fixed (WickraLineBreakBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_three_line_break_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<LineBreakBar>();
        }

        var result = new LineBreakBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new LineBreakBar(buffer[i].open, buffer[i].close, buffer[i].direction);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_line_break_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_line_break_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeLineStrike : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeLineStrike()
    {
        var ptr = NativeMethods.wickra_three_line_strike_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeLineStrike parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_line_strike_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_three_line_strike_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_three_line_strike_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_three_line_strike_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_three_line_strike_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_line_strike_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_line_strike_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeOutside : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeOutside()
    {
        var ptr = NativeMethods.wickra_three_outside_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeOutside parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_outside_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_three_outside_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_three_outside_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_three_outside_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_three_outside_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_outside_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_outside_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeSoldiersOrCrows : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeSoldiersOrCrows()
    {
        var ptr = NativeMethods.wickra_three_soldiers_or_crows_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeSoldiersOrCrows parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_soldiers_or_crows_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_three_soldiers_or_crows_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_three_soldiers_or_crows_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_three_soldiers_or_crows_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_three_soldiers_or_crows_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_soldiers_or_crows_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_soldiers_or_crows_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ThreeStarsInSouth : IDisposable
{
    private readonly WickraHandle _handle;

    public ThreeStarsInSouth()
    {
        var ptr = NativeMethods.wickra_three_stars_in_south_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ThreeStarsInSouth parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_three_stars_in_south_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_three_stars_in_south_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_three_stars_in_south_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_three_stars_in_south_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_three_stars_in_south_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_three_stars_in_south_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_three_stars_in_south_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Thrusting : IDisposable
{
    private readonly WickraHandle _handle;

    public Thrusting()
    {
        var ptr = NativeMethods.wickra_thrusting_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Thrusting parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_thrusting_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_thrusting_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_thrusting_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_thrusting_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_thrusting_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_thrusting_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_thrusting_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TickAggregator : IDisposable
{
    private readonly WickraHandle _handle;

    public TickAggregator(long bucket, bool gapFill)
    {
        var ptr = NativeMethods.wickra_tick_aggregator_new(bucket, gapFill);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TickAggregator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tick_aggregator_free);
    }


    /// <summary>Feed one trade tick; returns the candles it closed.</summary>
    public Candle[] Push(double price, double size, long timestamp)
    {
        var count = (long)NativeMethods.wickra_tick_aggregator_push(_handle.DangerousGetHandle(), price, size, timestamp);
        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<Candle>();
        }
        var buffer = new WickraCandle[count];
        unsafe
        {
            fixed (WickraCandle* ptr = buffer)
            {
                NativeMethods.wickra_tick_aggregator_drain(_handle.DangerousGetHandle(), ptr, (nuint)count);
            }
        }
        GC.KeepAlive(_handle);
        var result = new Candle[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new Candle(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].volume, buffer[i].timestamp);
        }
        return result;
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TickBars : IDisposable
{
    private readonly WickraHandle _handle;

    public TickBars(int ticks)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(ticks);
        var ptr = NativeMethods.wickra_tick_bars_new((nuint)ticks);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TickBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tick_bars_free);
    }

    public TickBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraTickBar> buffer = stackalloc WickraTickBar[cap];
        long count;
        unsafe
        {
            fixed (WickraTickBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_tick_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<TickBar>();
        }

        var result = new TickBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new TickBar(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].volume);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tick_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tick_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TickIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public TickIndex()
    {
        var ptr = NativeMethods.wickra_tick_index_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TickIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tick_index_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_tick_index_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tick_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tick_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tick_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tick_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Tii : IDisposable
{
    private readonly WickraHandle _handle;

    public Tii(int smaPeriod, int devPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(smaPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(devPeriod);
        var ptr = NativeMethods.wickra_tii_new((nuint)smaPeriod, (nuint)devPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Tii parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tii_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_tii_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tii_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tii_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tii_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tii_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tii_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TimeBasedStop : IDisposable
{
    private readonly WickraHandle _handle;

    public TimeBasedStop(int maxBars)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(maxBars);
        var ptr = NativeMethods.wickra_time_based_stop_new((nuint)maxBars);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TimeBasedStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_time_based_stop_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_time_based_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_time_based_stop_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_time_based_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_time_based_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_time_based_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_time_based_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TimeOfDayReturnProfile : IDisposable
{
    private readonly WickraHandle _handle;
    private readonly int _valuesCapacity;

    public TimeOfDayReturnProfile(int buckets, int utcOffsetMinutes)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(buckets);
        var ptr = NativeMethods.wickra_time_of_day_return_profile_new((nuint)buckets, utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TimeOfDayReturnProfile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_time_of_day_return_profile_free);
        _valuesCapacity = buckets;
    }

    public double[]? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var values = new double[_valuesCapacity];
        long length;
        unsafe
        {
            fixed (double* valuesPtr = values)
            {
                length = (long)NativeMethods.wickra_time_of_day_return_profile_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, valuesPtr, (nuint)_valuesCapacity);
            }
        }

        GC.KeepAlive(_handle);
        if (length < 0)
        {
            return null;
        }

        if (length < values.Length)
        {
            Array.Resize(ref values, (int)length);
        }

        return values;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_time_of_day_return_profile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_time_of_day_return_profile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_time_of_day_return_profile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_time_of_day_return_profile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TowerTopBottom : IDisposable
{
    private readonly WickraHandle _handle;

    public TowerTopBottom()
    {
        var ptr = NativeMethods.wickra_tower_top_bottom_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TowerTopBottom parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tower_top_bottom_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_tower_top_bottom_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tower_top_bottom_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tower_top_bottom_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tower_top_bottom_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tower_top_bottom_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tower_top_bottom_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TpoProfile : IDisposable
{
    private readonly WickraHandle _handle;
    private readonly int _valuesCapacity;

    public TpoProfile(int period, int binCount)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(binCount);
        var ptr = NativeMethods.wickra_tpo_profile_new((nuint)period, (nuint)binCount);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TpoProfile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tpo_profile_free);
        _valuesCapacity = binCount;
    }

    public TpoProfileOutputScalars? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var values = new double[_valuesCapacity];
        WickraTpoProfileOutputScalars scalars;
        long length;
        unsafe
        {
            fixed (double* valuesPtr = values)
            {
                length = (long)NativeMethods.wickra_tpo_profile_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &scalars, valuesPtr, (nuint)_valuesCapacity);
            }
        }

        GC.KeepAlive(_handle);
        if (length < 0)
        {
            return null;
        }

        if (length < values.Length)
        {
            Array.Resize(ref values, (int)length);
        }

        return new TpoProfileOutputScalars(scalars.price_low, scalars.price_high, values);
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tpo_profile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tpo_profile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tpo_profile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tpo_profile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TradeImbalance : IDisposable
{
    private readonly WickraHandle _handle;

    public TradeImbalance(int window)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        var ptr = NativeMethods.wickra_trade_imbalance_new((nuint)window);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TradeImbalance parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trade_imbalance_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_trade_imbalance_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trade_imbalance_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trade_imbalance_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trade_imbalance_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trade_imbalance_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TradeSignAutocorrelation : IDisposable
{
    private readonly WickraHandle _handle;

    public TradeSignAutocorrelation(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_trade_sign_autocorrelation_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TradeSignAutocorrelation parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trade_sign_autocorrelation_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_trade_sign_autocorrelation_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trade_sign_autocorrelation_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trade_sign_autocorrelation_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trade_sign_autocorrelation_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trade_sign_autocorrelation_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TradeVolumeIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public TradeVolumeIndex(double minTick)
    {
        var ptr = NativeMethods.wickra_trade_volume_index_new(minTick);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TradeVolumeIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trade_volume_index_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_trade_volume_index_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_trade_volume_index_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trade_volume_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trade_volume_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trade_volume_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trade_volume_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TrendLabel : IDisposable
{
    private readonly WickraHandle _handle;

    public TrendLabel(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_trend_label_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TrendLabel parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trend_label_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_trend_label_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_trend_label_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trend_label_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trend_label_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trend_label_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trend_label_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TrendStrengthIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public TrendStrengthIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_trend_strength_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TrendStrengthIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trend_strength_index_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_trend_strength_index_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_trend_strength_index_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trend_strength_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trend_strength_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trend_strength_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trend_strength_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Trendflex : IDisposable
{
    private readonly WickraHandle _handle;

    public Trendflex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_trendflex_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Trendflex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trendflex_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_trendflex_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_trendflex_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trendflex_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trendflex_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trendflex_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trendflex_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TreynorRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public TreynorRatio(int period, double riskFree)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_treynor_ratio_new((nuint)period, riskFree);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TreynorRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_treynor_ratio_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_treynor_ratio_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_treynor_ratio_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_treynor_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_treynor_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_treynor_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_treynor_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Triangle : IDisposable
{
    private readonly WickraHandle _handle;

    public Triangle()
    {
        var ptr = NativeMethods.wickra_triangle_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Triangle parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_triangle_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_triangle_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_triangle_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_triangle_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_triangle_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_triangle_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_triangle_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Trima : IDisposable
{
    private readonly WickraHandle _handle;

    public Trima(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_trima_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Trima parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trima_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_trima_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_trima_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trima_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trima_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trima_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trima_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Trin : IDisposable
{
    private readonly WickraHandle _handle;

    public Trin()
    {
        var ptr = NativeMethods.wickra_trin_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Trin parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trin_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_trin_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trin_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trin_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trin_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trin_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TripleTopBottom : IDisposable
{
    private readonly WickraHandle _handle;

    public TripleTopBottom()
    {
        var ptr = NativeMethods.wickra_triple_top_bottom_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TripleTopBottom parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_triple_top_bottom_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_triple_top_bottom_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_triple_top_bottom_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_triple_top_bottom_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_triple_top_bottom_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_triple_top_bottom_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_triple_top_bottom_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Tristar : IDisposable
{
    private readonly WickraHandle _handle;

    public Tristar()
    {
        var ptr = NativeMethods.wickra_tristar_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Tristar parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tristar_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_tristar_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tristar_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tristar_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tristar_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tristar_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tristar_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Trix : IDisposable
{
    private readonly WickraHandle _handle;

    public Trix(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_trix_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Trix parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_trix_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_trix_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_trix_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_trix_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_trix_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_trix_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_trix_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TrueRange : IDisposable
{
    private readonly WickraHandle _handle;

    public TrueRange()
    {
        var ptr = NativeMethods.wickra_true_range_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TrueRange parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_true_range_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_true_range_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_true_range_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_true_range_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_true_range_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_true_range_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_true_range_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Tsf : IDisposable
{
    private readonly WickraHandle _handle;

    public Tsf(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_tsf_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Tsf parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tsf_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_tsf_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tsf_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tsf_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tsf_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tsf_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tsf_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TsfOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public TsfOscillator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_tsf_oscillator_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TsfOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tsf_oscillator_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_tsf_oscillator_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tsf_oscillator_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tsf_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tsf_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tsf_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tsf_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Tsi : IDisposable
{
    private readonly WickraHandle _handle;

    public Tsi(int @long, int @short)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(@long);
        ArgumentOutOfRangeException.ThrowIfNegative(@short);
        var ptr = NativeMethods.wickra_tsi_new((nuint)@long, (nuint)@short);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Tsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tsi_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_tsi_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tsi_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Tsv : IDisposable
{
    private readonly WickraHandle _handle;

    public Tsv(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_tsv_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Tsv parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tsv_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_tsv_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tsv_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tsv_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tsv_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tsv_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tsv_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TtmSqueeze : IDisposable
{
    private readonly WickraHandle _handle;

    public TtmSqueeze(int period, double bbMult, double kcMult)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ttm_squeeze_new((nuint)period, bbMult, kcMult);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TtmSqueeze parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ttm_squeeze_free);
    }

    public TtmSqueezeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraTtmSqueezeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_ttm_squeeze_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new TtmSqueezeOutput(native.squeeze, native.momentum) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ttm_squeeze_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ttm_squeeze_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ttm_squeeze_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ttm_squeeze_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TtmTrend : IDisposable
{
    private readonly WickraHandle _handle;

    public TtmTrend(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ttm_trend_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TtmTrend parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ttm_trend_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_ttm_trend_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ttm_trend_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ttm_trend_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ttm_trend_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ttm_trend_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ttm_trend_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TurnOfMonth : IDisposable
{
    private readonly WickraHandle _handle;

    public TurnOfMonth(uint nFirst, uint nLast, int utcOffsetMinutes)
    {
        var ptr = NativeMethods.wickra_turn_of_month_new(nFirst, nLast, utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TurnOfMonth parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_turn_of_month_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_turn_of_month_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_turn_of_month_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_turn_of_month_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_turn_of_month_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_turn_of_month_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_turn_of_month_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Tweezer : IDisposable
{
    private readonly WickraHandle _handle;

    public Tweezer()
    {
        var ptr = NativeMethods.wickra_tweezer_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Tweezer parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_tweezer_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_tweezer_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_tweezer_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_tweezer_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_tweezer_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_tweezer_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_tweezer_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TwiggsMoneyFlow : IDisposable
{
    private readonly WickraHandle _handle;

    public TwiggsMoneyFlow(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_twiggs_money_flow_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TwiggsMoneyFlow parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_twiggs_money_flow_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_twiggs_money_flow_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_twiggs_money_flow_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_twiggs_money_flow_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_twiggs_money_flow_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_twiggs_money_flow_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_twiggs_money_flow_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TwoCrows : IDisposable
{
    private readonly WickraHandle _handle;

    public TwoCrows()
    {
        var ptr = NativeMethods.wickra_two_crows_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TwoCrows parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_two_crows_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_two_crows_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_two_crows_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_two_crows_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_two_crows_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_two_crows_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_two_crows_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class TypicalPrice : IDisposable
{
    private readonly WickraHandle _handle;

    public TypicalPrice()
    {
        var ptr = NativeMethods.wickra_typical_price_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid TypicalPrice parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_typical_price_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_typical_price_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_typical_price_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_typical_price_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_typical_price_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_typical_price_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_typical_price_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UlcerIndex : IDisposable
{
    private readonly WickraHandle _handle;

    public UlcerIndex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_ulcer_index_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UlcerIndex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ulcer_index_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_ulcer_index_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ulcer_index_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ulcer_index_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ulcer_index_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ulcer_index_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ulcer_index_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UltimateOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public UltimateOscillator(int @short, int mid, int @long)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(@short);
        ArgumentOutOfRangeException.ThrowIfNegative(mid);
        ArgumentOutOfRangeException.ThrowIfNegative(@long);
        var ptr = NativeMethods.wickra_ultimate_oscillator_new((nuint)@short, (nuint)mid, (nuint)@long);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UltimateOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_ultimate_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_ultimate_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_ultimate_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_ultimate_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_ultimate_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_ultimate_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_ultimate_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UniqueThreeRiver : IDisposable
{
    private readonly WickraHandle _handle;

    public UniqueThreeRiver()
    {
        var ptr = NativeMethods.wickra_unique_three_river_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UniqueThreeRiver parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_unique_three_river_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_unique_three_river_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_unique_three_river_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_unique_three_river_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_unique_three_river_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_unique_three_river_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_unique_three_river_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UniversalOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public UniversalOscillator(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_universal_oscillator_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UniversalOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_universal_oscillator_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_universal_oscillator_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_universal_oscillator_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_universal_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_universal_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_universal_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_universal_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UpDownVolumeRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public UpDownVolumeRatio()
    {
        var ptr = NativeMethods.wickra_up_down_volume_ratio_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UpDownVolumeRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_up_down_volume_ratio_free);
    }

    public double Update(ReadOnlySpan<double> change, ReadOnlySpan<double> volume, ReadOnlySpan<bool> newHigh, ReadOnlySpan<bool> newLow, ReadOnlySpan<bool> aboveMa, ReadOnlySpan<bool> onBuySignal, long timestamp)
    {
        if (volume.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newHigh.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (newLow.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (aboveMa.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }
        if (onBuySignal.Length != change.Length)
        {
            throw new ArgumentException("input spans in the same group must have equal length");
        }

        double result;
        unsafe
        {
            fixed (double* changePtr = change)
            fixed (double* volumePtr = volume)
            fixed (bool* newHighPtr = newHigh)
            fixed (bool* newLowPtr = newLow)
            fixed (bool* aboveMaPtr = aboveMa)
            fixed (bool* onBuySignalPtr = onBuySignal)
            {
                result = NativeMethods.wickra_up_down_volume_ratio_update(_handle.DangerousGetHandle(), changePtr, volumePtr, newHighPtr, newLowPtr, aboveMaPtr, onBuySignalPtr, (nuint)change.Length, timestamp);
            }
        }

        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_up_down_volume_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_up_down_volume_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_up_down_volume_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_up_down_volume_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UpsideGapThreeMethods : IDisposable
{
    private readonly WickraHandle _handle;

    public UpsideGapThreeMethods()
    {
        var ptr = NativeMethods.wickra_upside_gap_three_methods_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UpsideGapThreeMethods parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_upside_gap_three_methods_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_upside_gap_three_methods_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_upside_gap_three_methods_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_upside_gap_three_methods_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_upside_gap_three_methods_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_upside_gap_three_methods_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_upside_gap_three_methods_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UpsideGapTwoCrows : IDisposable
{
    private readonly WickraHandle _handle;

    public UpsideGapTwoCrows()
    {
        var ptr = NativeMethods.wickra_upside_gap_two_crows_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UpsideGapTwoCrows parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_upside_gap_two_crows_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_upside_gap_two_crows_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_upside_gap_two_crows_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_upside_gap_two_crows_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_upside_gap_two_crows_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_upside_gap_two_crows_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_upside_gap_two_crows_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class UpsidePotentialRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public UpsidePotentialRatio(int period, double mar)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_upside_potential_ratio_new((nuint)period, mar);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid UpsidePotentialRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_upside_potential_ratio_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_upside_potential_ratio_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_upside_potential_ratio_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_upside_potential_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_upside_potential_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_upside_potential_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_upside_potential_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ValueArea : IDisposable
{
    private readonly WickraHandle _handle;

    public ValueArea(int period, int binCount, double valueAreaPct)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(binCount);
        var ptr = NativeMethods.wickra_value_area_new((nuint)period, (nuint)binCount, valueAreaPct);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ValueArea parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_value_area_free);
    }

    public ValueAreaOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraValueAreaOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_value_area_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ValueAreaOutput(native.poc, native.vah, native.val) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_value_area_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_value_area_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_value_area_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_value_area_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ValueAtRisk : IDisposable
{
    private readonly WickraHandle _handle;

    public ValueAtRisk(int period, double confidence)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_value_at_risk_new((nuint)period, confidence);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ValueAtRisk parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_value_at_risk_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_value_at_risk_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_value_at_risk_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_value_at_risk_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_value_at_risk_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_value_at_risk_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_value_at_risk_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Variance : IDisposable
{
    private readonly WickraHandle _handle;

    public Variance(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_variance_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Variance parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_variance_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_variance_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_variance_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_variance_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_variance_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_variance_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_variance_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VarianceRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public VarianceRatio(int period, int q)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(q);
        var ptr = NativeMethods.wickra_variance_ratio_new((nuint)period, (nuint)q);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VarianceRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_variance_ratio_free);
    }

    public double Update(double x, double y)
    {
        var result = NativeMethods.wickra_variance_ratio_update(_handle.DangerousGetHandle(), x, y);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> x, ReadOnlySpan<double> y)
    {
        var n = x.Length;
        if (y.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* xPtr = x)
            fixed (double* yPtr = y)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_variance_ratio_batch(_handle.DangerousGetHandle(), xPtr, yPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_variance_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_variance_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_variance_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_variance_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VerticalHorizontalFilter : IDisposable
{
    private readonly WickraHandle _handle;

    public VerticalHorizontalFilter(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_vertical_horizontal_filter_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VerticalHorizontalFilter parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vertical_horizontal_filter_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_vertical_horizontal_filter_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_vertical_horizontal_filter_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vertical_horizontal_filter_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vertical_horizontal_filter_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vertical_horizontal_filter_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vertical_horizontal_filter_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Vidya : IDisposable
{
    private readonly WickraHandle _handle;

    public Vidya(int period, int cmoPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(cmoPeriod);
        var ptr = NativeMethods.wickra_vidya_new((nuint)period, (nuint)cmoPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Vidya parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vidya_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_vidya_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_vidya_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vidya_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vidya_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vidya_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vidya_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolatilityCone : IDisposable
{
    private readonly WickraHandle _handle;

    public VolatilityCone(int window, int lookback)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(window);
        ArgumentOutOfRangeException.ThrowIfNegative(lookback);
        var ptr = NativeMethods.wickra_volatility_cone_new((nuint)window, (nuint)lookback);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolatilityCone parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volatility_cone_free);
    }

    public VolatilityConeOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraVolatilityConeOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_volatility_cone_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new VolatilityConeOutput(native.current, native.min, native.median, native.max, native.percentile) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volatility_cone_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volatility_cone_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volatility_cone_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volatility_cone_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolatilityOfVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public VolatilityOfVolatility(int volWindow, int vovWindow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(volWindow);
        ArgumentOutOfRangeException.ThrowIfNegative(vovWindow);
        var ptr = NativeMethods.wickra_volatility_of_volatility_new((nuint)volWindow, (nuint)vovWindow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolatilityOfVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volatility_of_volatility_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_volatility_of_volatility_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_volatility_of_volatility_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volatility_of_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volatility_of_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volatility_of_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volatility_of_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolatilityRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public VolatilityRatio(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_volatility_ratio_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolatilityRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volatility_ratio_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_volatility_ratio_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_volatility_ratio_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volatility_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volatility_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volatility_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volatility_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VoltyStop : IDisposable
{
    private readonly WickraHandle _handle;

    public VoltyStop(int atrPeriod, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        var ptr = NativeMethods.wickra_volty_stop_new((nuint)atrPeriod, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VoltyStop parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volty_stop_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_volty_stop_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_volty_stop_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volty_stop_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volty_stop_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volty_stop_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volty_stop_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumeBars : IDisposable
{
    private readonly WickraHandle _handle;

    public VolumeBars(double volumePerBar)
    {
        var ptr = NativeMethods.wickra_volume_bars_new(volumePerBar);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumeBars parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_bars_free);
    }

    public VolumeBar[] Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        const int cap = 64;
        Span<WickraVolumeBar> buffer = stackalloc WickraVolumeBar[cap];
        long count;
        unsafe
        {
            fixed (WickraVolumeBar* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_volume_bars_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, ptr, (nuint)cap);
            }
        }

        GC.KeepAlive(_handle);
        if (count <= 0)
        {
            return Array.Empty<VolumeBar>();
        }

        var result = new VolumeBar[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new VolumeBar(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].volume);
        }

        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_bars_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_bars_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumeByTimeProfile : IDisposable
{
    private readonly WickraHandle _handle;
    private readonly int _valuesCapacity;

    public VolumeByTimeProfile(int buckets, int utcOffsetMinutes)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(buckets);
        var ptr = NativeMethods.wickra_volume_by_time_profile_new((nuint)buckets, utcOffsetMinutes);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumeByTimeProfile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_by_time_profile_free);
        _valuesCapacity = buckets;
    }

    public double[]? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var values = new double[_valuesCapacity];
        long length;
        unsafe
        {
            fixed (double* valuesPtr = values)
            {
                length = (long)NativeMethods.wickra_volume_by_time_profile_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, valuesPtr, (nuint)_valuesCapacity);
            }
        }

        GC.KeepAlive(_handle);
        if (length < 0)
        {
            return null;
        }

        if (length < values.Length)
        {
            Array.Resize(ref values, (int)length);
        }

        return values;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volume_by_time_profile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volume_by_time_profile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_by_time_profile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_by_time_profile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumeOscillator : IDisposable
{
    private readonly WickraHandle _handle;

    public VolumeOscillator(int fast, int slow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        var ptr = NativeMethods.wickra_volume_oscillator_new((nuint)fast, (nuint)slow);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumeOscillator parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_oscillator_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_volume_oscillator_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_volume_oscillator_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volume_oscillator_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volume_oscillator_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_oscillator_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_oscillator_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumePriceTrend : IDisposable
{
    private readonly WickraHandle _handle;

    public VolumePriceTrend()
    {
        var ptr = NativeMethods.wickra_volume_price_trend_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumePriceTrend parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_price_trend_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_volume_price_trend_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_volume_price_trend_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volume_price_trend_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volume_price_trend_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_price_trend_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_price_trend_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumeProfile : IDisposable
{
    private readonly WickraHandle _handle;
    private readonly int _valuesCapacity;

    public VolumeProfile(int period, int binCount)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(binCount);
        var ptr = NativeMethods.wickra_volume_profile_new((nuint)period, (nuint)binCount);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumeProfile parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_profile_free);
        _valuesCapacity = binCount;
    }

    public VolumeProfileOutputScalars? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var values = new double[_valuesCapacity];
        WickraVolumeProfileOutputScalars scalars;
        long length;
        unsafe
        {
            fixed (double* valuesPtr = values)
            {
                length = (long)NativeMethods.wickra_volume_profile_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &scalars, valuesPtr, (nuint)_valuesCapacity);
            }
        }

        GC.KeepAlive(_handle);
        if (length < 0)
        {
            return null;
        }

        if (length < values.Length)
        {
            Array.Resize(ref values, (int)length);
        }

        return new VolumeProfileOutputScalars(scalars.price_low, scalars.price_high, values);
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volume_profile_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volume_profile_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_profile_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_profile_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumeRsi : IDisposable
{
    private readonly WickraHandle _handle;

    public VolumeRsi(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_volume_rsi_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumeRsi parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_rsi_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_volume_rsi_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_volume_rsi_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volume_rsi_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volume_rsi_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_rsi_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_rsi_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumeWeightedMacd : IDisposable
{
    private readonly WickraHandle _handle;

    public VolumeWeightedMacd(int fast, int slow, int signal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_volume_weighted_macd_new((nuint)fast, (nuint)slow, (nuint)signal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumeWeightedMacd parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_weighted_macd_free);
    }

    public VolumeWeightedMacdOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraVolumeWeightedMacdOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_volume_weighted_macd_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new VolumeWeightedMacdOutput(native.macd, native.signal, native.histogram) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volume_weighted_macd_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volume_weighted_macd_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_weighted_macd_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_weighted_macd_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VolumeWeightedSr : IDisposable
{
    private readonly WickraHandle _handle;

    public VolumeWeightedSr(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_volume_weighted_sr_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VolumeWeightedSr parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_volume_weighted_sr_free);
    }

    public VolumeWeightedSrOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraVolumeWeightedSrOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_volume_weighted_sr_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new VolumeWeightedSrOutput(native.support, native.resistance) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_volume_weighted_sr_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_volume_weighted_sr_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_volume_weighted_sr_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_volume_weighted_sr_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Vortex : IDisposable
{
    private readonly WickraHandle _handle;

    public Vortex(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_vortex_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Vortex parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vortex_free);
    }

    public VortexOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraVortexOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_vortex_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new VortexOutput(native.plus, native.minus) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vortex_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vortex_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vortex_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vortex_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Vpin : IDisposable
{
    private readonly WickraHandle _handle;

    public Vpin(double bucketVolume, int numBuckets)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(numBuckets);
        var ptr = NativeMethods.wickra_vpin_new(bucketVolume, (nuint)numBuckets);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Vpin parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vpin_free);
    }

    public double Update(double price, double size, bool isBuy, long timestamp)
    {
        var result = NativeMethods.wickra_vpin_update(_handle.DangerousGetHandle(), price, size, isBuy, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vpin_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vpin_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vpin_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vpin_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Vwap : IDisposable
{
    private readonly WickraHandle _handle;

    public Vwap()
    {
        var ptr = NativeMethods.wickra_vwap_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Vwap parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vwap_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_vwap_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_vwap_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vwap_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vwap_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vwap_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vwap_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class VwapStdDevBands : IDisposable
{
    private readonly WickraHandle _handle;

    public VwapStdDevBands(double multiplier)
    {
        var ptr = NativeMethods.wickra_vwap_std_dev_bands_new(multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid VwapStdDevBands parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vwap_std_dev_bands_free);
    }

    public VwapStdDevBandsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraVwapStdDevBandsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_vwap_std_dev_bands_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new VwapStdDevBandsOutput(native.upper, native.middle, native.lower, native.stddev) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vwap_std_dev_bands_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vwap_std_dev_bands_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vwap_std_dev_bands_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vwap_std_dev_bands_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Vwma : IDisposable
{
    private readonly WickraHandle _handle;

    public Vwma(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_vwma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Vwma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vwma_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_vwma_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_vwma_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vwma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vwma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vwma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vwma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Vzo : IDisposable
{
    private readonly WickraHandle _handle;

    public Vzo(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_vzo_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Vzo parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_vzo_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_vzo_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_vzo_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_vzo_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_vzo_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_vzo_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_vzo_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Wad : IDisposable
{
    private readonly WickraHandle _handle;

    public Wad()
    {
        var ptr = NativeMethods.wickra_wad_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Wad parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_wad_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_wad_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_wad_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_wad_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_wad_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_wad_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_wad_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WavePm : IDisposable
{
    private readonly WickraHandle _handle;

    public WavePm(int length, int smoothing)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(length);
        ArgumentOutOfRangeException.ThrowIfNegative(smoothing);
        var ptr = NativeMethods.wickra_wave_pm_new((nuint)length, (nuint)smoothing);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WavePm parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_wave_pm_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_wave_pm_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_wave_pm_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_wave_pm_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_wave_pm_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_wave_pm_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_wave_pm_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WaveTrend : IDisposable
{
    private readonly WickraHandle _handle;

    public WaveTrend(int channelPeriod, int averagePeriod, int signalPeriod)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(channelPeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(averagePeriod);
        ArgumentOutOfRangeException.ThrowIfNegative(signalPeriod);
        var ptr = NativeMethods.wickra_wave_trend_new((nuint)channelPeriod, (nuint)averagePeriod, (nuint)signalPeriod);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WaveTrend parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_wave_trend_free);
    }

    public WaveTrendOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraWaveTrendOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_wave_trend_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new WaveTrendOutput(native.wt1, native.wt2) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_wave_trend_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_wave_trend_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_wave_trend_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_wave_trend_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Wedge : IDisposable
{
    private readonly WickraHandle _handle;

    public Wedge()
    {
        var ptr = NativeMethods.wickra_wedge_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Wedge parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_wedge_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_wedge_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_wedge_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_wedge_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_wedge_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_wedge_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_wedge_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WeightedClose : IDisposable
{
    private readonly WickraHandle _handle;

    public WeightedClose()
    {
        var ptr = NativeMethods.wickra_weighted_close_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WeightedClose parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_weighted_close_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_weighted_close_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_weighted_close_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_weighted_close_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_weighted_close_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_weighted_close_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_weighted_close_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WickRatio : IDisposable
{
    private readonly WickraHandle _handle;

    public WickRatio()
    {
        var ptr = NativeMethods.wickra_wick_ratio_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WickRatio parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_wick_ratio_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_wick_ratio_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_wick_ratio_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_wick_ratio_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_wick_ratio_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_wick_ratio_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_wick_ratio_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WilliamsFractals : IDisposable
{
    private readonly WickraHandle _handle;

    public WilliamsFractals()
    {
        var ptr = NativeMethods.wickra_williams_fractals_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WilliamsFractals parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_williams_fractals_free);
    }

    public WilliamsFractalsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraWilliamsFractalsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_williams_fractals_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new WilliamsFractalsOutput(native.up, native.down) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_williams_fractals_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_williams_fractals_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_williams_fractals_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_williams_fractals_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WilliamsR : IDisposable
{
    private readonly WickraHandle _handle;

    public WilliamsR(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_williams_r_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WilliamsR parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_williams_r_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_williams_r_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_williams_r_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_williams_r_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_williams_r_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_williams_r_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_williams_r_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WinRate : IDisposable
{
    private readonly WickraHandle _handle;

    public WinRate(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_win_rate_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WinRate parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_win_rate_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_win_rate_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_win_rate_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_win_rate_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_win_rate_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_win_rate_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_win_rate_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Wma : IDisposable
{
    private readonly WickraHandle _handle;

    public Wma(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_wma_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Wma parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_wma_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_wma_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_wma_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_wma_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_wma_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_wma_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_wma_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class WoodiePivots : IDisposable
{
    private readonly WickraHandle _handle;

    public WoodiePivots()
    {
        var ptr = NativeMethods.wickra_woodie_pivots_new();
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid WoodiePivots parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_woodie_pivots_free);
    }

    public WoodiePivotsOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraWoodiePivotsOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_woodie_pivots_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new WoodiePivotsOutput(native.pp, native.r1, native.r2, native.s1, native.s2) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_woodie_pivots_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_woodie_pivots_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_woodie_pivots_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_woodie_pivots_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class YangZhangVolatility : IDisposable
{
    private readonly WickraHandle _handle;

    public YangZhangVolatility(int period, int tradingPeriods)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        ArgumentOutOfRangeException.ThrowIfNegative(tradingPeriods);
        var ptr = NativeMethods.wickra_yang_zhang_volatility_new((nuint)period, (nuint)tradingPeriods);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid YangZhangVolatility parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_yang_zhang_volatility_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_yang_zhang_volatility_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_yang_zhang_volatility_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_yang_zhang_volatility_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_yang_zhang_volatility_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_yang_zhang_volatility_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_yang_zhang_volatility_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class YoyoExit : IDisposable
{
    private readonly WickraHandle _handle;

    public YoyoExit(int atrPeriod, double multiplier)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(atrPeriod);
        var ptr = NativeMethods.wickra_yoyo_exit_new((nuint)atrPeriod, multiplier);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid YoyoExit parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_yoyo_exit_free);
    }

    public double Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        var result = NativeMethods.wickra_yoyo_exit_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> open, ReadOnlySpan<double> high, ReadOnlySpan<double> low, ReadOnlySpan<double> close, ReadOnlySpan<double> volume, ReadOnlySpan<long> timestamp)
    {
        var n = open.Length;
        if (high.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (low.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (close.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (volume.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }
        if (timestamp.Length != n)
        {
            throw new ArgumentException("all input spans must have the same length");
        }

        var output = new double[n];
        unsafe
        {
            fixed (double* openPtr = open)
            fixed (double* highPtr = high)
            fixed (double* lowPtr = low)
            fixed (double* closePtr = close)
            fixed (double* volumePtr = volume)
            fixed (long* timestampPtr = timestamp)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_yoyo_exit_batch(_handle.DangerousGetHandle(), openPtr, highPtr, lowPtr, closePtr, volumePtr, timestampPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_yoyo_exit_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_yoyo_exit_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_yoyo_exit_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_yoyo_exit_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ZScore : IDisposable
{
    private readonly WickraHandle _handle;

    public ZScore(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_z_score_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ZScore parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_z_score_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_z_score_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_z_score_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_z_score_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_z_score_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_z_score_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_z_score_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ZeroLagMacd : IDisposable
{
    private readonly WickraHandle _handle;

    public ZeroLagMacd(int fast, int slow, int signal)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(fast);
        ArgumentOutOfRangeException.ThrowIfNegative(slow);
        ArgumentOutOfRangeException.ThrowIfNegative(signal);
        var ptr = NativeMethods.wickra_zero_lag_macd_new((nuint)fast, (nuint)slow, (nuint)signal);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ZeroLagMacd parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_zero_lag_macd_free);
    }

    public ZeroLagMacdOutput? Update(double @value)
    {
        WickraZeroLagMacdOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_zero_lag_macd_update(_handle.DangerousGetHandle(), @value, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ZeroLagMacdOutput(native.macd, native.signal, native.histogram) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_zero_lag_macd_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_zero_lag_macd_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_zero_lag_macd_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_zero_lag_macd_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class ZigZag : IDisposable
{
    private readonly WickraHandle _handle;

    public ZigZag(double threshold)
    {
        var ptr = NativeMethods.wickra_zig_zag_new(threshold);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid ZigZag parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_zig_zag_free);
    }

    public ZigZagOutput? Update(double open, double high, double low, double close, double volume, long timestamp)
    {
        WickraZigZagOutput native;
        bool ok;
        unsafe
        {
            ok = NativeMethods.wickra_zig_zag_update(_handle.DangerousGetHandle(), open, high, low, close, volume, timestamp, &native);
        }

        GC.KeepAlive(_handle);
        return ok ? new ZigZagOutput(native.swing, native.direction) : null;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_zig_zag_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_zig_zag_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_zig_zag_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_zig_zag_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

public sealed class Zlema : IDisposable
{
    private readonly WickraHandle _handle;

    public Zlema(int period)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(period);
        var ptr = NativeMethods.wickra_zlema_new((nuint)period);
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid Zlema parameters");
        }

        _handle = new WickraHandle(ptr, NativeMethods.wickra_zlema_free);
    }

    public double Update(double @value)
    {
        var result = NativeMethods.wickra_zlema_update(_handle.DangerousGetHandle(), @value);
        GC.KeepAlive(_handle);
        return result;
    }

    public double[] Batch(ReadOnlySpan<double> input)
    {
        var n = input.Length;

        var output = new double[n];
        unsafe
        {
            fixed (double* inputPtr = input)
            fixed (double* outputPtr = output)
            {
                NativeMethods.wickra_zlema_batch(_handle.DangerousGetHandle(), inputPtr, outputPtr, (nuint)n);
            }
        }

        GC.KeepAlive(_handle);
        return output;
    }

    /// <summary>Number of updates required before <see cref="Update"/> yields a value.</summary>
    public int WarmupPeriod()
    {
        var result = (int)NativeMethods.wickra_zlema_warmup_period(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>Whether the indicator has consumed enough input to emit a value.</summary>
    public bool IsReady()
    {
        var result = NativeMethods.wickra_zlema_is_ready(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return result;
    }

    /// <summary>The indicator's canonical name.</summary>
    public string Name()
    {
        var ptr = NativeMethods.wickra_zlema_name(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
        return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
    }

    public void Reset()
    {
        NativeMethods.wickra_zlema_reset(_handle.DangerousGetHandle());
        GC.KeepAlive(_handle);
    }

    public void Dispose() => _handle.Dispose();
}

/// <summary>Kline interval for the live Binance feed.</summary>
public enum BinanceInterval : byte
{
    OneSecond,
    OneMinute,
    ThreeMinutes,
    FiveMinutes,
    FifteenMinutes,
    ThirtyMinutes,
    OneHour,
    TwoHours,
    FourHours,
    SixHours,
    EightHours,
    TwelveHours,
    OneDay,
    ThreeDays,
    OneWeek,
    OneMonth,
}

/// <summary>One event from the live Binance feed.</summary>
public readonly record struct KlineEvent(string Symbol, double Open, double High, double Low, double Close, double Volume, long OpenTime, bool IsClosed);

/// <summary>A live Binance kline stream over the Wickra C ABI.</summary>
public sealed class BinanceFeed : IDisposable
{
    private readonly WickraHandle _handle;

    /// <summary>Connect to Binance's live kline stream for the given comma-separated symbols
    /// (case-insensitive) at <paramref name="interval"/>. <paramref name="baseUrl"/> overrides the
    /// endpoint (null = production; pass a ws:// URL to target a test server).</summary>
    public BinanceFeed(string symbols, BinanceInterval interval, string? baseUrl = null)
    {
        ArgumentNullException.ThrowIfNull(symbols);
        var symBytes = System.Text.Encoding.UTF8.GetBytes(symbols + '\0');
        var urlBytes = baseUrl is null ? null : System.Text.Encoding.UTF8.GetBytes(baseUrl + '\0');
        nint ptr;
        unsafe
        {
            fixed (byte* sp = symBytes)
            fixed (byte* up = urlBytes)
            {
                ptr = NativeMethods.wickra_binance_connect(sp, (byte)interval, up);
            }
        }
        if (ptr == nint.Zero)
        {
            throw new ArgumentException("invalid BinanceFeed parameters");
        }
        _handle = new WickraHandle(ptr, NativeMethods.wickra_binance_free);
    }

    /// <summary>Poll for the next kline event, waiting up to <paramref name="timeout"/>.
    /// Returns the event, or null on timeout (call again). Throws once the stream is
    /// closed or has errored out.</summary>
    public KlineEvent? Next(TimeSpan timeout)
    {
        WickraKlineEvent ev;
        int code;
        unsafe
        {
            code = NativeMethods.wickra_binance_next(_handle.DangerousGetHandle(), &ev, (long)timeout.TotalMilliseconds);
        }
        GC.KeepAlive(_handle);
        if (code == 0)
        {
            return null;
        }
        if (code != 1)
        {
            throw new InvalidOperationException("binance feed closed");
        }
        string symbol;
        unsafe
        {
            symbol = Marshal.PtrToStringUTF8((nint)ev.symbol) ?? string.Empty;
        }
        return new KlineEvent(symbol, ev.open, ev.high, ev.low, ev.close, ev.volume, ev.open_time, ev.is_closed != 0);
    }

    /// <summary>Fetch historical klines from Binance's REST endpoint. <paramref name="symbol"/>
    /// is the trading pair (case-insensitive), <paramref name="limit"/> the number of
    /// candles (1..=1000). <paramref name="startMs"/>/<paramref name="endMs"/> are inclusive
    /// Unix-millisecond bounds (negative = unset); <paramref name="baseUrl"/> overrides the
    /// host (null = production). Blocks until the response arrives.</summary>
    public static Candle[] FetchKlines(string symbol, BinanceInterval interval, uint limit, long startMs = -1, long endMs = -1, string? baseUrl = null)
    {
        ArgumentNullException.ThrowIfNull(symbol);
        if (limit == 0)
        {
            throw new ArgumentException("limit must be in 1..=1000");
        }
        var symBytes = System.Text.Encoding.UTF8.GetBytes(symbol + '\0');
        var urlBytes = baseUrl is null ? null : System.Text.Encoding.UTF8.GetBytes(baseUrl + '\0');
        var buffer = new WickraCandle[limit];
        long count;
        unsafe
        {
            fixed (byte* sp = symBytes)
            fixed (byte* up = urlBytes)
            fixed (WickraCandle* ptr = buffer)
            {
                count = (long)NativeMethods.wickra_binance_fetch_klines(sp, (byte)interval, limit, startMs, endMs, up, ptr, (nuint)limit);
            }
        }
        if (count < 0)
        {
            throw new ArgumentException("invalid FetchKlines parameters or transport error");
        }
        var result = new Candle[count];
        for (var i = 0; i < count; i++)
        {
            result[i] = new Candle(buffer[i].open, buffer[i].high, buffer[i].low, buffer[i].close, buffer[i].volume, buffer[i].timestamp);
        }
        return result;
    }

    public void Dispose()
    {
        unsafe
        {
            NativeMethods.wickra_binance_close(_handle.DangerousGetHandle());
        }
        GC.KeepAlive(_handle);
        _handle.Dispose();
    }
}

