//! C ABI for Wickra — the hub every C-capable language (C, C++, Go, C#, Java, R) //! links against. Each indicator is exposed as `extern "C"` functions over an //! opaque handle: //! //! - `wickra__new(...)` — construct; returns `NULL` on invalid parameters. //! - `wickra__update(h, ...)` — feed one point; returns the output, or `NaN` //! while warming up / if `h` is `NULL` / if the inputs are invalid. //! - `wickra__batch(h, ..., out, n)` — write one output per input into the //! caller-owned `out` buffer (length `n`), `NaN` at warmup positions. //! - `wickra__reset(h)` — clear all state. //! - `wickra__free(h)` — destroy the handle. Every `_new` must be paired //! with exactly one `_free`; there is no RAII across the C boundary. //! //! GENERATED from the Wickra core. Do not edit by hand — regenerate from the //! core and commit this file together with `include/wickra.h`. use core::ptr; use core::slice; use wickra_core::{ AbandonedBaby, Abcd, AbsoluteBreadthIndex, AccelerationBands, AcceleratorOscillator, AdOscillator, AdVolumeLine, AdaptiveCci, AdaptiveCycle, AdaptiveLaguerreFilter, AdaptiveRsi, Adl, AdvanceBlock, AdvanceDecline, AdvanceDeclineRatio, Adx, Adxr, Alligator, Alma, Alpha, AmihudIlliquidity, AnchoredRsi, AnchoredVwap, AndrewsPitchfork, Apo, Aroon, AroonOscillator, Atr, AtrBands, AtrRatchet, AtrTrailingStop, AutoFib, Autocorrelation, AutocorrelationPeriodogram, AverageDailyRange, AverageDrawdown, AvgPrice, AwesomeOscillator, AwesomeOscillatorHistogram, BalanceOfPower, BandpassFilter, BarBuilder, Bat, BeltHold, Beta, BetaNeutralSpread, BetterVolume, BipowerVariation, BodySizePct, BollingerBands, BollingerBandwidth, BomarBands, BreadthThrust, Breakaway, BullishPercentIndex, BurkeRatio, Butterfly, CalendarSpread, CalmarRatio, Camarilla, Candle, CandleVolume, Cci, CenterOfGravity, CentralPivotRange, Cfo, ChaikinMoneyFlow, ChaikinOscillator, ChaikinVolatility, ChandeKrollStop, ChandelierExit, ChoppinessIndex, ClassicPivots, CloseVsOpen, ClosingMarubozu, Cmo, CoefficientOfVariation, Cointegration, CommonSenseRatio, CompositeProfile, ConcealingBabySwallow, ConditionalValueAtRisk, ConnorsRsi, Coppock, CorrelationTrendIndicator, Counterattack, Crab, CrossSection, CumulativeVolumeDelta, CumulativeVolumeIndex, CupAndHandle, CyberneticCycle, Cypher, DayOfWeekProfile, Decycler, DecyclerOscillator, Dema, DemandIndex, DemarkPivots, DepthSlope, DerivativeOscillator, DerivativesTick, DetrendedStdDev, DisparityIndex, DistanceSsd, Doji, DojiStar, DollarBars, Donchian, DonchianStop, DoubleBollinger, DoubleTopBottom, DownsideGapThreeMethods, Dpo, DragonflyDoji, DrawdownDuration, DumplingTop, Dx, DynamicMomentumIndex, EaseOfMovement, EffectiveSpread, EhlersStochastic, Ehma, ElderImpulse, ElderRay, ElderSafeZone, Ema, EmpiricalModeDecomposition, Engulfing, Equivolume, EstimatedLeverageRatio, EvenBetterSinewave, EveningDojiStar, Evwma, EwmaVolatility, Expectancy, FallingThreeMethods, Fama, FibArcs, FibChannel, FibConfluence, FibExtension, FibFan, FibProjection, FibRetracement, FibTimeZones, FibonacciPivots, FisherRsi, FisherTransform, FlagPennant, Footprint, ForceIndex, FractalChaosBands, Frama, FryPanBottom, FundingBasis, FundingImpliedApr, FundingRate, FundingRateMean, FundingRateZScore, GainLossRatio, GainToPainRatio, GapSideBySideWhite, Garch11, GarmanKlassVolatility, Gartley, GatorOscillator, GeneralizedDema, GeometricMa, GoldenPocket, GrangerCausality, GravestoneDoji, Hammer, HangingMan, Harami, HaramiCross, HasbrouckInformationShare, HeadAndShoulders, HeikinAshi, HeikinAshiOscillator, HiLoActivator, HighLowIndex, HighLowRange, HighLowVolumeNodes, HighWave, HighpassFilter, Hikkake, HikkakeModified, HilbertDominantCycle, HistoricalVolatility, Hma, HoltWinters, HomingPigeon, HtDcPhase, HtPhasor, HtTrendMode, HurstChannel, HurstExponent, Ichimoku, IdenticalThreeCrows, ImbalanceBars, InNeck, Indicator, Inertia, InformationRatio, InitialBalance, InstantaneousTrendline, IntradayIntensity, IntradayMomentumIndex, IntradayVolatilityProfile, InverseFisherTransform, InvertedHammer, JarqueBera, Jma, JumpIndicator, KRatio, KagiBars, KalmanHedgeRatio, Kama, KaseDevStop, KasePermissionStochastic, KellyCriterion, Keltner, KendallTau, Kicking, KickingByLength, Kst, Kurtosis, Kvo, KylesLambda, LadderBottom, LaguerreRsi, LeadLagCrossCorrelation, Level, LinRegAngle, LinRegChannel, LinRegIntercept, LinRegSlope, LinearRegression, LiquidationFeatures, LogReturn, LongLeggedDoji, LongLine, LongShortRatio, M2Measure, MaEnvelope, MaType, MacdExt, MacdFix, MacdHistogram, MacdIndicator, Mama, MarketFacilitationIndex, MartinRatio, Marubozu, MassIndex, MatHold, MatchingLow, MaxDrawdown, McClellanOscillator, McClellanSummationIndex, McGinleyDynamic, MedianAbsoluteDeviation, MedianChannel, MedianMa, MedianPrice, Member, Mfi, Microprice, MidPoint, MidPrice, MinusDi, MinusDm, ModifiedMaStop, Mom, MorningDojiStar, MorningEveningStar, MurreyMathLines, NakedPoc, Natr, NewHighsNewLows, NewPriceLines, Nrtr, Nvi, OIPriceDivergence, OIWeighted, Obv, OiToVolumeRatio, OmegaRatio, OnNeck, OpenInterestDelta, OpenInterestMomentum, OpeningMarubozu, OpeningRange, OrderBook, OrderBookImbalanceFull, OrderBookImbalanceTop1, OrderBookImbalanceTopN, OrderFlowImbalance, OuHalfLife, OvernightGap, OvernightIntradayReturn, PainIndex, PairSpreadZScore, PairwiseBeta, ParkinsonVolatility, PearsonCorrelation, PercentAboveMa, PercentB, PercentageTrailingStop, PerpetualPremiumIndex, Pgo, PiercingDarkCloud, Pin, PivotReversal, PlusDi, PlusDm, Pmo, PointAndFigureBars, PolarizedFractalEfficiency, Ppo, PpoHistogram, ProfileShape, ProfitFactor, ProjectionBands, ProjectionOscillator, Psar, Pvi, Qqe, Qstick, QuartileBands, QuotedSpread, RSquared, RangeBars, RealizedSpread, RealizedVolatility, RecoveryFactor, RectangleRange, Reflex, RegimeLabel, RelativeStrengthAB, RenkoBars, RenkoTrailingStop, RickshawMan, RisingThreeMethods, Rmi, Roc, Rocp, Rocr, Rocr100, RogersSatchellVolatility, RollMeasure, RollingCorrelation, RollingCovariance, RollingIqr, RollingMinMaxScaler, RollingPercentileRank, RollingQuantile, RollingVwap, RoofingFilter, Rsi, Rsx, RunBars, Rvi, RviVolatility, Rwi, SampleEntropy, SarExt, SeasonalZScore, SeparatingLines, SessionHighLow, SessionRange, SessionVwap, ShannonEntropy, Shark, SharpeRatio, ShootingStar, ShortLine, Side, SignedVolume, SineWave, SineWeightedMa, SinglePrints, Skewness, Sma, Smi, Smma, SmoothedHeikinAshi, SortinoRatio, SpearmanCorrelation, SpinningTop, SpreadAr1Coefficient, SpreadBollingerBands, SpreadHurst, StalledPattern, StandardError, StandardErrorBands, StarcBands, Stc, StdDev, StepTrailingStop, SterlingRatio, StickSandwich, StochRsi, Stochastic, StochasticCci, SuperSmoother, SuperTrend, TailRatio, TakerBuySellRatio, Takuri, TasukiGap, TdCamouflage, TdClop, TdClopwin, TdCombo, TdCountdown, TdDWave, TdDeMarker, TdDifferential, TdLines, TdMovingAverage, TdOpen, TdPressure, TdPropulsion, TdRangeProjection, TdRei, TdRiskLevel, TdSequential, TdSetup, TdTrap, Tema, TermStructureBasis, ThreeDrives, ThreeInside, ThreeLineBreak, ThreeLineBreakBars, ThreeLineStrike, ThreeOutside, ThreeSoldiersOrCrows, ThreeStarsInSouth, Thrusting, TickBars, TickIndex, Tii, TimeBasedStop, TimeOfDayReturnProfile, TowerTopBottom, TpoProfile, Trade, TradeImbalance, TradeQuote, TradeSignAutocorrelation, TradeVolumeIndex, TrendLabel, TrendStrengthIndex, Trendflex, TreynorRatio, Triangle, Trima, Trin, TripleTopBottom, Tristar, Trix, TrueRange, Tsf, TsfOscillator, Tsi, Tsv, TtmSqueeze, TtmTrend, TurnOfMonth, Tweezer, TwiggsMoneyFlow, TwoCrows, TypicalPrice, UlcerIndex, UltimateOscillator, UniqueThreeRiver, UniversalOscillator, UpDownVolumeRatio, UpsideGapThreeMethods, UpsideGapTwoCrows, UpsidePotentialRatio, ValueArea, ValueAtRisk, Variance, VarianceRatio, VerticalHorizontalFilter, Vidya, VolatilityCone, VolatilityOfVolatility, VolatilityRatio, VoltyStop, VolumeBars, VolumeByTimeProfile, VolumeOscillator, VolumePriceTrend, VolumeProfile, VolumeRsi, VolumeWeightedMacd, VolumeWeightedSr, Vortex, Vpin, Vwap, VwapStdDevBands, Vwma, Vzo, Wad, WavePm, WaveTrend, Wedge, WeightedClose, WickRatio, WilliamsFractals, WilliamsR, WinRate, Wma, WoodiePivots, YangZhangVolatility, YoyoExit, ZScore, ZeroLagMacd, ZigZag, Zlema, T3, }; // ===== Scalar indicators (f64 -> f64) ===== /// Create a `AdaptiveCycle` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_adaptive_cycle_free`. #[no_mangle] pub extern "C" fn wickra_adaptive_cycle_new() -> *mut AdaptiveCycle { Box::into_raw(Box::new(AdaptiveCycle::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_adaptive_cycle_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cycle_update( handle: *mut AdaptiveCycle, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_adaptive_cycle_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cycle_batch( handle: *mut AdaptiveCycle, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_adaptive_cycle_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cycle_reset(handle: *mut AdaptiveCycle) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_adaptive_cycle_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_adaptive_cycle_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cycle_free(handle: *mut AdaptiveCycle) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdaptiveLaguerreFilter` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_adaptive_laguerre_filter_free`. #[no_mangle] pub extern "C" fn wickra_adaptive_laguerre_filter_new( period: usize, ) -> *mut AdaptiveLaguerreFilter { match AdaptiveLaguerreFilter::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_adaptive_laguerre_filter_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_update( handle: *mut AdaptiveLaguerreFilter, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_adaptive_laguerre_filter_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_batch( handle: *mut AdaptiveLaguerreFilter, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_adaptive_laguerre_filter_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_reset( handle: *mut AdaptiveLaguerreFilter, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_adaptive_laguerre_filter_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_adaptive_laguerre_filter_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_laguerre_filter_free(handle: *mut AdaptiveLaguerreFilter) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdaptiveRsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_adaptive_rsi_free`. #[no_mangle] pub extern "C" fn wickra_adaptive_rsi_new(period: usize) -> *mut AdaptiveRsi { match AdaptiveRsi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_adaptive_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_rsi_update(handle: *mut AdaptiveRsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_adaptive_rsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_rsi_batch( handle: *mut AdaptiveRsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_adaptive_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_rsi_reset(handle: *mut AdaptiveRsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_adaptive_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_adaptive_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_rsi_free(handle: *mut AdaptiveRsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Alma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_alma_free`. #[no_mangle] pub extern "C" fn wickra_alma_new(period: usize, offset: f64, sigma: f64) -> *mut Alma { match Alma::new(period, offset, sigma) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_alma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alma_update(handle: *mut Alma, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_alma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_alma_batch( handle: *mut Alma, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_alma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alma_reset(handle: *mut Alma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_alma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_alma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alma_free(handle: *mut Alma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AnchoredRsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_anchored_rsi_free`. #[no_mangle] pub extern "C" fn wickra_anchored_rsi_new() -> *mut AnchoredRsi { Box::into_raw(Box::new(AnchoredRsi::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_anchored_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_rsi_update(handle: *mut AnchoredRsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_anchored_rsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_rsi_batch( handle: *mut AnchoredRsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_anchored_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_rsi_reset(handle: *mut AnchoredRsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_anchored_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_anchored_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_rsi_free(handle: *mut AnchoredRsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Apo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_apo_free`. #[no_mangle] pub extern "C" fn wickra_apo_new(fast: usize, slow: usize) -> *mut Apo { match Apo::new(fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_apo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_apo_update(handle: *mut Apo, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_apo_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_apo_batch( handle: *mut Apo, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_apo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_apo_reset(handle: *mut Apo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_apo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_apo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_apo_free(handle: *mut Apo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Autocorrelation` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_autocorrelation_free`. #[no_mangle] pub extern "C" fn wickra_autocorrelation_new(period: usize, lag: usize) -> *mut Autocorrelation { match Autocorrelation::new(period, lag) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_autocorrelation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_update( handle: *mut Autocorrelation, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_autocorrelation_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_batch( handle: *mut Autocorrelation, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_autocorrelation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_reset(handle: *mut Autocorrelation) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_autocorrelation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_autocorrelation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_free(handle: *mut Autocorrelation) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AutocorrelationPeriodogram` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_autocorrelation_periodogram_free`. #[no_mangle] pub extern "C" fn wickra_autocorrelation_periodogram_new( min_period: usize, max_period: usize, ) -> *mut AutocorrelationPeriodogram { match AutocorrelationPeriodogram::new(min_period, max_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_autocorrelation_periodogram_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_periodogram_update( handle: *mut AutocorrelationPeriodogram, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_autocorrelation_periodogram_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_periodogram_batch( handle: *mut AutocorrelationPeriodogram, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_autocorrelation_periodogram_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_periodogram_reset( handle: *mut AutocorrelationPeriodogram, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_autocorrelation_periodogram_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_autocorrelation_periodogram_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_autocorrelation_periodogram_free( handle: *mut AutocorrelationPeriodogram, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AverageDrawdown` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_average_drawdown_free`. #[no_mangle] pub extern "C" fn wickra_average_drawdown_new(period: usize) -> *mut AverageDrawdown { match AverageDrawdown::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_average_drawdown_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_average_drawdown_update( handle: *mut AverageDrawdown, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_average_drawdown_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_average_drawdown_batch( handle: *mut AverageDrawdown, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_average_drawdown_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_average_drawdown_reset(handle: *mut AverageDrawdown) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_average_drawdown_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_average_drawdown_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_average_drawdown_free(handle: *mut AverageDrawdown) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BandpassFilter` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_bandpass_filter_free`. #[no_mangle] pub extern "C" fn wickra_bandpass_filter_new(period: usize, bandwidth: f64) -> *mut BandpassFilter { match BandpassFilter::new(period, bandwidth) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_bandpass_filter_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bandpass_filter_update( handle: *mut BandpassFilter, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_bandpass_filter_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_bandpass_filter_batch( handle: *mut BandpassFilter, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_bandpass_filter_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bandpass_filter_reset(handle: *mut BandpassFilter) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_bandpass_filter_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_bandpass_filter_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bandpass_filter_free(handle: *mut BandpassFilter) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BipowerVariation` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_bipower_variation_free`. #[no_mangle] pub extern "C" fn wickra_bipower_variation_new(period: usize) -> *mut BipowerVariation { match BipowerVariation::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_bipower_variation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bipower_variation_update( handle: *mut BipowerVariation, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_bipower_variation_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_bipower_variation_batch( handle: *mut BipowerVariation, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_bipower_variation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bipower_variation_reset(handle: *mut BipowerVariation) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_bipower_variation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_bipower_variation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bipower_variation_free(handle: *mut BipowerVariation) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BollingerBandwidth` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_bollinger_bandwidth_free`. #[no_mangle] pub extern "C" fn wickra_bollinger_bandwidth_new( period: usize, multiplier: f64, ) -> *mut BollingerBandwidth { match BollingerBandwidth::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_bollinger_bandwidth_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bollinger_bandwidth_update( handle: *mut BollingerBandwidth, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_bollinger_bandwidth_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_bollinger_bandwidth_batch( handle: *mut BollingerBandwidth, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_bollinger_bandwidth_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bollinger_bandwidth_reset(handle: *mut BollingerBandwidth) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_bollinger_bandwidth_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_bollinger_bandwidth_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bollinger_bandwidth_free(handle: *mut BollingerBandwidth) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BurkeRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_burke_ratio_free`. #[no_mangle] pub extern "C" fn wickra_burke_ratio_new(period: usize) -> *mut BurkeRatio { match BurkeRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_burke_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_burke_ratio_update(handle: *mut BurkeRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_burke_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_burke_ratio_batch( handle: *mut BurkeRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_burke_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_burke_ratio_reset(handle: *mut BurkeRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_burke_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_burke_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_burke_ratio_free(handle: *mut BurkeRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CalmarRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_calmar_ratio_free`. #[no_mangle] pub extern "C" fn wickra_calmar_ratio_new(period: usize) -> *mut CalmarRatio { match CalmarRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_calmar_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_calmar_ratio_update(handle: *mut CalmarRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_calmar_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_calmar_ratio_batch( handle: *mut CalmarRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_calmar_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_calmar_ratio_reset(handle: *mut CalmarRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_calmar_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_calmar_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_calmar_ratio_free(handle: *mut CalmarRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CenterOfGravity` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_center_of_gravity_free`. #[no_mangle] pub extern "C" fn wickra_center_of_gravity_new(period: usize) -> *mut CenterOfGravity { match CenterOfGravity::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_center_of_gravity_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_center_of_gravity_update( handle: *mut CenterOfGravity, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_center_of_gravity_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_center_of_gravity_batch( handle: *mut CenterOfGravity, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_center_of_gravity_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_center_of_gravity_reset(handle: *mut CenterOfGravity) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_center_of_gravity_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_center_of_gravity_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_center_of_gravity_free(handle: *mut CenterOfGravity) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Cfo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cfo_free`. #[no_mangle] pub extern "C" fn wickra_cfo_new(period: usize) -> *mut Cfo { match Cfo::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_cfo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cfo_update(handle: *mut Cfo, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_cfo_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_cfo_batch( handle: *mut Cfo, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cfo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cfo_reset(handle: *mut Cfo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cfo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cfo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cfo_free(handle: *mut Cfo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Cmo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cmo_free`. #[no_mangle] pub extern "C" fn wickra_cmo_new(period: usize) -> *mut Cmo { match Cmo::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_cmo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cmo_update(handle: *mut Cmo, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_cmo_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_cmo_batch( handle: *mut Cmo, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cmo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cmo_reset(handle: *mut Cmo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cmo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cmo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cmo_free(handle: *mut Cmo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CoefficientOfVariation` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_coefficient_of_variation_free`. #[no_mangle] pub extern "C" fn wickra_coefficient_of_variation_new( period: usize, ) -> *mut CoefficientOfVariation { match CoefficientOfVariation::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_coefficient_of_variation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_coefficient_of_variation_update( handle: *mut CoefficientOfVariation, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_coefficient_of_variation_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_coefficient_of_variation_batch( handle: *mut CoefficientOfVariation, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_coefficient_of_variation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_coefficient_of_variation_reset( handle: *mut CoefficientOfVariation, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_coefficient_of_variation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_coefficient_of_variation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_coefficient_of_variation_free(handle: *mut CoefficientOfVariation) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CommonSenseRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_common_sense_ratio_free`. #[no_mangle] pub extern "C" fn wickra_common_sense_ratio_new(period: usize) -> *mut CommonSenseRatio { match CommonSenseRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_common_sense_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_common_sense_ratio_update( handle: *mut CommonSenseRatio, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_common_sense_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_common_sense_ratio_batch( handle: *mut CommonSenseRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_common_sense_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_common_sense_ratio_reset(handle: *mut CommonSenseRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_common_sense_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_common_sense_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_common_sense_ratio_free(handle: *mut CommonSenseRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ConditionalValueAtRisk` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_conditional_value_at_risk_free`. #[no_mangle] pub extern "C" fn wickra_conditional_value_at_risk_new( period: usize, confidence: f64, ) -> *mut ConditionalValueAtRisk { match ConditionalValueAtRisk::new(period, confidence) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_conditional_value_at_risk_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_conditional_value_at_risk_update( handle: *mut ConditionalValueAtRisk, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_conditional_value_at_risk_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_conditional_value_at_risk_batch( handle: *mut ConditionalValueAtRisk, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_conditional_value_at_risk_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_conditional_value_at_risk_reset( handle: *mut ConditionalValueAtRisk, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_conditional_value_at_risk_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_conditional_value_at_risk_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_conditional_value_at_risk_free( handle: *mut ConditionalValueAtRisk, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ConnorsRsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_connors_rsi_free`. #[no_mangle] pub extern "C" fn wickra_connors_rsi_new( period_rsi: usize, period_streak: usize, period_rank: usize, ) -> *mut ConnorsRsi { match ConnorsRsi::new(period_rsi, period_streak, period_rank) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_connors_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_connors_rsi_update(handle: *mut ConnorsRsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_connors_rsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_connors_rsi_batch( handle: *mut ConnorsRsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_connors_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_connors_rsi_reset(handle: *mut ConnorsRsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_connors_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_connors_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_connors_rsi_free(handle: *mut ConnorsRsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Coppock` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_coppock_free`. #[no_mangle] pub extern "C" fn wickra_coppock_new( roc_long_period: usize, roc_short_period: usize, wma_period: usize, ) -> *mut Coppock { match Coppock::new(roc_long_period, roc_short_period, wma_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_coppock_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_coppock_update(handle: *mut Coppock, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_coppock_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_coppock_batch( handle: *mut Coppock, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_coppock_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_coppock_reset(handle: *mut Coppock) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_coppock_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_coppock_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_coppock_free(handle: *mut Coppock) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CorrelationTrendIndicator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_correlation_trend_indicator_free`. #[no_mangle] pub extern "C" fn wickra_correlation_trend_indicator_new( period: usize, ) -> *mut CorrelationTrendIndicator { match CorrelationTrendIndicator::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_correlation_trend_indicator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_correlation_trend_indicator_update( handle: *mut CorrelationTrendIndicator, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_correlation_trend_indicator_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_correlation_trend_indicator_batch( handle: *mut CorrelationTrendIndicator, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_correlation_trend_indicator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_correlation_trend_indicator_reset( handle: *mut CorrelationTrendIndicator, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_correlation_trend_indicator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_correlation_trend_indicator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_correlation_trend_indicator_free( handle: *mut CorrelationTrendIndicator, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CyberneticCycle` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cybernetic_cycle_free`. #[no_mangle] pub extern "C" fn wickra_cybernetic_cycle_new(period: usize) -> *mut CyberneticCycle { match CyberneticCycle::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_cybernetic_cycle_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cybernetic_cycle_update( handle: *mut CyberneticCycle, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_cybernetic_cycle_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_cybernetic_cycle_batch( handle: *mut CyberneticCycle, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cybernetic_cycle_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cybernetic_cycle_reset(handle: *mut CyberneticCycle) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cybernetic_cycle_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cybernetic_cycle_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cybernetic_cycle_free(handle: *mut CyberneticCycle) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Decycler` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_decycler_free`. #[no_mangle] pub extern "C" fn wickra_decycler_new(period: usize) -> *mut Decycler { match Decycler::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_decycler_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_update(handle: *mut Decycler, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_decycler_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_batch( handle: *mut Decycler, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_decycler_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_reset(handle: *mut Decycler) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_decycler_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_decycler_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_free(handle: *mut Decycler) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DecyclerOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_decycler_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_decycler_oscillator_new( fast: usize, slow: usize, ) -> *mut DecyclerOscillator { match DecyclerOscillator::new(fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_decycler_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_oscillator_update( handle: *mut DecyclerOscillator, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_decycler_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_oscillator_batch( handle: *mut DecyclerOscillator, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_decycler_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_oscillator_reset(handle: *mut DecyclerOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_decycler_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_decycler_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_decycler_oscillator_free(handle: *mut DecyclerOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Dema` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_dema_free`. #[no_mangle] pub extern "C" fn wickra_dema_new(period: usize) -> *mut Dema { match Dema::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_dema_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dema_update(handle: *mut Dema, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_dema_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_dema_batch( handle: *mut Dema, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_dema_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dema_reset(handle: *mut Dema) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_dema_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_dema_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dema_free(handle: *mut Dema) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DerivativeOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_derivative_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_derivative_oscillator_new( rsi_period: usize, smooth1: usize, smooth2: usize, signal_period: usize, ) -> *mut DerivativeOscillator { match DerivativeOscillator::new(rsi_period, smooth1, smooth2, signal_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_derivative_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_derivative_oscillator_update( handle: *mut DerivativeOscillator, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_derivative_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_derivative_oscillator_batch( handle: *mut DerivativeOscillator, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_derivative_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_derivative_oscillator_reset(handle: *mut DerivativeOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_derivative_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_derivative_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_derivative_oscillator_free(handle: *mut DerivativeOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DetrendedStdDev` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_detrended_std_dev_free`. #[no_mangle] pub extern "C" fn wickra_detrended_std_dev_new(period: usize) -> *mut DetrendedStdDev { match DetrendedStdDev::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_detrended_std_dev_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_detrended_std_dev_update( handle: *mut DetrendedStdDev, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_detrended_std_dev_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_detrended_std_dev_batch( handle: *mut DetrendedStdDev, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_detrended_std_dev_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_detrended_std_dev_reset(handle: *mut DetrendedStdDev) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_detrended_std_dev_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_detrended_std_dev_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_detrended_std_dev_free(handle: *mut DetrendedStdDev) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DisparityIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_disparity_index_free`. #[no_mangle] pub extern "C" fn wickra_disparity_index_new(period: usize) -> *mut DisparityIndex { match DisparityIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_disparity_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_disparity_index_update( handle: *mut DisparityIndex, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_disparity_index_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_disparity_index_batch( handle: *mut DisparityIndex, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_disparity_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_disparity_index_reset(handle: *mut DisparityIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_disparity_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_disparity_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_disparity_index_free(handle: *mut DisparityIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Dpo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_dpo_free`. #[no_mangle] pub extern "C" fn wickra_dpo_new(period: usize) -> *mut Dpo { match Dpo::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_dpo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dpo_update(handle: *mut Dpo, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_dpo_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_dpo_batch( handle: *mut Dpo, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_dpo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dpo_reset(handle: *mut Dpo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_dpo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_dpo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dpo_free(handle: *mut Dpo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DrawdownDuration` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_drawdown_duration_free`. #[no_mangle] pub extern "C" fn wickra_drawdown_duration_new() -> *mut DrawdownDuration { Box::into_raw(Box::new(DrawdownDuration::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_drawdown_duration_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_drawdown_duration_update( handle: *mut DrawdownDuration, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).map_or(f64::NAN, f64::from), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_drawdown_duration_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_drawdown_duration_batch( handle: *mut DrawdownDuration, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).map_or(f64::NAN, f64::from); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_drawdown_duration_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_drawdown_duration_reset(handle: *mut DrawdownDuration) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_drawdown_duration_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_drawdown_duration_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_drawdown_duration_free(handle: *mut DrawdownDuration) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DynamicMomentumIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_dynamic_momentum_index_free`. #[no_mangle] pub extern "C" fn wickra_dynamic_momentum_index_new(period: usize) -> *mut DynamicMomentumIndex { match DynamicMomentumIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_dynamic_momentum_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dynamic_momentum_index_update( handle: *mut DynamicMomentumIndex, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_dynamic_momentum_index_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_dynamic_momentum_index_batch( handle: *mut DynamicMomentumIndex, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_dynamic_momentum_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dynamic_momentum_index_reset(handle: *mut DynamicMomentumIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_dynamic_momentum_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_dynamic_momentum_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dynamic_momentum_index_free(handle: *mut DynamicMomentumIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `EhlersStochastic` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ehlers_stochastic_free`. #[no_mangle] pub extern "C" fn wickra_ehlers_stochastic_new(period: usize) -> *mut EhlersStochastic { match EhlersStochastic::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ehlers_stochastic_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ehlers_stochastic_update( handle: *mut EhlersStochastic, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ehlers_stochastic_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ehlers_stochastic_batch( handle: *mut EhlersStochastic, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ehlers_stochastic_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ehlers_stochastic_reset(handle: *mut EhlersStochastic) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ehlers_stochastic_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ehlers_stochastic_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ehlers_stochastic_free(handle: *mut EhlersStochastic) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Ehma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ehma_free`. #[no_mangle] pub extern "C" fn wickra_ehma_new(period: usize) -> *mut Ehma { match Ehma::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ehma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ehma_update(handle: *mut Ehma, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ehma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ehma_batch( handle: *mut Ehma, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ehma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ehma_reset(handle: *mut Ehma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ehma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ehma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ehma_free(handle: *mut Ehma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ElderImpulse` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_elder_impulse_free`. #[no_mangle] pub extern "C" fn wickra_elder_impulse_new( ema_period: usize, macd_fast: usize, macd_slow: usize, macd_signal: usize, ) -> *mut ElderImpulse { match ElderImpulse::new(ema_period, macd_fast, macd_slow, macd_signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_elder_impulse_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_impulse_update(handle: *mut ElderImpulse, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_elder_impulse_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_elder_impulse_batch( handle: *mut ElderImpulse, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_elder_impulse_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_impulse_reset(handle: *mut ElderImpulse) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_elder_impulse_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_elder_impulse_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_impulse_free(handle: *mut ElderImpulse) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Ema` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ema_free`. #[no_mangle] pub extern "C" fn wickra_ema_new(period: usize) -> *mut Ema { match Ema::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ema_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ema_update(handle: *mut Ema, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ema_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ema_batch( handle: *mut Ema, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ema_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ema_reset(handle: *mut Ema) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ema_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ema_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ema_free(handle: *mut Ema) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `EmpiricalModeDecomposition` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_empirical_mode_decomposition_free`. #[no_mangle] pub extern "C" fn wickra_empirical_mode_decomposition_new( period: usize, fraction: f64, ) -> *mut EmpiricalModeDecomposition { match EmpiricalModeDecomposition::new(period, fraction) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_empirical_mode_decomposition_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_empirical_mode_decomposition_update( handle: *mut EmpiricalModeDecomposition, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_empirical_mode_decomposition_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_empirical_mode_decomposition_batch( handle: *mut EmpiricalModeDecomposition, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_empirical_mode_decomposition_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_empirical_mode_decomposition_reset( handle: *mut EmpiricalModeDecomposition, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_empirical_mode_decomposition_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_empirical_mode_decomposition_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_empirical_mode_decomposition_free( handle: *mut EmpiricalModeDecomposition, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `EvenBetterSinewave` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_even_better_sinewave_free`. #[no_mangle] pub extern "C" fn wickra_even_better_sinewave_new( hp_period: usize, ssf_length: usize, ) -> *mut EvenBetterSinewave { match EvenBetterSinewave::new(hp_period, ssf_length) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_even_better_sinewave_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_even_better_sinewave_update( handle: *mut EvenBetterSinewave, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_even_better_sinewave_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_even_better_sinewave_batch( handle: *mut EvenBetterSinewave, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_even_better_sinewave_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_even_better_sinewave_reset(handle: *mut EvenBetterSinewave) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_even_better_sinewave_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_even_better_sinewave_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_even_better_sinewave_free(handle: *mut EvenBetterSinewave) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `EwmaVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ewma_volatility_free`. #[no_mangle] pub extern "C" fn wickra_ewma_volatility_new(lambda: f64) -> *mut EwmaVolatility { match EwmaVolatility::new(lambda) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ewma_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ewma_volatility_update( handle: *mut EwmaVolatility, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ewma_volatility_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ewma_volatility_batch( handle: *mut EwmaVolatility, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ewma_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ewma_volatility_reset(handle: *mut EwmaVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ewma_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ewma_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ewma_volatility_free(handle: *mut EwmaVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Expectancy` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_expectancy_free`. #[no_mangle] pub extern "C" fn wickra_expectancy_new(period: usize) -> *mut Expectancy { match Expectancy::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_expectancy_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_expectancy_update(handle: *mut Expectancy, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_expectancy_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_expectancy_batch( handle: *mut Expectancy, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_expectancy_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_expectancy_reset(handle: *mut Expectancy) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_expectancy_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_expectancy_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_expectancy_free(handle: *mut Expectancy) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Fama` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fama_free`. #[no_mangle] pub extern "C" fn wickra_fama_new(fast_limit: f64, slow_limit: f64) -> *mut Fama { match Fama::new(fast_limit, slow_limit) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_fama_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fama_update(handle: *mut Fama, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_fama_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_fama_batch( handle: *mut Fama, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fama_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fama_reset(handle: *mut Fama) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fama_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fama_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fama_free(handle: *mut Fama) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FisherRsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fisher_rsi_free`. #[no_mangle] pub extern "C" fn wickra_fisher_rsi_new(period: usize) -> *mut FisherRsi { match FisherRsi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_fisher_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_rsi_update(handle: *mut FisherRsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_fisher_rsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_rsi_batch( handle: *mut FisherRsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fisher_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_rsi_reset(handle: *mut FisherRsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fisher_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fisher_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_rsi_free(handle: *mut FisherRsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FisherTransform` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fisher_transform_free`. #[no_mangle] pub extern "C" fn wickra_fisher_transform_new(period: usize) -> *mut FisherTransform { match FisherTransform::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_fisher_transform_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_transform_update( handle: *mut FisherTransform, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_fisher_transform_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_transform_batch( handle: *mut FisherTransform, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fisher_transform_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_transform_reset(handle: *mut FisherTransform) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fisher_transform_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fisher_transform_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fisher_transform_free(handle: *mut FisherTransform) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Frama` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_frama_free`. #[no_mangle] pub extern "C" fn wickra_frama_new(period: usize) -> *mut Frama { match Frama::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_frama_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_frama_update(handle: *mut Frama, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_frama_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_frama_batch( handle: *mut Frama, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_frama_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_frama_reset(handle: *mut Frama) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_frama_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_frama_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_frama_free(handle: *mut Frama) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GainLossRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_gain_loss_ratio_free`. #[no_mangle] pub extern "C" fn wickra_gain_loss_ratio_new(period: usize) -> *mut GainLossRatio { match GainLossRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_gain_loss_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gain_loss_ratio_update( handle: *mut GainLossRatio, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_gain_loss_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_gain_loss_ratio_batch( handle: *mut GainLossRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_gain_loss_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gain_loss_ratio_reset(handle: *mut GainLossRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_gain_loss_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_gain_loss_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gain_loss_ratio_free(handle: *mut GainLossRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GainToPainRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_gain_to_pain_ratio_free`. #[no_mangle] pub extern "C" fn wickra_gain_to_pain_ratio_new(period: usize) -> *mut GainToPainRatio { match GainToPainRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_gain_to_pain_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gain_to_pain_ratio_update( handle: *mut GainToPainRatio, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_gain_to_pain_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_gain_to_pain_ratio_batch( handle: *mut GainToPainRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_gain_to_pain_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gain_to_pain_ratio_reset(handle: *mut GainToPainRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_gain_to_pain_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_gain_to_pain_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gain_to_pain_ratio_free(handle: *mut GainToPainRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Garch11` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_garch11_free`. #[no_mangle] pub extern "C" fn wickra_garch11_new(omega: f64, alpha: f64, beta: f64) -> *mut Garch11 { match Garch11::new(omega, alpha, beta) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_garch11_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_garch11_update(handle: *mut Garch11, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_garch11_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_garch11_batch( handle: *mut Garch11, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_garch11_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_garch11_reset(handle: *mut Garch11) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_garch11_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_garch11_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_garch11_free(handle: *mut Garch11) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GeneralizedDema` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_generalized_dema_free`. #[no_mangle] pub extern "C" fn wickra_generalized_dema_new(period: usize, v: f64) -> *mut GeneralizedDema { match GeneralizedDema::new(period, v) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_generalized_dema_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_generalized_dema_update( handle: *mut GeneralizedDema, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_generalized_dema_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_generalized_dema_batch( handle: *mut GeneralizedDema, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_generalized_dema_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_generalized_dema_reset(handle: *mut GeneralizedDema) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_generalized_dema_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_generalized_dema_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_generalized_dema_free(handle: *mut GeneralizedDema) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GeometricMa` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_geometric_ma_free`. #[no_mangle] pub extern "C" fn wickra_geometric_ma_new(period: usize) -> *mut GeometricMa { match GeometricMa::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_geometric_ma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_geometric_ma_update(handle: *mut GeometricMa, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_geometric_ma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_geometric_ma_batch( handle: *mut GeometricMa, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_geometric_ma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_geometric_ma_reset(handle: *mut GeometricMa) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_geometric_ma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_geometric_ma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_geometric_ma_free(handle: *mut GeometricMa) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HighpassFilter` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_highpass_filter_free`. #[no_mangle] pub extern "C" fn wickra_highpass_filter_new(period: usize) -> *mut HighpassFilter { match HighpassFilter::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_highpass_filter_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_highpass_filter_update( handle: *mut HighpassFilter, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_highpass_filter_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_highpass_filter_batch( handle: *mut HighpassFilter, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_highpass_filter_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_highpass_filter_reset(handle: *mut HighpassFilter) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_highpass_filter_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_highpass_filter_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_highpass_filter_free(handle: *mut HighpassFilter) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HilbertDominantCycle` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hilbert_dominant_cycle_free`. #[no_mangle] pub extern "C" fn wickra_hilbert_dominant_cycle_new() -> *mut HilbertDominantCycle { Box::into_raw(Box::new(HilbertDominantCycle::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_hilbert_dominant_cycle_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_update( handle: *mut HilbertDominantCycle, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_hilbert_dominant_cycle_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_batch( handle: *mut HilbertDominantCycle, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hilbert_dominant_cycle_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_reset(handle: *mut HilbertDominantCycle) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hilbert_dominant_cycle_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hilbert_dominant_cycle_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hilbert_dominant_cycle_free(handle: *mut HilbertDominantCycle) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HistoricalVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_historical_volatility_free`. #[no_mangle] pub extern "C" fn wickra_historical_volatility_new( period: usize, trading_periods: usize, ) -> *mut HistoricalVolatility { match HistoricalVolatility::new(period, trading_periods) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_historical_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_historical_volatility_update( handle: *mut HistoricalVolatility, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_historical_volatility_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_historical_volatility_batch( handle: *mut HistoricalVolatility, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_historical_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_historical_volatility_reset(handle: *mut HistoricalVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_historical_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_historical_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_historical_volatility_free(handle: *mut HistoricalVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Hma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hma_free`. #[no_mangle] pub extern "C" fn wickra_hma_new(period: usize) -> *mut Hma { match Hma::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_hma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hma_update(handle: *mut Hma, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_hma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_hma_batch( handle: *mut Hma, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hma_reset(handle: *mut Hma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hma_free(handle: *mut Hma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HoltWinters` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_holt_winters_free`. #[no_mangle] pub extern "C" fn wickra_holt_winters_new(alpha: f64, beta: f64) -> *mut HoltWinters { match HoltWinters::new(alpha, beta) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_holt_winters_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_holt_winters_update(handle: *mut HoltWinters, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_holt_winters_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_holt_winters_batch( handle: *mut HoltWinters, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_holt_winters_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_holt_winters_reset(handle: *mut HoltWinters) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_holt_winters_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_holt_winters_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_holt_winters_free(handle: *mut HoltWinters) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HtDcPhase` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ht_dc_phase_free`. #[no_mangle] pub extern "C" fn wickra_ht_dc_phase_new() -> *mut HtDcPhase { Box::into_raw(Box::new(HtDcPhase::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ht_dc_phase_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_dc_phase_update(handle: *mut HtDcPhase, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ht_dc_phase_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ht_dc_phase_batch( handle: *mut HtDcPhase, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ht_dc_phase_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_dc_phase_reset(handle: *mut HtDcPhase) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ht_dc_phase_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ht_dc_phase_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_dc_phase_free(handle: *mut HtDcPhase) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HtTrendMode` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ht_trend_mode_free`. #[no_mangle] pub extern "C" fn wickra_ht_trend_mode_new() -> *mut HtTrendMode { Box::into_raw(Box::new(HtTrendMode::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ht_trend_mode_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_trend_mode_update(handle: *mut HtTrendMode, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ht_trend_mode_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ht_trend_mode_batch( handle: *mut HtTrendMode, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ht_trend_mode_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_trend_mode_reset(handle: *mut HtTrendMode) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ht_trend_mode_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ht_trend_mode_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_trend_mode_free(handle: *mut HtTrendMode) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HurstExponent` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hurst_exponent_free`. #[no_mangle] pub extern "C" fn wickra_hurst_exponent_new(period: usize, chunks: usize) -> *mut HurstExponent { match HurstExponent::new(period, chunks) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_hurst_exponent_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hurst_exponent_update( handle: *mut HurstExponent, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_hurst_exponent_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_hurst_exponent_batch( handle: *mut HurstExponent, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hurst_exponent_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hurst_exponent_reset(handle: *mut HurstExponent) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hurst_exponent_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hurst_exponent_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hurst_exponent_free(handle: *mut HurstExponent) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `InstantaneousTrendline` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_instantaneous_trendline_free`. #[no_mangle] pub extern "C" fn wickra_instantaneous_trendline_new(period: usize) -> *mut InstantaneousTrendline { match InstantaneousTrendline::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_instantaneous_trendline_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_instantaneous_trendline_update( handle: *mut InstantaneousTrendline, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_instantaneous_trendline_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_instantaneous_trendline_batch( handle: *mut InstantaneousTrendline, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_instantaneous_trendline_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_instantaneous_trendline_reset(handle: *mut InstantaneousTrendline) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_instantaneous_trendline_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_instantaneous_trendline_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_instantaneous_trendline_free(handle: *mut InstantaneousTrendline) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `InverseFisherTransform` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_inverse_fisher_transform_free`. #[no_mangle] pub extern "C" fn wickra_inverse_fisher_transform_new(scale: f64) -> *mut InverseFisherTransform { match InverseFisherTransform::new(scale) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_inverse_fisher_transform_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inverse_fisher_transform_update( handle: *mut InverseFisherTransform, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_inverse_fisher_transform_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_inverse_fisher_transform_batch( handle: *mut InverseFisherTransform, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_inverse_fisher_transform_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inverse_fisher_transform_reset( handle: *mut InverseFisherTransform, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_inverse_fisher_transform_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_inverse_fisher_transform_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inverse_fisher_transform_free(handle: *mut InverseFisherTransform) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `JarqueBera` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_jarque_bera_free`. #[no_mangle] pub extern "C" fn wickra_jarque_bera_new(period: usize) -> *mut JarqueBera { match JarqueBera::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_jarque_bera_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jarque_bera_update(handle: *mut JarqueBera, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_jarque_bera_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_jarque_bera_batch( handle: *mut JarqueBera, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_jarque_bera_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jarque_bera_reset(handle: *mut JarqueBera) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_jarque_bera_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_jarque_bera_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jarque_bera_free(handle: *mut JarqueBera) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Jma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_jma_free`. #[no_mangle] pub extern "C" fn wickra_jma_new(period: usize, phase: f64, power: u32) -> *mut Jma { match Jma::new(period, phase, power) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_jma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jma_update(handle: *mut Jma, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_jma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_jma_batch( handle: *mut Jma, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_jma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jma_reset(handle: *mut Jma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_jma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_jma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jma_free(handle: *mut Jma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `JumpIndicator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_jump_indicator_free`. #[no_mangle] pub extern "C" fn wickra_jump_indicator_new(period: usize, threshold: f64) -> *mut JumpIndicator { match JumpIndicator::new(period, threshold) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_jump_indicator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jump_indicator_update( handle: *mut JumpIndicator, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_jump_indicator_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_jump_indicator_batch( handle: *mut JumpIndicator, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_jump_indicator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jump_indicator_reset(handle: *mut JumpIndicator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_jump_indicator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_jump_indicator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_jump_indicator_free(handle: *mut JumpIndicator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_k_ratio_free`. #[no_mangle] pub extern "C" fn wickra_k_ratio_new(period: usize) -> *mut KRatio { match KRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_k_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_k_ratio_update(handle: *mut KRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_k_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_k_ratio_batch( handle: *mut KRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_k_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_k_ratio_reset(handle: *mut KRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_k_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_k_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_k_ratio_free(handle: *mut KRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Kama` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kama_free`. #[no_mangle] pub extern "C" fn wickra_kama_new(er_period: usize, fast: usize, slow: usize) -> *mut Kama { match Kama::new(er_period, fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_kama_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kama_update(handle: *mut Kama, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_kama_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_kama_batch( handle: *mut Kama, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kama_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kama_reset(handle: *mut Kama) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kama_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kama_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kama_free(handle: *mut Kama) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KellyCriterion` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kelly_criterion_free`. #[no_mangle] pub extern "C" fn wickra_kelly_criterion_new(period: usize) -> *mut KellyCriterion { match KellyCriterion::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_kelly_criterion_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kelly_criterion_update( handle: *mut KellyCriterion, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_kelly_criterion_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_kelly_criterion_batch( handle: *mut KellyCriterion, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kelly_criterion_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kelly_criterion_reset(handle: *mut KellyCriterion) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kelly_criterion_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kelly_criterion_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kelly_criterion_free(handle: *mut KellyCriterion) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Kurtosis` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kurtosis_free`. #[no_mangle] pub extern "C" fn wickra_kurtosis_new(period: usize) -> *mut Kurtosis { match Kurtosis::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_kurtosis_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kurtosis_update(handle: *mut Kurtosis, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_kurtosis_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_kurtosis_batch( handle: *mut Kurtosis, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kurtosis_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kurtosis_reset(handle: *mut Kurtosis) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kurtosis_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kurtosis_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kurtosis_free(handle: *mut Kurtosis) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LaguerreRsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_laguerre_rsi_free`. #[no_mangle] pub extern "C" fn wickra_laguerre_rsi_new(gamma: f64) -> *mut LaguerreRsi { match LaguerreRsi::new(gamma) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_laguerre_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_laguerre_rsi_update(handle: *mut LaguerreRsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_laguerre_rsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_laguerre_rsi_batch( handle: *mut LaguerreRsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_laguerre_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_laguerre_rsi_reset(handle: *mut LaguerreRsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_laguerre_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_laguerre_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_laguerre_rsi_free(handle: *mut LaguerreRsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LinearRegression` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_linear_regression_free`. #[no_mangle] pub extern "C" fn wickra_linear_regression_new(period: usize) -> *mut LinearRegression { match LinearRegression::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_linear_regression_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_linear_regression_update( handle: *mut LinearRegression, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_linear_regression_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_linear_regression_batch( handle: *mut LinearRegression, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_linear_regression_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_linear_regression_reset(handle: *mut LinearRegression) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_linear_regression_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_linear_regression_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_linear_regression_free(handle: *mut LinearRegression) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LinRegAngle` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_angle_free`. #[no_mangle] pub extern "C" fn wickra_lin_reg_angle_new(period: usize) -> *mut LinRegAngle { match LinRegAngle::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_lin_reg_angle_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_angle_update(handle: *mut LinRegAngle, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_lin_reg_angle_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_angle_batch( handle: *mut LinRegAngle, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_lin_reg_angle_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_angle_reset(handle: *mut LinRegAngle) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_lin_reg_angle_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_lin_reg_angle_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_angle_free(handle: *mut LinRegAngle) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LinRegIntercept` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_intercept_free`. #[no_mangle] pub extern "C" fn wickra_lin_reg_intercept_new(period: usize) -> *mut LinRegIntercept { match LinRegIntercept::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_lin_reg_intercept_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_intercept_update( handle: *mut LinRegIntercept, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_lin_reg_intercept_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_intercept_batch( handle: *mut LinRegIntercept, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_lin_reg_intercept_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_intercept_reset(handle: *mut LinRegIntercept) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_lin_reg_intercept_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_lin_reg_intercept_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_intercept_free(handle: *mut LinRegIntercept) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LinRegSlope` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_slope_free`. #[no_mangle] pub extern "C" fn wickra_lin_reg_slope_new(period: usize) -> *mut LinRegSlope { match LinRegSlope::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_lin_reg_slope_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_slope_update(handle: *mut LinRegSlope, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_lin_reg_slope_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_slope_batch( handle: *mut LinRegSlope, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_lin_reg_slope_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_slope_reset(handle: *mut LinRegSlope) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_lin_reg_slope_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_lin_reg_slope_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_slope_free(handle: *mut LinRegSlope) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LogReturn` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_log_return_free`. #[no_mangle] pub extern "C" fn wickra_log_return_new(period: usize) -> *mut LogReturn { match LogReturn::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_log_return_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_log_return_update(handle: *mut LogReturn, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_log_return_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_log_return_batch( handle: *mut LogReturn, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_log_return_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_log_return_reset(handle: *mut LogReturn) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_log_return_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_log_return_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_log_return_free(handle: *mut LogReturn) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `M2Measure` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_m2_measure_free`. #[no_mangle] pub extern "C" fn wickra_m2_measure_new( period: usize, risk_free: f64, benchmark_stddev: f64, ) -> *mut M2Measure { match M2Measure::new(period, risk_free, benchmark_stddev) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_m2_measure_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_m2_measure_update(handle: *mut M2Measure, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_m2_measure_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_m2_measure_batch( handle: *mut M2Measure, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_m2_measure_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_m2_measure_reset(handle: *mut M2Measure) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_m2_measure_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_m2_measure_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_m2_measure_free(handle: *mut M2Measure) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MacdHistogram` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_macd_histogram_free`. #[no_mangle] pub extern "C" fn wickra_macd_histogram_new( fast: usize, slow: usize, signal: usize, ) -> *mut MacdHistogram { match MacdHistogram::new(fast, slow, signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_macd_histogram_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_histogram_update( handle: *mut MacdHistogram, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_macd_histogram_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_macd_histogram_batch( handle: *mut MacdHistogram, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_macd_histogram_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_histogram_reset(handle: *mut MacdHistogram) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_macd_histogram_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_macd_histogram_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_histogram_free(handle: *mut MacdHistogram) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MartinRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_martin_ratio_free`. #[no_mangle] pub extern "C" fn wickra_martin_ratio_new(period: usize) -> *mut MartinRatio { match MartinRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_martin_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_martin_ratio_update(handle: *mut MartinRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_martin_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_martin_ratio_batch( handle: *mut MartinRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_martin_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_martin_ratio_reset(handle: *mut MartinRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_martin_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_martin_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_martin_ratio_free(handle: *mut MartinRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MaxDrawdown` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_max_drawdown_free`. #[no_mangle] pub extern "C" fn wickra_max_drawdown_new(period: usize) -> *mut MaxDrawdown { match MaxDrawdown::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_max_drawdown_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_max_drawdown_update(handle: *mut MaxDrawdown, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_max_drawdown_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_max_drawdown_batch( handle: *mut MaxDrawdown, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_max_drawdown_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_max_drawdown_reset(handle: *mut MaxDrawdown) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_max_drawdown_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_max_drawdown_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_max_drawdown_free(handle: *mut MaxDrawdown) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `McGinleyDynamic` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mc_ginley_dynamic_free`. #[no_mangle] pub extern "C" fn wickra_mc_ginley_dynamic_new(period: usize) -> *mut McGinleyDynamic { match McGinleyDynamic::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_mc_ginley_dynamic_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mc_ginley_dynamic_update( handle: *mut McGinleyDynamic, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_mc_ginley_dynamic_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_mc_ginley_dynamic_batch( handle: *mut McGinleyDynamic, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mc_ginley_dynamic_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mc_ginley_dynamic_reset(handle: *mut McGinleyDynamic) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mc_ginley_dynamic_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mc_ginley_dynamic_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mc_ginley_dynamic_free(handle: *mut McGinleyDynamic) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MedianAbsoluteDeviation` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_median_absolute_deviation_free`. #[no_mangle] pub extern "C" fn wickra_median_absolute_deviation_new( period: usize, ) -> *mut MedianAbsoluteDeviation { match MedianAbsoluteDeviation::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_median_absolute_deviation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_absolute_deviation_update( handle: *mut MedianAbsoluteDeviation, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_median_absolute_deviation_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_median_absolute_deviation_batch( handle: *mut MedianAbsoluteDeviation, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_median_absolute_deviation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_absolute_deviation_reset( handle: *mut MedianAbsoluteDeviation, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_median_absolute_deviation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_median_absolute_deviation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_absolute_deviation_free( handle: *mut MedianAbsoluteDeviation, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MedianMa` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_median_ma_free`. #[no_mangle] pub extern "C" fn wickra_median_ma_new(period: usize) -> *mut MedianMa { match MedianMa::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_median_ma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_ma_update(handle: *mut MedianMa, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_median_ma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_median_ma_batch( handle: *mut MedianMa, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_median_ma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_ma_reset(handle: *mut MedianMa) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_median_ma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_median_ma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_ma_free(handle: *mut MedianMa) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MidPoint` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mid_point_free`. #[no_mangle] pub extern "C" fn wickra_mid_point_new(period: usize) -> *mut MidPoint { match MidPoint::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_mid_point_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mid_point_update(handle: *mut MidPoint, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_mid_point_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_mid_point_batch( handle: *mut MidPoint, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mid_point_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mid_point_reset(handle: *mut MidPoint) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mid_point_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mid_point_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mid_point_free(handle: *mut MidPoint) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Mom` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mom_free`. #[no_mangle] pub extern "C" fn wickra_mom_new(period: usize) -> *mut Mom { match Mom::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_mom_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mom_update(handle: *mut Mom, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_mom_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_mom_batch( handle: *mut Mom, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mom_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mom_reset(handle: *mut Mom) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mom_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mom_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mom_free(handle: *mut Mom) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OmegaRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_omega_ratio_free`. #[no_mangle] pub extern "C" fn wickra_omega_ratio_new(period: usize, threshold: f64) -> *mut OmegaRatio { match OmegaRatio::new(period, threshold) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_omega_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_omega_ratio_update(handle: *mut OmegaRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_omega_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_omega_ratio_batch( handle: *mut OmegaRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_omega_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_omega_ratio_reset(handle: *mut OmegaRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_omega_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_omega_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_omega_ratio_free(handle: *mut OmegaRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PainIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pain_index_free`. #[no_mangle] pub extern "C" fn wickra_pain_index_new(period: usize) -> *mut PainIndex { match PainIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_pain_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pain_index_update(handle: *mut PainIndex, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_pain_index_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_pain_index_batch( handle: *mut PainIndex, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pain_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pain_index_reset(handle: *mut PainIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pain_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pain_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pain_index_free(handle: *mut PainIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PercentB` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_percent_b_free`. #[no_mangle] pub extern "C" fn wickra_percent_b_new(period: usize, multiplier: f64) -> *mut PercentB { match PercentB::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_percent_b_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percent_b_update(handle: *mut PercentB, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_percent_b_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_percent_b_batch( handle: *mut PercentB, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_percent_b_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percent_b_reset(handle: *mut PercentB) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_percent_b_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_percent_b_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percent_b_free(handle: *mut PercentB) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PercentageTrailingStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_percentage_trailing_stop_free`. #[no_mangle] pub extern "C" fn wickra_percentage_trailing_stop_new(percent: f64) -> *mut PercentageTrailingStop { match PercentageTrailingStop::new(percent) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_percentage_trailing_stop_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percentage_trailing_stop_update( handle: *mut PercentageTrailingStop, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_percentage_trailing_stop_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_percentage_trailing_stop_batch( handle: *mut PercentageTrailingStop, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_percentage_trailing_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percentage_trailing_stop_reset( handle: *mut PercentageTrailingStop, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_percentage_trailing_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_percentage_trailing_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percentage_trailing_stop_free(handle: *mut PercentageTrailingStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Pmo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pmo_free`. #[no_mangle] pub extern "C" fn wickra_pmo_new(smoothing1: usize, smoothing2: usize) -> *mut Pmo { match Pmo::new(smoothing1, smoothing2) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_pmo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pmo_update(handle: *mut Pmo, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_pmo_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_pmo_batch( handle: *mut Pmo, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pmo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pmo_reset(handle: *mut Pmo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pmo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pmo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pmo_free(handle: *mut Pmo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PolarizedFractalEfficiency` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_polarized_fractal_efficiency_free`. #[no_mangle] pub extern "C" fn wickra_polarized_fractal_efficiency_new( period: usize, smoothing: usize, ) -> *mut PolarizedFractalEfficiency { match PolarizedFractalEfficiency::new(period, smoothing) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_polarized_fractal_efficiency_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_update( handle: *mut PolarizedFractalEfficiency, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_polarized_fractal_efficiency_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_batch( handle: *mut PolarizedFractalEfficiency, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_polarized_fractal_efficiency_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_reset( handle: *mut PolarizedFractalEfficiency, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_polarized_fractal_efficiency_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_polarized_fractal_efficiency_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_polarized_fractal_efficiency_free( handle: *mut PolarizedFractalEfficiency, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Ppo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ppo_free`. #[no_mangle] pub extern "C" fn wickra_ppo_new(fast: usize, slow: usize) -> *mut Ppo { match Ppo::new(fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ppo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_update(handle: *mut Ppo, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ppo_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_batch( handle: *mut Ppo, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ppo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_reset(handle: *mut Ppo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ppo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ppo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_free(handle: *mut Ppo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PpoHistogram` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ppo_histogram_free`. #[no_mangle] pub extern "C" fn wickra_ppo_histogram_new( fast: usize, slow: usize, signal: usize, ) -> *mut PpoHistogram { match PpoHistogram::new(fast, slow, signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ppo_histogram_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_histogram_update(handle: *mut PpoHistogram, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ppo_histogram_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_histogram_batch( handle: *mut PpoHistogram, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ppo_histogram_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_histogram_reset(handle: *mut PpoHistogram) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ppo_histogram_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ppo_histogram_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ppo_histogram_free(handle: *mut PpoHistogram) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ProfitFactor` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_profit_factor_free`. #[no_mangle] pub extern "C" fn wickra_profit_factor_new(period: usize) -> *mut ProfitFactor { match ProfitFactor::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_profit_factor_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_profit_factor_update(handle: *mut ProfitFactor, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_profit_factor_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_profit_factor_batch( handle: *mut ProfitFactor, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_profit_factor_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_profit_factor_reset(handle: *mut ProfitFactor) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_profit_factor_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_profit_factor_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_profit_factor_free(handle: *mut ProfitFactor) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RSquared` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_r_squared_free`. #[no_mangle] pub extern "C" fn wickra_r_squared_new(period: usize) -> *mut RSquared { match RSquared::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_r_squared_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_r_squared_update(handle: *mut RSquared, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_r_squared_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_r_squared_batch( handle: *mut RSquared, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_r_squared_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_r_squared_reset(handle: *mut RSquared) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_r_squared_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_r_squared_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_r_squared_free(handle: *mut RSquared) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RealizedVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_realized_volatility_free`. #[no_mangle] pub extern "C" fn wickra_realized_volatility_new(period: usize) -> *mut RealizedVolatility { match RealizedVolatility::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_realized_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_realized_volatility_update( handle: *mut RealizedVolatility, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_realized_volatility_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_realized_volatility_batch( handle: *mut RealizedVolatility, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_realized_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_realized_volatility_reset(handle: *mut RealizedVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_realized_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_realized_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_realized_volatility_free(handle: *mut RealizedVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RecoveryFactor` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_recovery_factor_free`. #[no_mangle] pub extern "C" fn wickra_recovery_factor_new() -> *mut RecoveryFactor { Box::into_raw(Box::new(RecoveryFactor::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_recovery_factor_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_recovery_factor_update( handle: *mut RecoveryFactor, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_recovery_factor_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_recovery_factor_batch( handle: *mut RecoveryFactor, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_recovery_factor_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_recovery_factor_reset(handle: *mut RecoveryFactor) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_recovery_factor_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_recovery_factor_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_recovery_factor_free(handle: *mut RecoveryFactor) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Reflex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_reflex_free`. #[no_mangle] pub extern "C" fn wickra_reflex_new(period: usize) -> *mut Reflex { match Reflex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_reflex_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_reflex_update(handle: *mut Reflex, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_reflex_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_reflex_batch( handle: *mut Reflex, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_reflex_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_reflex_reset(handle: *mut Reflex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_reflex_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_reflex_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_reflex_free(handle: *mut Reflex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RegimeLabel` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_regime_label_free`. #[no_mangle] pub extern "C" fn wickra_regime_label_new(vol_period: usize, lookback: usize) -> *mut RegimeLabel { match RegimeLabel::new(vol_period, lookback) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_regime_label_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_regime_label_update(handle: *mut RegimeLabel, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_regime_label_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_regime_label_batch( handle: *mut RegimeLabel, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_regime_label_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_regime_label_reset(handle: *mut RegimeLabel) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_regime_label_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_regime_label_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_regime_label_free(handle: *mut RegimeLabel) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RenkoTrailingStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_renko_trailing_stop_free`. #[no_mangle] pub extern "C" fn wickra_renko_trailing_stop_new(block_size: f64) -> *mut RenkoTrailingStop { match RenkoTrailingStop::new(block_size) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_renko_trailing_stop_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_renko_trailing_stop_update( handle: *mut RenkoTrailingStop, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_renko_trailing_stop_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_renko_trailing_stop_batch( handle: *mut RenkoTrailingStop, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_renko_trailing_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_renko_trailing_stop_reset(handle: *mut RenkoTrailingStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_renko_trailing_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_renko_trailing_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_renko_trailing_stop_free(handle: *mut RenkoTrailingStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rmi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rmi_free`. #[no_mangle] pub extern "C" fn wickra_rmi_new(period: usize, momentum: usize) -> *mut Rmi { match Rmi::new(period, momentum) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rmi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rmi_update(handle: *mut Rmi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rmi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rmi_batch( handle: *mut Rmi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rmi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rmi_reset(handle: *mut Rmi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rmi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rmi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rmi_free(handle: *mut Rmi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Roc` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_roc_free`. #[no_mangle] pub extern "C" fn wickra_roc_new(period: usize) -> *mut Roc { match Roc::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_roc_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roc_update(handle: *mut Roc, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_roc_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_roc_batch( handle: *mut Roc, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_roc_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roc_reset(handle: *mut Roc) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_roc_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_roc_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roc_free(handle: *mut Roc) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rocp` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rocp_free`. #[no_mangle] pub extern "C" fn wickra_rocp_new(period: usize) -> *mut Rocp { match Rocp::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rocp_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocp_update(handle: *mut Rocp, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rocp_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rocp_batch( handle: *mut Rocp, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rocp_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocp_reset(handle: *mut Rocp) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rocp_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rocp_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocp_free(handle: *mut Rocp) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rocr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rocr_free`. #[no_mangle] pub extern "C" fn wickra_rocr_new(period: usize) -> *mut Rocr { match Rocr::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rocr_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocr_update(handle: *mut Rocr, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rocr_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rocr_batch( handle: *mut Rocr, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rocr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocr_reset(handle: *mut Rocr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rocr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rocr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocr_free(handle: *mut Rocr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rocr100` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rocr100_free`. #[no_mangle] pub extern "C" fn wickra_rocr100_new(period: usize) -> *mut Rocr100 { match Rocr100::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rocr100_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocr100_update(handle: *mut Rocr100, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rocr100_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rocr100_batch( handle: *mut Rocr100, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rocr100_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocr100_reset(handle: *mut Rocr100) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rocr100_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rocr100_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rocr100_free(handle: *mut Rocr100) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollingIqr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rolling_iqr_free`. #[no_mangle] pub extern "C" fn wickra_rolling_iqr_new(period: usize) -> *mut RollingIqr { match RollingIqr::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rolling_iqr_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_iqr_update(handle: *mut RollingIqr, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rolling_iqr_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_iqr_batch( handle: *mut RollingIqr, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rolling_iqr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_iqr_reset(handle: *mut RollingIqr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rolling_iqr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rolling_iqr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_iqr_free(handle: *mut RollingIqr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollingMinMaxScaler` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rolling_min_max_scaler_free`. #[no_mangle] pub extern "C" fn wickra_rolling_min_max_scaler_new(period: usize) -> *mut RollingMinMaxScaler { match RollingMinMaxScaler::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rolling_min_max_scaler_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_min_max_scaler_update( handle: *mut RollingMinMaxScaler, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rolling_min_max_scaler_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_min_max_scaler_batch( handle: *mut RollingMinMaxScaler, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rolling_min_max_scaler_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_min_max_scaler_reset(handle: *mut RollingMinMaxScaler) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rolling_min_max_scaler_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rolling_min_max_scaler_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_min_max_scaler_free(handle: *mut RollingMinMaxScaler) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollingPercentileRank` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rolling_percentile_rank_free`. #[no_mangle] pub extern "C" fn wickra_rolling_percentile_rank_new(period: usize) -> *mut RollingPercentileRank { match RollingPercentileRank::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rolling_percentile_rank_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_percentile_rank_update( handle: *mut RollingPercentileRank, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rolling_percentile_rank_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_percentile_rank_batch( handle: *mut RollingPercentileRank, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rolling_percentile_rank_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_percentile_rank_reset(handle: *mut RollingPercentileRank) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rolling_percentile_rank_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rolling_percentile_rank_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_percentile_rank_free(handle: *mut RollingPercentileRank) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollingQuantile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rolling_quantile_free`. #[no_mangle] pub extern "C" fn wickra_rolling_quantile_new( period: usize, quantile: f64, ) -> *mut RollingQuantile { match RollingQuantile::new(period, quantile) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rolling_quantile_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_quantile_update( handle: *mut RollingQuantile, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rolling_quantile_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_quantile_batch( handle: *mut RollingQuantile, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rolling_quantile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_quantile_reset(handle: *mut RollingQuantile) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rolling_quantile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rolling_quantile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_quantile_free(handle: *mut RollingQuantile) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RoofingFilter` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_roofing_filter_free`. #[no_mangle] pub extern "C" fn wickra_roofing_filter_new( lp_period: usize, hp_period: usize, ) -> *mut RoofingFilter { match RoofingFilter::new(lp_period, hp_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_roofing_filter_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roofing_filter_update( handle: *mut RoofingFilter, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_roofing_filter_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_roofing_filter_batch( handle: *mut RoofingFilter, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_roofing_filter_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roofing_filter_reset(handle: *mut RoofingFilter) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_roofing_filter_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_roofing_filter_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roofing_filter_free(handle: *mut RoofingFilter) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rsi_free`. #[no_mangle] pub extern "C" fn wickra_rsi_new(period: usize) -> *mut Rsi { match Rsi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rsi_update(handle: *mut Rsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rsi_batch( handle: *mut Rsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rsi_reset(handle: *mut Rsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rsi_free(handle: *mut Rsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rsx` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rsx_free`. #[no_mangle] pub extern "C" fn wickra_rsx_new(length: usize) -> *mut Rsx { match Rsx::new(length) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rsx_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rsx_update(handle: *mut Rsx, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rsx_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rsx_batch( handle: *mut Rsx, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rsx_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rsx_reset(handle: *mut Rsx) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rsx_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rsx_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rsx_free(handle: *mut Rsx) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RviVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rvi_volatility_free`. #[no_mangle] pub extern "C" fn wickra_rvi_volatility_new(period: usize) -> *mut RviVolatility { match RviVolatility::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rvi_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_volatility_update( handle: *mut RviVolatility, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rvi_volatility_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_volatility_batch( handle: *mut RviVolatility, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rvi_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_volatility_reset(handle: *mut RviVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rvi_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rvi_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_volatility_free(handle: *mut RviVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SampleEntropy` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sample_entropy_free`. #[no_mangle] pub extern "C" fn wickra_sample_entropy_new( period: usize, m: usize, r_factor: f64, ) -> *mut SampleEntropy { match SampleEntropy::new(period, m, r_factor) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_sample_entropy_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sample_entropy_update( handle: *mut SampleEntropy, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_sample_entropy_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_sample_entropy_batch( handle: *mut SampleEntropy, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sample_entropy_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sample_entropy_reset(handle: *mut SampleEntropy) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sample_entropy_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sample_entropy_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sample_entropy_free(handle: *mut SampleEntropy) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ShannonEntropy` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_shannon_entropy_free`. #[no_mangle] pub extern "C" fn wickra_shannon_entropy_new(period: usize, bins: usize) -> *mut ShannonEntropy { match ShannonEntropy::new(period, bins) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_shannon_entropy_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shannon_entropy_update( handle: *mut ShannonEntropy, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_shannon_entropy_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_shannon_entropy_batch( handle: *mut ShannonEntropy, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_shannon_entropy_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shannon_entropy_reset(handle: *mut ShannonEntropy) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_shannon_entropy_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_shannon_entropy_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shannon_entropy_free(handle: *mut ShannonEntropy) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SharpeRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sharpe_ratio_free`. #[no_mangle] pub extern "C" fn wickra_sharpe_ratio_new(period: usize, risk_free: f64) -> *mut SharpeRatio { match SharpeRatio::new(period, risk_free) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_sharpe_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sharpe_ratio_update(handle: *mut SharpeRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_sharpe_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_sharpe_ratio_batch( handle: *mut SharpeRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sharpe_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sharpe_ratio_reset(handle: *mut SharpeRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sharpe_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sharpe_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sharpe_ratio_free(handle: *mut SharpeRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SineWave` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sine_wave_free`. #[no_mangle] pub extern "C" fn wickra_sine_wave_new() -> *mut SineWave { Box::into_raw(Box::new(SineWave::new())) } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_sine_wave_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sine_wave_update(handle: *mut SineWave, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_sine_wave_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_sine_wave_batch( handle: *mut SineWave, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sine_wave_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sine_wave_reset(handle: *mut SineWave) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sine_wave_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sine_wave_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sine_wave_free(handle: *mut SineWave) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SineWeightedMa` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sine_weighted_ma_free`. #[no_mangle] pub extern "C" fn wickra_sine_weighted_ma_new(period: usize) -> *mut SineWeightedMa { match SineWeightedMa::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_sine_weighted_ma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sine_weighted_ma_update( handle: *mut SineWeightedMa, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_sine_weighted_ma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_sine_weighted_ma_batch( handle: *mut SineWeightedMa, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sine_weighted_ma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sine_weighted_ma_reset(handle: *mut SineWeightedMa) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sine_weighted_ma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sine_weighted_ma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sine_weighted_ma_free(handle: *mut SineWeightedMa) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Skewness` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_skewness_free`. #[no_mangle] pub extern "C" fn wickra_skewness_new(period: usize) -> *mut Skewness { match Skewness::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_skewness_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_skewness_update(handle: *mut Skewness, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_skewness_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_skewness_batch( handle: *mut Skewness, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_skewness_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_skewness_reset(handle: *mut Skewness) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_skewness_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_skewness_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_skewness_free(handle: *mut Skewness) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Sma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sma_free`. #[no_mangle] pub extern "C" fn wickra_sma_new(period: usize) -> *mut Sma { match Sma::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_sma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sma_update(handle: *mut Sma, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_sma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_sma_batch( handle: *mut Sma, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sma_reset(handle: *mut Sma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sma_free(handle: *mut Sma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Smma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_smma_free`. #[no_mangle] pub extern "C" fn wickra_smma_new(period: usize) -> *mut Smma { match Smma::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_smma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smma_update(handle: *mut Smma, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_smma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_smma_batch( handle: *mut Smma, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_smma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smma_reset(handle: *mut Smma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_smma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_smma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smma_free(handle: *mut Smma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SortinoRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sortino_ratio_free`. #[no_mangle] pub extern "C" fn wickra_sortino_ratio_new(period: usize, mar: f64) -> *mut SortinoRatio { match SortinoRatio::new(period, mar) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_sortino_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sortino_ratio_update(handle: *mut SortinoRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_sortino_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_sortino_ratio_batch( handle: *mut SortinoRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sortino_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sortino_ratio_reset(handle: *mut SortinoRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sortino_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sortino_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sortino_ratio_free(handle: *mut SortinoRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StandardError` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_standard_error_free`. #[no_mangle] pub extern "C" fn wickra_standard_error_new(period: usize) -> *mut StandardError { match StandardError::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_standard_error_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_standard_error_update( handle: *mut StandardError, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_standard_error_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_standard_error_batch( handle: *mut StandardError, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_standard_error_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_standard_error_reset(handle: *mut StandardError) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_standard_error_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_standard_error_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_standard_error_free(handle: *mut StandardError) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Stc` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_stc_free`. #[no_mangle] pub extern "C" fn wickra_stc_new( fast: usize, slow: usize, schaff_period: usize, factor: f64, ) -> *mut Stc { match Stc::new(fast, slow, schaff_period, factor) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_stc_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stc_update(handle: *mut Stc, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_stc_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_stc_batch( handle: *mut Stc, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_stc_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stc_reset(handle: *mut Stc) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_stc_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_stc_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stc_free(handle: *mut Stc) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StdDev` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_std_dev_free`. #[no_mangle] pub extern "C" fn wickra_std_dev_new(period: usize) -> *mut StdDev { match StdDev::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_std_dev_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_std_dev_update(handle: *mut StdDev, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_std_dev_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_std_dev_batch( handle: *mut StdDev, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_std_dev_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_std_dev_reset(handle: *mut StdDev) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_std_dev_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_std_dev_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_std_dev_free(handle: *mut StdDev) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StepTrailingStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_step_trailing_stop_free`. #[no_mangle] pub extern "C" fn wickra_step_trailing_stop_new(step_size: f64) -> *mut StepTrailingStop { match StepTrailingStop::new(step_size) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_step_trailing_stop_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_step_trailing_stop_update( handle: *mut StepTrailingStop, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_step_trailing_stop_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_step_trailing_stop_batch( handle: *mut StepTrailingStop, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_step_trailing_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_step_trailing_stop_reset(handle: *mut StepTrailingStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_step_trailing_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_step_trailing_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_step_trailing_stop_free(handle: *mut StepTrailingStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SterlingRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sterling_ratio_free`. #[no_mangle] pub extern "C" fn wickra_sterling_ratio_new(period: usize) -> *mut SterlingRatio { match SterlingRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_sterling_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sterling_ratio_update( handle: *mut SterlingRatio, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_sterling_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_sterling_ratio_batch( handle: *mut SterlingRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sterling_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sterling_ratio_reset(handle: *mut SterlingRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sterling_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sterling_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sterling_ratio_free(handle: *mut SterlingRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StochRsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_stoch_rsi_free`. #[no_mangle] pub extern "C" fn wickra_stoch_rsi_new(rsi_period: usize, stoch_period: usize) -> *mut StochRsi { match StochRsi::new(rsi_period, stoch_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_stoch_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stoch_rsi_update(handle: *mut StochRsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_stoch_rsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_stoch_rsi_batch( handle: *mut StochRsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_stoch_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stoch_rsi_reset(handle: *mut StochRsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_stoch_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_stoch_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stoch_rsi_free(handle: *mut StochRsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SuperSmoother` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_super_smoother_free`. #[no_mangle] pub extern "C" fn wickra_super_smoother_new(period: usize) -> *mut SuperSmoother { match SuperSmoother::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_super_smoother_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_super_smoother_update( handle: *mut SuperSmoother, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_super_smoother_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_super_smoother_batch( handle: *mut SuperSmoother, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_super_smoother_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_super_smoother_reset(handle: *mut SuperSmoother) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_super_smoother_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_super_smoother_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_super_smoother_free(handle: *mut SuperSmoother) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `T3` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_t3_free`. #[no_mangle] pub extern "C" fn wickra_t3_new(period: usize, v: f64) -> *mut T3 { match T3::new(period, v) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_t3_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_t3_update(handle: *mut T3, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_t3_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_t3_batch( handle: *mut T3, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_t3_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_t3_reset(handle: *mut T3) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_t3_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_t3_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_t3_free(handle: *mut T3) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TailRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tail_ratio_free`. #[no_mangle] pub extern "C" fn wickra_tail_ratio_new(period: usize) -> *mut TailRatio { match TailRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_tail_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tail_ratio_update(handle: *mut TailRatio, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_tail_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_tail_ratio_batch( handle: *mut TailRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tail_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tail_ratio_reset(handle: *mut TailRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tail_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tail_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tail_ratio_free(handle: *mut TailRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Tema` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tema_free`. #[no_mangle] pub extern "C" fn wickra_tema_new(period: usize) -> *mut Tema { match Tema::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_tema_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tema_update(handle: *mut Tema, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_tema_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_tema_batch( handle: *mut Tema, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tema_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tema_reset(handle: *mut Tema) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tema_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tema_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tema_free(handle: *mut Tema) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Tii` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tii_free`. #[no_mangle] pub extern "C" fn wickra_tii_new(sma_period: usize, dev_period: usize) -> *mut Tii { match Tii::new(sma_period, dev_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_tii_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tii_update(handle: *mut Tii, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_tii_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_tii_batch( handle: *mut Tii, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tii_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tii_reset(handle: *mut Tii) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tii_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tii_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tii_free(handle: *mut Tii) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TrendLabel` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trend_label_free`. #[no_mangle] pub extern "C" fn wickra_trend_label_new(period: usize) -> *mut TrendLabel { match TrendLabel::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_trend_label_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trend_label_update(handle: *mut TrendLabel, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_trend_label_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_trend_label_batch( handle: *mut TrendLabel, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trend_label_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trend_label_reset(handle: *mut TrendLabel) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trend_label_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trend_label_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trend_label_free(handle: *mut TrendLabel) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TrendStrengthIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trend_strength_index_free`. #[no_mangle] pub extern "C" fn wickra_trend_strength_index_new(period: usize) -> *mut TrendStrengthIndex { match TrendStrengthIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_trend_strength_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trend_strength_index_update( handle: *mut TrendStrengthIndex, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_trend_strength_index_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_trend_strength_index_batch( handle: *mut TrendStrengthIndex, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trend_strength_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trend_strength_index_reset(handle: *mut TrendStrengthIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trend_strength_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trend_strength_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trend_strength_index_free(handle: *mut TrendStrengthIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Trendflex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trendflex_free`. #[no_mangle] pub extern "C" fn wickra_trendflex_new(period: usize) -> *mut Trendflex { match Trendflex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_trendflex_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trendflex_update(handle: *mut Trendflex, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_trendflex_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_trendflex_batch( handle: *mut Trendflex, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trendflex_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trendflex_reset(handle: *mut Trendflex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trendflex_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trendflex_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trendflex_free(handle: *mut Trendflex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Trima` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trima_free`. #[no_mangle] pub extern "C" fn wickra_trima_new(period: usize) -> *mut Trima { match Trima::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_trima_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trima_update(handle: *mut Trima, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_trima_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_trima_batch( handle: *mut Trima, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trima_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trima_reset(handle: *mut Trima) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trima_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trima_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trima_free(handle: *mut Trima) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Trix` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trix_free`. #[no_mangle] pub extern "C" fn wickra_trix_new(period: usize) -> *mut Trix { match Trix::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_trix_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trix_update(handle: *mut Trix, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_trix_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_trix_batch( handle: *mut Trix, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trix_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trix_reset(handle: *mut Trix) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trix_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trix_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trix_free(handle: *mut Trix) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Tsf` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tsf_free`. #[no_mangle] pub extern "C" fn wickra_tsf_new(period: usize) -> *mut Tsf { match Tsf::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_tsf_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_update(handle: *mut Tsf, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_tsf_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_batch( handle: *mut Tsf, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tsf_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_reset(handle: *mut Tsf) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tsf_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tsf_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_free(handle: *mut Tsf) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TsfOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tsf_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_tsf_oscillator_new(period: usize) -> *mut TsfOscillator { match TsfOscillator::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_tsf_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_oscillator_update( handle: *mut TsfOscillator, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_tsf_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_oscillator_batch( handle: *mut TsfOscillator, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tsf_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_oscillator_reset(handle: *mut TsfOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tsf_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tsf_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsf_oscillator_free(handle: *mut TsfOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Tsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tsi_free`. #[no_mangle] pub extern "C" fn wickra_tsi_new(long: usize, short: usize) -> *mut Tsi { match Tsi::new(long, short) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_tsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsi_update(handle: *mut Tsi, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_tsi_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_tsi_batch( handle: *mut Tsi, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsi_reset(handle: *mut Tsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsi_free(handle: *mut Tsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UlcerIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ulcer_index_free`. #[no_mangle] pub extern "C" fn wickra_ulcer_index_new(period: usize) -> *mut UlcerIndex { match UlcerIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ulcer_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ulcer_index_update(handle: *mut UlcerIndex, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ulcer_index_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ulcer_index_batch( handle: *mut UlcerIndex, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ulcer_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ulcer_index_reset(handle: *mut UlcerIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ulcer_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ulcer_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ulcer_index_free(handle: *mut UlcerIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UniversalOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_universal_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_universal_oscillator_new(period: usize) -> *mut UniversalOscillator { match UniversalOscillator::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_universal_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_universal_oscillator_update( handle: *mut UniversalOscillator, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_universal_oscillator_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_universal_oscillator_batch( handle: *mut UniversalOscillator, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_universal_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_universal_oscillator_reset(handle: *mut UniversalOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_universal_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_universal_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_universal_oscillator_free(handle: *mut UniversalOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UpsidePotentialRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_upside_potential_ratio_free`. #[no_mangle] pub extern "C" fn wickra_upside_potential_ratio_new( period: usize, mar: f64, ) -> *mut UpsidePotentialRatio { match UpsidePotentialRatio::new(period, mar) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_upside_potential_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_potential_ratio_update( handle: *mut UpsidePotentialRatio, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_upside_potential_ratio_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_upside_potential_ratio_batch( handle: *mut UpsidePotentialRatio, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_upside_potential_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_potential_ratio_reset(handle: *mut UpsidePotentialRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_upside_potential_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_upside_potential_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_potential_ratio_free(handle: *mut UpsidePotentialRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ValueAtRisk` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_value_at_risk_free`. #[no_mangle] pub extern "C" fn wickra_value_at_risk_new(period: usize, confidence: f64) -> *mut ValueAtRisk { match ValueAtRisk::new(period, confidence) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_value_at_risk_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_value_at_risk_update(handle: *mut ValueAtRisk, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_value_at_risk_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_value_at_risk_batch( handle: *mut ValueAtRisk, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_value_at_risk_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_value_at_risk_reset(handle: *mut ValueAtRisk) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_value_at_risk_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_value_at_risk_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_value_at_risk_free(handle: *mut ValueAtRisk) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Variance` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_variance_free`. #[no_mangle] pub extern "C" fn wickra_variance_new(period: usize) -> *mut Variance { match Variance::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_variance_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_variance_update(handle: *mut Variance, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_variance_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_variance_batch( handle: *mut Variance, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_variance_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_variance_reset(handle: *mut Variance) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_variance_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_variance_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_variance_free(handle: *mut Variance) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VerticalHorizontalFilter` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vertical_horizontal_filter_free`. #[no_mangle] pub extern "C" fn wickra_vertical_horizontal_filter_new( period: usize, ) -> *mut VerticalHorizontalFilter { match VerticalHorizontalFilter::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_vertical_horizontal_filter_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vertical_horizontal_filter_update( handle: *mut VerticalHorizontalFilter, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_vertical_horizontal_filter_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_vertical_horizontal_filter_batch( handle: *mut VerticalHorizontalFilter, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vertical_horizontal_filter_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vertical_horizontal_filter_reset( handle: *mut VerticalHorizontalFilter, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vertical_horizontal_filter_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vertical_horizontal_filter_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vertical_horizontal_filter_free( handle: *mut VerticalHorizontalFilter, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Vidya` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vidya_free`. #[no_mangle] pub extern "C" fn wickra_vidya_new(period: usize, cmo_period: usize) -> *mut Vidya { match Vidya::new(period, cmo_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_vidya_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vidya_update(handle: *mut Vidya, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_vidya_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_vidya_batch( handle: *mut Vidya, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vidya_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vidya_reset(handle: *mut Vidya) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vidya_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vidya_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vidya_free(handle: *mut Vidya) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolatilityOfVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volatility_of_volatility_free`. #[no_mangle] pub extern "C" fn wickra_volatility_of_volatility_new( vol_window: usize, vov_window: usize, ) -> *mut VolatilityOfVolatility { match VolatilityOfVolatility::new(vol_window, vov_window) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_volatility_of_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_of_volatility_update( handle: *mut VolatilityOfVolatility, value: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_volatility_of_volatility_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_of_volatility_batch( handle: *mut VolatilityOfVolatility, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volatility_of_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_of_volatility_reset( handle: *mut VolatilityOfVolatility, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volatility_of_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volatility_of_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_of_volatility_free(handle: *mut VolatilityOfVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WavePm` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_wave_pm_free`. #[no_mangle] pub extern "C" fn wickra_wave_pm_new(length: usize, smoothing: usize) -> *mut WavePm { match WavePm::new(length, smoothing) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_wave_pm_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wave_pm_update(handle: *mut WavePm, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_wave_pm_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_wave_pm_batch( handle: *mut WavePm, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_wave_pm_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wave_pm_reset(handle: *mut WavePm) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_wave_pm_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_wave_pm_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wave_pm_free(handle: *mut WavePm) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WinRate` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_win_rate_free`. #[no_mangle] pub extern "C" fn wickra_win_rate_new(period: usize) -> *mut WinRate { match WinRate::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_win_rate_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_win_rate_update(handle: *mut WinRate, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_win_rate_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_win_rate_batch( handle: *mut WinRate, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_win_rate_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_win_rate_reset(handle: *mut WinRate) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_win_rate_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_win_rate_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_win_rate_free(handle: *mut WinRate) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Wma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_wma_free`. #[no_mangle] pub extern "C" fn wickra_wma_new(period: usize) -> *mut Wma { match Wma::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_wma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wma_update(handle: *mut Wma, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_wma_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_wma_batch( handle: *mut Wma, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_wma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wma_reset(handle: *mut Wma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_wma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_wma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wma_free(handle: *mut Wma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ZScore` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_z_score_free`. #[no_mangle] pub extern "C" fn wickra_z_score_new(period: usize) -> *mut ZScore { match ZScore::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_z_score_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_z_score_update(handle: *mut ZScore, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_z_score_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_z_score_batch( handle: *mut ZScore, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_z_score_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_z_score_reset(handle: *mut ZScore) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_z_score_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_z_score_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_z_score_free(handle: *mut ZScore) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Zlema` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_zlema_free`. #[no_mangle] pub extern "C" fn wickra_zlema_new(period: usize) -> *mut Zlema { match Zlema::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_zlema_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zlema_update(handle: *mut Zlema, value: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update(value).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over `input[0..n]`, writing one output per input into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_zlema_new`, not freed); `input`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_zlema_batch( handle: *mut Zlema, input: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || input.is_null() || out.is_null() { return; } let ind = &mut *handle; let inputs = slice::from_raw_parts(input, n); let outputs = slice::from_raw_parts_mut(out, n); for (slot, &value) in outputs.iter_mut().zip(inputs) { *slot = ind.update(value).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_zlema_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zlema_reset(handle: *mut Zlema) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_zlema_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_zlema_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zlema_free(handle: *mut Zlema) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Pairwise indicators ((f64, f64) -> f64) ===== /// Create a `Alpha` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_alpha_free`. #[no_mangle] pub extern "C" fn wickra_alpha_new(period: usize, risk_free: f64) -> *mut Alpha { match Alpha::new(period, risk_free) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_alpha_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alpha_update(handle: *mut Alpha, x: f64, y: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_alpha_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_alpha_batch( handle: *mut Alpha, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_alpha_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alpha_reset(handle: *mut Alpha) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_alpha_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_alpha_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alpha_free(handle: *mut Alpha) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Beta` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_beta_free`. #[no_mangle] pub extern "C" fn wickra_beta_new(period: usize) -> *mut Beta { match Beta::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_beta_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_beta_update(handle: *mut Beta, x: f64, y: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_beta_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_beta_batch( handle: *mut Beta, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_beta_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_beta_reset(handle: *mut Beta) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_beta_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_beta_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_beta_free(handle: *mut Beta) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BetaNeutralSpread` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_beta_neutral_spread_free`. #[no_mangle] pub extern "C" fn wickra_beta_neutral_spread_new(period: usize) -> *mut BetaNeutralSpread { match BetaNeutralSpread::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_beta_neutral_spread_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_beta_neutral_spread_update( handle: *mut BetaNeutralSpread, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_beta_neutral_spread_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_beta_neutral_spread_batch( handle: *mut BetaNeutralSpread, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_beta_neutral_spread_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_beta_neutral_spread_reset(handle: *mut BetaNeutralSpread) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_beta_neutral_spread_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_beta_neutral_spread_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_beta_neutral_spread_free(handle: *mut BetaNeutralSpread) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DistanceSsd` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_distance_ssd_free`. #[no_mangle] pub extern "C" fn wickra_distance_ssd_new(period: usize) -> *mut DistanceSsd { match DistanceSsd::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_distance_ssd_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_distance_ssd_update( handle: *mut DistanceSsd, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_distance_ssd_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_distance_ssd_batch( handle: *mut DistanceSsd, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_distance_ssd_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_distance_ssd_reset(handle: *mut DistanceSsd) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_distance_ssd_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_distance_ssd_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_distance_ssd_free(handle: *mut DistanceSsd) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GrangerCausality` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_granger_causality_free`. #[no_mangle] pub extern "C" fn wickra_granger_causality_new(period: usize, lag: usize) -> *mut GrangerCausality { match GrangerCausality::new(period, lag) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_granger_causality_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_granger_causality_update( handle: *mut GrangerCausality, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_granger_causality_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_granger_causality_batch( handle: *mut GrangerCausality, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_granger_causality_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_granger_causality_reset(handle: *mut GrangerCausality) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_granger_causality_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_granger_causality_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_granger_causality_free(handle: *mut GrangerCausality) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HasbrouckInformationShare` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hasbrouck_information_share_free`. #[no_mangle] pub extern "C" fn wickra_hasbrouck_information_share_new( period: usize, ) -> *mut HasbrouckInformationShare { match HasbrouckInformationShare::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_hasbrouck_information_share_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hasbrouck_information_share_update( handle: *mut HasbrouckInformationShare, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_hasbrouck_information_share_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_hasbrouck_information_share_batch( handle: *mut HasbrouckInformationShare, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hasbrouck_information_share_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hasbrouck_information_share_reset( handle: *mut HasbrouckInformationShare, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hasbrouck_information_share_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hasbrouck_information_share_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hasbrouck_information_share_free( handle: *mut HasbrouckInformationShare, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `InformationRatio` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_information_ratio_free`. #[no_mangle] pub extern "C" fn wickra_information_ratio_new(period: usize) -> *mut InformationRatio { match InformationRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_information_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_information_ratio_update( handle: *mut InformationRatio, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_information_ratio_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_information_ratio_batch( handle: *mut InformationRatio, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_information_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_information_ratio_reset(handle: *mut InformationRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_information_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_information_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_information_ratio_free(handle: *mut InformationRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KendallTau` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kendall_tau_free`. #[no_mangle] pub extern "C" fn wickra_kendall_tau_new(period: usize) -> *mut KendallTau { match KendallTau::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_kendall_tau_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kendall_tau_update(handle: *mut KendallTau, x: f64, y: f64) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_kendall_tau_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_kendall_tau_batch( handle: *mut KendallTau, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kendall_tau_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kendall_tau_reset(handle: *mut KendallTau) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kendall_tau_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kendall_tau_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kendall_tau_free(handle: *mut KendallTau) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OuHalfLife` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ou_half_life_free`. #[no_mangle] pub extern "C" fn wickra_ou_half_life_new(period: usize) -> *mut OuHalfLife { match OuHalfLife::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_ou_half_life_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ou_half_life_update( handle: *mut OuHalfLife, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_ou_half_life_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_ou_half_life_batch( handle: *mut OuHalfLife, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ou_half_life_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ou_half_life_reset(handle: *mut OuHalfLife) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ou_half_life_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ou_half_life_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ou_half_life_free(handle: *mut OuHalfLife) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PairSpreadZScore` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pair_spread_z_score_free`. #[no_mangle] pub extern "C" fn wickra_pair_spread_z_score_new( beta_period: usize, z_period: usize, ) -> *mut PairSpreadZScore { match PairSpreadZScore::new(beta_period, z_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_pair_spread_z_score_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pair_spread_z_score_update( handle: *mut PairSpreadZScore, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_pair_spread_z_score_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_pair_spread_z_score_batch( handle: *mut PairSpreadZScore, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pair_spread_z_score_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pair_spread_z_score_reset(handle: *mut PairSpreadZScore) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pair_spread_z_score_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pair_spread_z_score_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pair_spread_z_score_free(handle: *mut PairSpreadZScore) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PairwiseBeta` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pairwise_beta_free`. #[no_mangle] pub extern "C" fn wickra_pairwise_beta_new(period: usize) -> *mut PairwiseBeta { match PairwiseBeta::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_pairwise_beta_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pairwise_beta_update( handle: *mut PairwiseBeta, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_pairwise_beta_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_pairwise_beta_batch( handle: *mut PairwiseBeta, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pairwise_beta_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pairwise_beta_reset(handle: *mut PairwiseBeta) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pairwise_beta_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pairwise_beta_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pairwise_beta_free(handle: *mut PairwiseBeta) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PearsonCorrelation` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pearson_correlation_free`. #[no_mangle] pub extern "C" fn wickra_pearson_correlation_new(period: usize) -> *mut PearsonCorrelation { match PearsonCorrelation::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_pearson_correlation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pearson_correlation_update( handle: *mut PearsonCorrelation, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_pearson_correlation_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_pearson_correlation_batch( handle: *mut PearsonCorrelation, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pearson_correlation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pearson_correlation_reset(handle: *mut PearsonCorrelation) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pearson_correlation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pearson_correlation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pearson_correlation_free(handle: *mut PearsonCorrelation) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollingCorrelation` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rolling_correlation_free`. #[no_mangle] pub extern "C" fn wickra_rolling_correlation_new(period: usize) -> *mut RollingCorrelation { match RollingCorrelation::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rolling_correlation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_correlation_update( handle: *mut RollingCorrelation, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rolling_correlation_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_correlation_batch( handle: *mut RollingCorrelation, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rolling_correlation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_correlation_reset(handle: *mut RollingCorrelation) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rolling_correlation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rolling_correlation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_correlation_free(handle: *mut RollingCorrelation) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollingCovariance` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rolling_covariance_free`. #[no_mangle] pub extern "C" fn wickra_rolling_covariance_new(period: usize) -> *mut RollingCovariance { match RollingCovariance::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_rolling_covariance_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_covariance_update( handle: *mut RollingCovariance, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_rolling_covariance_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_covariance_batch( handle: *mut RollingCovariance, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rolling_covariance_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_covariance_reset(handle: *mut RollingCovariance) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rolling_covariance_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rolling_covariance_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_covariance_free(handle: *mut RollingCovariance) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SpearmanCorrelation` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_spearman_correlation_free`. #[no_mangle] pub extern "C" fn wickra_spearman_correlation_new(period: usize) -> *mut SpearmanCorrelation { match SpearmanCorrelation::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_spearman_correlation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spearman_correlation_update( handle: *mut SpearmanCorrelation, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_spearman_correlation_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_spearman_correlation_batch( handle: *mut SpearmanCorrelation, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_spearman_correlation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spearman_correlation_reset(handle: *mut SpearmanCorrelation) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_spearman_correlation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_spearman_correlation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spearman_correlation_free(handle: *mut SpearmanCorrelation) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SpreadAr1Coefficient` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_spread_ar1_coefficient_free`. #[no_mangle] pub extern "C" fn wickra_spread_ar1_coefficient_new(period: usize) -> *mut SpreadAr1Coefficient { match SpreadAr1Coefficient::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_spread_ar1_coefficient_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_ar1_coefficient_update( handle: *mut SpreadAr1Coefficient, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_spread_ar1_coefficient_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_spread_ar1_coefficient_batch( handle: *mut SpreadAr1Coefficient, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_spread_ar1_coefficient_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_ar1_coefficient_reset(handle: *mut SpreadAr1Coefficient) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_spread_ar1_coefficient_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_spread_ar1_coefficient_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_ar1_coefficient_free(handle: *mut SpreadAr1Coefficient) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SpreadHurst` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_spread_hurst_free`. #[no_mangle] pub extern "C" fn wickra_spread_hurst_new(period: usize) -> *mut SpreadHurst { match SpreadHurst::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_spread_hurst_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_hurst_update( handle: *mut SpreadHurst, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_spread_hurst_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_spread_hurst_batch( handle: *mut SpreadHurst, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_spread_hurst_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_hurst_reset(handle: *mut SpreadHurst) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_spread_hurst_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_spread_hurst_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_hurst_free(handle: *mut SpreadHurst) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TreynorRatio` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_treynor_ratio_free`. #[no_mangle] pub extern "C" fn wickra_treynor_ratio_new(period: usize, risk_free: f64) -> *mut TreynorRatio { match TreynorRatio::new(period, risk_free) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_treynor_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_treynor_ratio_update( handle: *mut TreynorRatio, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_treynor_ratio_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_treynor_ratio_batch( handle: *mut TreynorRatio, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_treynor_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_treynor_ratio_reset(handle: *mut TreynorRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_treynor_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_treynor_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_treynor_ratio_free(handle: *mut TreynorRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VarianceRatio` pairwise indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_variance_ratio_free`. #[no_mangle] pub extern "C" fn wickra_variance_ratio_new(period: usize, q: usize) -> *mut VarianceRatio { match VarianceRatio::new(period, q) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one `(x, y)` pair; returns the output, or `NaN` during warmup / on a `NULL` handle. /// /// # Safety /// `handle` must be a valid pointer from `wickra_variance_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_variance_ratio_update( handle: *mut VarianceRatio, x: f64, y: f64, ) -> f64 { match handle.as_mut() { Some(ind) => ind.update((x, y)).unwrap_or(f64::NAN), None => f64::NAN, } } /// Run over the paired series `x[0..n]` / `y[0..n]` into `out[0..n]` (`NaN` at warmup). /// /// # Safety /// `handle` valid (from `wickra_variance_ratio_new`, not freed); `x`/`y`/`out` cover `n` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_variance_ratio_batch( handle: *mut VarianceRatio, x: *const f64, y: *const f64, out: *mut f64, n: usize, ) { if handle.is_null() || x.is_null() || y.is_null() || out.is_null() { return; } let ind = &mut *handle; let xs = slice::from_raw_parts(x, n); let ys = slice::from_raw_parts(y, n); let outputs = slice::from_raw_parts_mut(out, n); for ((slot, &xv), &yv) in outputs.iter_mut().zip(xs).zip(ys) { *slot = ind.update((xv, yv)).unwrap_or(f64::NAN); } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_variance_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_variance_ratio_reset(handle: *mut VarianceRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_variance_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_variance_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_variance_ratio_free(handle: *mut VarianceRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Candle indicators (OHLCV -> f64; includes candlestick patterns) ===== /// Create a `AbandonedBaby` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_abandoned_baby_free`. #[no_mangle] pub extern "C" fn wickra_abandoned_baby_new() -> *mut AbandonedBaby { Box::into_raw(Box::new(AbandonedBaby::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_abandoned_baby_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_abandoned_baby_update( handle: *mut AbandonedBaby, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_abandoned_baby_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_abandoned_baby_batch( handle: *mut AbandonedBaby, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_abandoned_baby_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_abandoned_baby_reset(handle: *mut AbandonedBaby) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_abandoned_baby_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_abandoned_baby_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_abandoned_baby_free(handle: *mut AbandonedBaby) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Abcd` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_abcd_free`. #[no_mangle] pub extern "C" fn wickra_abcd_new() -> *mut Abcd { Box::into_raw(Box::new(Abcd::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_abcd_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_abcd_update( handle: *mut Abcd, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_abcd_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_abcd_batch( handle: *mut Abcd, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_abcd_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_abcd_reset(handle: *mut Abcd) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_abcd_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_abcd_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_abcd_free(handle: *mut Abcd) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AcceleratorOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_accelerator_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_accelerator_oscillator_new( ao_fast: usize, ao_slow: usize, signal_period: usize, ) -> *mut AcceleratorOscillator { match AcceleratorOscillator::new(ao_fast, ao_slow, signal_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_accelerator_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_accelerator_oscillator_update( handle: *mut AcceleratorOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_accelerator_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_accelerator_oscillator_batch( handle: *mut AcceleratorOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_accelerator_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_accelerator_oscillator_reset(handle: *mut AcceleratorOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_accelerator_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_accelerator_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_accelerator_oscillator_free(handle: *mut AcceleratorOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ad_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_ad_oscillator_new() -> *mut AdOscillator { Box::into_raw(Box::new(AdOscillator::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_ad_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ad_oscillator_update( handle: *mut AdOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_ad_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_ad_oscillator_batch( handle: *mut AdOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ad_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ad_oscillator_reset(handle: *mut AdOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ad_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ad_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ad_oscillator_free(handle: *mut AdOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdaptiveCci` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_adaptive_cci_free`. #[no_mangle] pub extern "C" fn wickra_adaptive_cci_new(period: usize) -> *mut AdaptiveCci { match AdaptiveCci::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_adaptive_cci_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cci_update( handle: *mut AdaptiveCci, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_adaptive_cci_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cci_batch( handle: *mut AdaptiveCci, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_adaptive_cci_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cci_reset(handle: *mut AdaptiveCci) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_adaptive_cci_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_adaptive_cci_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adaptive_cci_free(handle: *mut AdaptiveCci) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Adl` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_adl_free`. #[no_mangle] pub extern "C" fn wickra_adl_new() -> *mut Adl { Box::into_raw(Box::new(Adl::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_adl_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adl_update( handle: *mut Adl, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_adl_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_adl_batch( handle: *mut Adl, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_adl_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adl_reset(handle: *mut Adl) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_adl_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_adl_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adl_free(handle: *mut Adl) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdvanceBlock` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_advance_block_free`. #[no_mangle] pub extern "C" fn wickra_advance_block_new() -> *mut AdvanceBlock { Box::into_raw(Box::new(AdvanceBlock::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_advance_block_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_advance_block_update( handle: *mut AdvanceBlock, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_advance_block_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_advance_block_batch( handle: *mut AdvanceBlock, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_advance_block_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_advance_block_reset(handle: *mut AdvanceBlock) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_advance_block_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_advance_block_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_advance_block_free(handle: *mut AdvanceBlock) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Adxr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_adxr_free`. #[no_mangle] pub extern "C" fn wickra_adxr_new(period: usize) -> *mut Adxr { match Adxr::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_adxr_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adxr_update( handle: *mut Adxr, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_adxr_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_adxr_batch( handle: *mut Adxr, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_adxr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adxr_reset(handle: *mut Adxr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_adxr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_adxr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adxr_free(handle: *mut Adxr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AnchoredVwap` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_anchored_vwap_free`. #[no_mangle] pub extern "C" fn wickra_anchored_vwap_new() -> *mut AnchoredVwap { Box::into_raw(Box::new(AnchoredVwap::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_anchored_vwap_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_vwap_update( handle: *mut AnchoredVwap, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_anchored_vwap_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_vwap_batch( handle: *mut AnchoredVwap, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_anchored_vwap_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_vwap_reset(handle: *mut AnchoredVwap) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_anchored_vwap_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_anchored_vwap_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_anchored_vwap_free(handle: *mut AnchoredVwap) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AroonOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_aroon_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_aroon_oscillator_new(period: usize) -> *mut AroonOscillator { match AroonOscillator::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_aroon_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_aroon_oscillator_update( handle: *mut AroonOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_aroon_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_aroon_oscillator_batch( handle: *mut AroonOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_aroon_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_aroon_oscillator_reset(handle: *mut AroonOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_aroon_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_aroon_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_aroon_oscillator_free(handle: *mut AroonOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Atr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_atr_free`. #[no_mangle] pub extern "C" fn wickra_atr_new(period: usize) -> *mut Atr { match Atr::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_atr_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_update( handle: *mut Atr, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_atr_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_atr_batch( handle: *mut Atr, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_atr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_reset(handle: *mut Atr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_atr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_atr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_free(handle: *mut Atr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AtrTrailingStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_atr_trailing_stop_free`. #[no_mangle] pub extern "C" fn wickra_atr_trailing_stop_new( atr_period: usize, multiplier: f64, ) -> *mut AtrTrailingStop { match AtrTrailingStop::new(atr_period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_atr_trailing_stop_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_trailing_stop_update( handle: *mut AtrTrailingStop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_atr_trailing_stop_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_atr_trailing_stop_batch( handle: *mut AtrTrailingStop, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_atr_trailing_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_trailing_stop_reset(handle: *mut AtrTrailingStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_atr_trailing_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_atr_trailing_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_trailing_stop_free(handle: *mut AtrTrailingStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AverageDailyRange` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_average_daily_range_free`. #[no_mangle] pub extern "C" fn wickra_average_daily_range_new( period: usize, utc_offset_minutes: i32, ) -> *mut AverageDailyRange { match AverageDailyRange::new(period, utc_offset_minutes) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_average_daily_range_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_average_daily_range_update( handle: *mut AverageDailyRange, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_average_daily_range_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_average_daily_range_batch( handle: *mut AverageDailyRange, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_average_daily_range_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_average_daily_range_reset(handle: *mut AverageDailyRange) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_average_daily_range_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_average_daily_range_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_average_daily_range_free(handle: *mut AverageDailyRange) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AvgPrice` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_avg_price_free`. #[no_mangle] pub extern "C" fn wickra_avg_price_new() -> *mut AvgPrice { Box::into_raw(Box::new(AvgPrice::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_avg_price_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_avg_price_update( handle: *mut AvgPrice, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_avg_price_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_avg_price_batch( handle: *mut AvgPrice, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_avg_price_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_avg_price_reset(handle: *mut AvgPrice) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_avg_price_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_avg_price_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_avg_price_free(handle: *mut AvgPrice) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AwesomeOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_awesome_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_awesome_oscillator_new( fast: usize, slow: usize, ) -> *mut AwesomeOscillator { match AwesomeOscillator::new(fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_awesome_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_update( handle: *mut AwesomeOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_awesome_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_batch( handle: *mut AwesomeOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_awesome_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_reset(handle: *mut AwesomeOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_awesome_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_awesome_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_free(handle: *mut AwesomeOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AwesomeOscillatorHistogram` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_awesome_oscillator_histogram_free`. #[no_mangle] pub extern "C" fn wickra_awesome_oscillator_histogram_new( fast: usize, slow: usize, sma_period: usize, ) -> *mut AwesomeOscillatorHistogram { match AwesomeOscillatorHistogram::new(fast, slow, sma_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_awesome_oscillator_histogram_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_update( handle: *mut AwesomeOscillatorHistogram, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_awesome_oscillator_histogram_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_batch( handle: *mut AwesomeOscillatorHistogram, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_awesome_oscillator_histogram_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_reset( handle: *mut AwesomeOscillatorHistogram, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_awesome_oscillator_histogram_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_awesome_oscillator_histogram_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_awesome_oscillator_histogram_free( handle: *mut AwesomeOscillatorHistogram, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BalanceOfPower` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_balance_of_power_free`. #[no_mangle] pub extern "C" fn wickra_balance_of_power_new() -> *mut BalanceOfPower { Box::into_raw(Box::new(BalanceOfPower::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_balance_of_power_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_balance_of_power_update( handle: *mut BalanceOfPower, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_balance_of_power_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_balance_of_power_batch( handle: *mut BalanceOfPower, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_balance_of_power_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_balance_of_power_reset(handle: *mut BalanceOfPower) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_balance_of_power_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_balance_of_power_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_balance_of_power_free(handle: *mut BalanceOfPower) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Bat` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_bat_free`. #[no_mangle] pub extern "C" fn wickra_bat_new() -> *mut Bat { Box::into_raw(Box::new(Bat::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_bat_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bat_update( handle: *mut Bat, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_bat_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_bat_batch( handle: *mut Bat, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_bat_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bat_reset(handle: *mut Bat) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_bat_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_bat_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bat_free(handle: *mut Bat) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BeltHold` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_belt_hold_free`. #[no_mangle] pub extern "C" fn wickra_belt_hold_new() -> *mut BeltHold { Box::into_raw(Box::new(BeltHold::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_belt_hold_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_belt_hold_update( handle: *mut BeltHold, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_belt_hold_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_belt_hold_batch( handle: *mut BeltHold, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_belt_hold_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_belt_hold_reset(handle: *mut BeltHold) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_belt_hold_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_belt_hold_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_belt_hold_free(handle: *mut BeltHold) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BetterVolume` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_better_volume_free`. #[no_mangle] pub extern "C" fn wickra_better_volume_new(period: usize) -> *mut BetterVolume { match BetterVolume::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_better_volume_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_better_volume_update( handle: *mut BetterVolume, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_better_volume_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_better_volume_batch( handle: *mut BetterVolume, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_better_volume_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_better_volume_reset(handle: *mut BetterVolume) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_better_volume_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_better_volume_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_better_volume_free(handle: *mut BetterVolume) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BodySizePct` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_body_size_pct_free`. #[no_mangle] pub extern "C" fn wickra_body_size_pct_new() -> *mut BodySizePct { Box::into_raw(Box::new(BodySizePct::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_body_size_pct_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_body_size_pct_update( handle: *mut BodySizePct, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_body_size_pct_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_body_size_pct_batch( handle: *mut BodySizePct, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_body_size_pct_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_body_size_pct_reset(handle: *mut BodySizePct) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_body_size_pct_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_body_size_pct_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_body_size_pct_free(handle: *mut BodySizePct) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Breakaway` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_breakaway_free`. #[no_mangle] pub extern "C" fn wickra_breakaway_new() -> *mut Breakaway { Box::into_raw(Box::new(Breakaway::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_breakaway_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_breakaway_update( handle: *mut Breakaway, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_breakaway_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_breakaway_batch( handle: *mut Breakaway, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_breakaway_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_breakaway_reset(handle: *mut Breakaway) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_breakaway_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_breakaway_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_breakaway_free(handle: *mut Breakaway) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Butterfly` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_butterfly_free`. #[no_mangle] pub extern "C" fn wickra_butterfly_new() -> *mut Butterfly { Box::into_raw(Box::new(Butterfly::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_butterfly_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_butterfly_update( handle: *mut Butterfly, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_butterfly_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_butterfly_batch( handle: *mut Butterfly, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_butterfly_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_butterfly_reset(handle: *mut Butterfly) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_butterfly_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_butterfly_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_butterfly_free(handle: *mut Butterfly) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Cci` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cci_free`. #[no_mangle] pub extern "C" fn wickra_cci_new(period: usize) -> *mut Cci { match Cci::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_cci_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cci_update( handle: *mut Cci, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_cci_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_cci_batch( handle: *mut Cci, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cci_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cci_reset(handle: *mut Cci) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cci_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cci_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cci_free(handle: *mut Cci) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ChaikinOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_chaikin_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_chaikin_oscillator_new( fast: usize, slow: usize, ) -> *mut ChaikinOscillator { match ChaikinOscillator::new(fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_chaikin_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_oscillator_update( handle: *mut ChaikinOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_chaikin_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_oscillator_batch( handle: *mut ChaikinOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_chaikin_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_oscillator_reset(handle: *mut ChaikinOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_chaikin_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_chaikin_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_oscillator_free(handle: *mut ChaikinOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ChaikinVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_chaikin_volatility_free`. #[no_mangle] pub extern "C" fn wickra_chaikin_volatility_new( ema_period: usize, roc_period: usize, ) -> *mut ChaikinVolatility { match ChaikinVolatility::new(ema_period, roc_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_chaikin_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_volatility_update( handle: *mut ChaikinVolatility, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_chaikin_volatility_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_volatility_batch( handle: *mut ChaikinVolatility, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_chaikin_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_volatility_reset(handle: *mut ChaikinVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_chaikin_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_chaikin_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_volatility_free(handle: *mut ChaikinVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ChoppinessIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_choppiness_index_free`. #[no_mangle] pub extern "C" fn wickra_choppiness_index_new(period: usize) -> *mut ChoppinessIndex { match ChoppinessIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_choppiness_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_choppiness_index_update( handle: *mut ChoppinessIndex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_choppiness_index_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_choppiness_index_batch( handle: *mut ChoppinessIndex, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_choppiness_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_choppiness_index_reset(handle: *mut ChoppinessIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_choppiness_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_choppiness_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_choppiness_index_free(handle: *mut ChoppinessIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CloseVsOpen` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_close_vs_open_free`. #[no_mangle] pub extern "C" fn wickra_close_vs_open_new() -> *mut CloseVsOpen { Box::into_raw(Box::new(CloseVsOpen::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_close_vs_open_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_close_vs_open_update( handle: *mut CloseVsOpen, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_close_vs_open_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_close_vs_open_batch( handle: *mut CloseVsOpen, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_close_vs_open_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_close_vs_open_reset(handle: *mut CloseVsOpen) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_close_vs_open_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_close_vs_open_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_close_vs_open_free(handle: *mut CloseVsOpen) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ClosingMarubozu` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_closing_marubozu_free`. #[no_mangle] pub extern "C" fn wickra_closing_marubozu_new() -> *mut ClosingMarubozu { Box::into_raw(Box::new(ClosingMarubozu::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_closing_marubozu_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_closing_marubozu_update( handle: *mut ClosingMarubozu, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_closing_marubozu_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_closing_marubozu_batch( handle: *mut ClosingMarubozu, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_closing_marubozu_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_closing_marubozu_reset(handle: *mut ClosingMarubozu) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_closing_marubozu_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_closing_marubozu_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_closing_marubozu_free(handle: *mut ClosingMarubozu) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ChaikinMoneyFlow` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_chaikin_money_flow_free`. #[no_mangle] pub extern "C" fn wickra_chaikin_money_flow_new(period: usize) -> *mut ChaikinMoneyFlow { match ChaikinMoneyFlow::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_chaikin_money_flow_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_money_flow_update( handle: *mut ChaikinMoneyFlow, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_chaikin_money_flow_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_money_flow_batch( handle: *mut ChaikinMoneyFlow, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_chaikin_money_flow_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_money_flow_reset(handle: *mut ChaikinMoneyFlow) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_chaikin_money_flow_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_chaikin_money_flow_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chaikin_money_flow_free(handle: *mut ChaikinMoneyFlow) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ConcealingBabySwallow` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_concealing_baby_swallow_free`. #[no_mangle] pub extern "C" fn wickra_concealing_baby_swallow_new() -> *mut ConcealingBabySwallow { Box::into_raw(Box::new(ConcealingBabySwallow::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_concealing_baby_swallow_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_concealing_baby_swallow_update( handle: *mut ConcealingBabySwallow, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_concealing_baby_swallow_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_concealing_baby_swallow_batch( handle: *mut ConcealingBabySwallow, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_concealing_baby_swallow_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_concealing_baby_swallow_reset(handle: *mut ConcealingBabySwallow) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_concealing_baby_swallow_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_concealing_baby_swallow_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_concealing_baby_swallow_free(handle: *mut ConcealingBabySwallow) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Counterattack` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_counterattack_free`. #[no_mangle] pub extern "C" fn wickra_counterattack_new() -> *mut Counterattack { Box::into_raw(Box::new(Counterattack::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_counterattack_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_counterattack_update( handle: *mut Counterattack, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_counterattack_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_counterattack_batch( handle: *mut Counterattack, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_counterattack_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_counterattack_reset(handle: *mut Counterattack) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_counterattack_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_counterattack_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_counterattack_free(handle: *mut Counterattack) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Crab` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_crab_free`. #[no_mangle] pub extern "C" fn wickra_crab_new() -> *mut Crab { Box::into_raw(Box::new(Crab::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_crab_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_crab_update( handle: *mut Crab, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_crab_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_crab_batch( handle: *mut Crab, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_crab_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_crab_reset(handle: *mut Crab) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_crab_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_crab_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_crab_free(handle: *mut Crab) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CupAndHandle` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cup_and_handle_free`. #[no_mangle] pub extern "C" fn wickra_cup_and_handle_new() -> *mut CupAndHandle { Box::into_raw(Box::new(CupAndHandle::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_cup_and_handle_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cup_and_handle_update( handle: *mut CupAndHandle, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_cup_and_handle_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_cup_and_handle_batch( handle: *mut CupAndHandle, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cup_and_handle_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cup_and_handle_reset(handle: *mut CupAndHandle) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cup_and_handle_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cup_and_handle_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cup_and_handle_free(handle: *mut CupAndHandle) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Cypher` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cypher_free`. #[no_mangle] pub extern "C" fn wickra_cypher_new() -> *mut Cypher { Box::into_raw(Box::new(Cypher::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_cypher_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cypher_update( handle: *mut Cypher, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_cypher_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_cypher_batch( handle: *mut Cypher, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cypher_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cypher_reset(handle: *mut Cypher) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cypher_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cypher_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cypher_free(handle: *mut Cypher) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DemandIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_demand_index_free`. #[no_mangle] pub extern "C" fn wickra_demand_index_new(period: usize) -> *mut DemandIndex { match DemandIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_demand_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_demand_index_update( handle: *mut DemandIndex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_demand_index_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_demand_index_batch( handle: *mut DemandIndex, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_demand_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_demand_index_reset(handle: *mut DemandIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_demand_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_demand_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_demand_index_free(handle: *mut DemandIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Doji` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_doji_free`. #[no_mangle] pub extern "C" fn wickra_doji_new() -> *mut Doji { Box::into_raw(Box::new(Doji::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_doji_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_doji_update( handle: *mut Doji, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_doji_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_doji_batch( handle: *mut Doji, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_doji_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_doji_reset(handle: *mut Doji) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_doji_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_doji_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_doji_free(handle: *mut Doji) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DojiStar` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_doji_star_free`. #[no_mangle] pub extern "C" fn wickra_doji_star_new() -> *mut DojiStar { Box::into_raw(Box::new(DojiStar::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_doji_star_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_doji_star_update( handle: *mut DojiStar, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_doji_star_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_doji_star_batch( handle: *mut DojiStar, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_doji_star_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_doji_star_reset(handle: *mut DojiStar) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_doji_star_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_doji_star_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_doji_star_free(handle: *mut DojiStar) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DoubleTopBottom` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_double_top_bottom_free`. #[no_mangle] pub extern "C" fn wickra_double_top_bottom_new() -> *mut DoubleTopBottom { Box::into_raw(Box::new(DoubleTopBottom::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_double_top_bottom_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_double_top_bottom_update( handle: *mut DoubleTopBottom, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_double_top_bottom_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_double_top_bottom_batch( handle: *mut DoubleTopBottom, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_double_top_bottom_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_double_top_bottom_reset(handle: *mut DoubleTopBottom) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_double_top_bottom_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_double_top_bottom_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_double_top_bottom_free(handle: *mut DoubleTopBottom) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DownsideGapThreeMethods` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_downside_gap_three_methods_free`. #[no_mangle] pub extern "C" fn wickra_downside_gap_three_methods_new() -> *mut DownsideGapThreeMethods { Box::into_raw(Box::new(DownsideGapThreeMethods::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_downside_gap_three_methods_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_downside_gap_three_methods_update( handle: *mut DownsideGapThreeMethods, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_downside_gap_three_methods_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_downside_gap_three_methods_batch( handle: *mut DownsideGapThreeMethods, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_downside_gap_three_methods_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_downside_gap_three_methods_reset( handle: *mut DownsideGapThreeMethods, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_downside_gap_three_methods_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_downside_gap_three_methods_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_downside_gap_three_methods_free( handle: *mut DownsideGapThreeMethods, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DragonflyDoji` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_dragonfly_doji_free`. #[no_mangle] pub extern "C" fn wickra_dragonfly_doji_new() -> *mut DragonflyDoji { Box::into_raw(Box::new(DragonflyDoji::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_dragonfly_doji_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dragonfly_doji_update( handle: *mut DragonflyDoji, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_dragonfly_doji_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_dragonfly_doji_batch( handle: *mut DragonflyDoji, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_dragonfly_doji_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dragonfly_doji_reset(handle: *mut DragonflyDoji) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_dragonfly_doji_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_dragonfly_doji_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dragonfly_doji_free(handle: *mut DragonflyDoji) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DumplingTop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_dumpling_top_free`. #[no_mangle] pub extern "C" fn wickra_dumpling_top_new(period: usize) -> *mut DumplingTop { match DumplingTop::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_dumpling_top_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dumpling_top_update( handle: *mut DumplingTop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_dumpling_top_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_dumpling_top_batch( handle: *mut DumplingTop, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_dumpling_top_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dumpling_top_reset(handle: *mut DumplingTop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_dumpling_top_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_dumpling_top_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dumpling_top_free(handle: *mut DumplingTop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Dx` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_dx_free`. #[no_mangle] pub extern "C" fn wickra_dx_new(period: usize) -> *mut Dx { match Dx::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_dx_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dx_update( handle: *mut Dx, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_dx_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_dx_batch( handle: *mut Dx, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_dx_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dx_reset(handle: *mut Dx) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_dx_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_dx_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dx_free(handle: *mut Dx) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `EaseOfMovement` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ease_of_movement_free`. #[no_mangle] pub extern "C" fn wickra_ease_of_movement_new(period: usize) -> *mut EaseOfMovement { match EaseOfMovement::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_ease_of_movement_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ease_of_movement_update( handle: *mut EaseOfMovement, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_ease_of_movement_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_ease_of_movement_batch( handle: *mut EaseOfMovement, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ease_of_movement_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ease_of_movement_reset(handle: *mut EaseOfMovement) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ease_of_movement_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ease_of_movement_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ease_of_movement_free(handle: *mut EaseOfMovement) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Engulfing` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_engulfing_free`. #[no_mangle] pub extern "C" fn wickra_engulfing_new() -> *mut Engulfing { Box::into_raw(Box::new(Engulfing::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_engulfing_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_engulfing_update( handle: *mut Engulfing, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_engulfing_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_engulfing_batch( handle: *mut Engulfing, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_engulfing_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_engulfing_reset(handle: *mut Engulfing) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_engulfing_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_engulfing_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_engulfing_free(handle: *mut Engulfing) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `EveningDojiStar` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_evening_doji_star_free`. #[no_mangle] pub extern "C" fn wickra_evening_doji_star_new() -> *mut EveningDojiStar { Box::into_raw(Box::new(EveningDojiStar::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_evening_doji_star_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_evening_doji_star_update( handle: *mut EveningDojiStar, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_evening_doji_star_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_evening_doji_star_batch( handle: *mut EveningDojiStar, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_evening_doji_star_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_evening_doji_star_reset(handle: *mut EveningDojiStar) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_evening_doji_star_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_evening_doji_star_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_evening_doji_star_free(handle: *mut EveningDojiStar) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Evwma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_evwma_free`. #[no_mangle] pub extern "C" fn wickra_evwma_new(period: usize) -> *mut Evwma { match Evwma::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_evwma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_evwma_update( handle: *mut Evwma, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_evwma_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_evwma_batch( handle: *mut Evwma, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_evwma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_evwma_reset(handle: *mut Evwma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_evwma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_evwma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_evwma_free(handle: *mut Evwma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FallingThreeMethods` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_falling_three_methods_free`. #[no_mangle] pub extern "C" fn wickra_falling_three_methods_new() -> *mut FallingThreeMethods { Box::into_raw(Box::new(FallingThreeMethods::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_falling_three_methods_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_falling_three_methods_update( handle: *mut FallingThreeMethods, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_falling_three_methods_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_falling_three_methods_batch( handle: *mut FallingThreeMethods, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_falling_three_methods_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_falling_three_methods_reset(handle: *mut FallingThreeMethods) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_falling_three_methods_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_falling_three_methods_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_falling_three_methods_free(handle: *mut FallingThreeMethods) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FlagPennant` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_flag_pennant_free`. #[no_mangle] pub extern "C" fn wickra_flag_pennant_new() -> *mut FlagPennant { Box::into_raw(Box::new(FlagPennant::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_flag_pennant_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_flag_pennant_update( handle: *mut FlagPennant, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_flag_pennant_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_flag_pennant_batch( handle: *mut FlagPennant, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_flag_pennant_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_flag_pennant_reset(handle: *mut FlagPennant) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_flag_pennant_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_flag_pennant_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_flag_pennant_free(handle: *mut FlagPennant) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ForceIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_force_index_free`. #[no_mangle] pub extern "C" fn wickra_force_index_new(period: usize) -> *mut ForceIndex { match ForceIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_force_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_force_index_update( handle: *mut ForceIndex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_force_index_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_force_index_batch( handle: *mut ForceIndex, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_force_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_force_index_reset(handle: *mut ForceIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_force_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_force_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_force_index_free(handle: *mut ForceIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FryPanBottom` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fry_pan_bottom_free`. #[no_mangle] pub extern "C" fn wickra_fry_pan_bottom_new(period: usize) -> *mut FryPanBottom { match FryPanBottom::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_fry_pan_bottom_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fry_pan_bottom_update( handle: *mut FryPanBottom, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_fry_pan_bottom_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_fry_pan_bottom_batch( handle: *mut FryPanBottom, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fry_pan_bottom_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fry_pan_bottom_reset(handle: *mut FryPanBottom) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fry_pan_bottom_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fry_pan_bottom_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fry_pan_bottom_free(handle: *mut FryPanBottom) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GapSideBySideWhite` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_gap_side_by_side_white_free`. #[no_mangle] pub extern "C" fn wickra_gap_side_by_side_white_new() -> *mut GapSideBySideWhite { Box::into_raw(Box::new(GapSideBySideWhite::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_gap_side_by_side_white_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gap_side_by_side_white_update( handle: *mut GapSideBySideWhite, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_gap_side_by_side_white_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_gap_side_by_side_white_batch( handle: *mut GapSideBySideWhite, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_gap_side_by_side_white_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gap_side_by_side_white_reset(handle: *mut GapSideBySideWhite) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_gap_side_by_side_white_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_gap_side_by_side_white_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gap_side_by_side_white_free(handle: *mut GapSideBySideWhite) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GarmanKlassVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_garman_klass_volatility_free`. #[no_mangle] pub extern "C" fn wickra_garman_klass_volatility_new( period: usize, trading_periods: usize, ) -> *mut GarmanKlassVolatility { match GarmanKlassVolatility::new(period, trading_periods) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_garman_klass_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_garman_klass_volatility_update( handle: *mut GarmanKlassVolatility, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_garman_klass_volatility_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_garman_klass_volatility_batch( handle: *mut GarmanKlassVolatility, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_garman_klass_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_garman_klass_volatility_reset(handle: *mut GarmanKlassVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_garman_klass_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_garman_klass_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_garman_klass_volatility_free(handle: *mut GarmanKlassVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Gartley` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_gartley_free`. #[no_mangle] pub extern "C" fn wickra_gartley_new() -> *mut Gartley { Box::into_raw(Box::new(Gartley::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_gartley_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gartley_update( handle: *mut Gartley, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_gartley_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_gartley_batch( handle: *mut Gartley, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_gartley_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gartley_reset(handle: *mut Gartley) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_gartley_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_gartley_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gartley_free(handle: *mut Gartley) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GravestoneDoji` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_gravestone_doji_free`. #[no_mangle] pub extern "C" fn wickra_gravestone_doji_new() -> *mut GravestoneDoji { Box::into_raw(Box::new(GravestoneDoji::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_gravestone_doji_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gravestone_doji_update( handle: *mut GravestoneDoji, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_gravestone_doji_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_gravestone_doji_batch( handle: *mut GravestoneDoji, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_gravestone_doji_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gravestone_doji_reset(handle: *mut GravestoneDoji) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_gravestone_doji_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_gravestone_doji_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gravestone_doji_free(handle: *mut GravestoneDoji) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Hammer` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hammer_free`. #[no_mangle] pub extern "C" fn wickra_hammer_new() -> *mut Hammer { Box::into_raw(Box::new(Hammer::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_hammer_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hammer_update( handle: *mut Hammer, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_hammer_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_hammer_batch( handle: *mut Hammer, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hammer_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hammer_reset(handle: *mut Hammer) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hammer_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hammer_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hammer_free(handle: *mut Hammer) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HangingMan` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hanging_man_free`. #[no_mangle] pub extern "C" fn wickra_hanging_man_new() -> *mut HangingMan { Box::into_raw(Box::new(HangingMan::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_hanging_man_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hanging_man_update( handle: *mut HangingMan, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_hanging_man_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_hanging_man_batch( handle: *mut HangingMan, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hanging_man_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hanging_man_reset(handle: *mut HangingMan) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hanging_man_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hanging_man_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hanging_man_free(handle: *mut HangingMan) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Harami` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_harami_free`. #[no_mangle] pub extern "C" fn wickra_harami_new() -> *mut Harami { Box::into_raw(Box::new(Harami::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_harami_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_harami_update( handle: *mut Harami, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_harami_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_harami_batch( handle: *mut Harami, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_harami_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_harami_reset(handle: *mut Harami) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_harami_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_harami_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_harami_free(handle: *mut Harami) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HaramiCross` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_harami_cross_free`. #[no_mangle] pub extern "C" fn wickra_harami_cross_new() -> *mut HaramiCross { Box::into_raw(Box::new(HaramiCross::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_harami_cross_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_harami_cross_update( handle: *mut HaramiCross, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_harami_cross_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_harami_cross_batch( handle: *mut HaramiCross, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_harami_cross_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_harami_cross_reset(handle: *mut HaramiCross) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_harami_cross_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_harami_cross_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_harami_cross_free(handle: *mut HaramiCross) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HeadAndShoulders` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_head_and_shoulders_free`. #[no_mangle] pub extern "C" fn wickra_head_and_shoulders_new() -> *mut HeadAndShoulders { Box::into_raw(Box::new(HeadAndShoulders::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_head_and_shoulders_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_head_and_shoulders_update( handle: *mut HeadAndShoulders, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_head_and_shoulders_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_head_and_shoulders_batch( handle: *mut HeadAndShoulders, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_head_and_shoulders_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_head_and_shoulders_reset(handle: *mut HeadAndShoulders) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_head_and_shoulders_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_head_and_shoulders_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_head_and_shoulders_free(handle: *mut HeadAndShoulders) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HeikinAshiOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_heikin_ashi_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_heikin_ashi_oscillator_new(period: usize) -> *mut HeikinAshiOscillator { match HeikinAshiOscillator::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_heikin_ashi_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_update( handle: *mut HeikinAshiOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_heikin_ashi_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_batch( handle: *mut HeikinAshiOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_heikin_ashi_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_reset(handle: *mut HeikinAshiOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_heikin_ashi_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_heikin_ashi_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_heikin_ashi_oscillator_free(handle: *mut HeikinAshiOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HighLowRange` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_high_low_range_free`. #[no_mangle] pub extern "C" fn wickra_high_low_range_new() -> *mut HighLowRange { Box::into_raw(Box::new(HighLowRange::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_high_low_range_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_range_update( handle: *mut HighLowRange, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_high_low_range_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_range_batch( handle: *mut HighLowRange, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_high_low_range_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_range_reset(handle: *mut HighLowRange) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_high_low_range_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_high_low_range_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_range_free(handle: *mut HighLowRange) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HighWave` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_high_wave_free`. #[no_mangle] pub extern "C" fn wickra_high_wave_new() -> *mut HighWave { Box::into_raw(Box::new(HighWave::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_high_wave_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_wave_update( handle: *mut HighWave, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_high_wave_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_high_wave_batch( handle: *mut HighWave, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_high_wave_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_wave_reset(handle: *mut HighWave) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_high_wave_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_high_wave_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_wave_free(handle: *mut HighWave) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Hikkake` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hikkake_free`. #[no_mangle] pub extern "C" fn wickra_hikkake_new() -> *mut Hikkake { Box::into_raw(Box::new(Hikkake::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_hikkake_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_update( handle: *mut Hikkake, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_hikkake_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_batch( handle: *mut Hikkake, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hikkake_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_reset(handle: *mut Hikkake) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hikkake_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hikkake_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_free(handle: *mut Hikkake) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HikkakeModified` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hikkake_modified_free`. #[no_mangle] pub extern "C" fn wickra_hikkake_modified_new() -> *mut HikkakeModified { Box::into_raw(Box::new(HikkakeModified::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_hikkake_modified_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_modified_update( handle: *mut HikkakeModified, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_hikkake_modified_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_modified_batch( handle: *mut HikkakeModified, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hikkake_modified_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_modified_reset(handle: *mut HikkakeModified) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hikkake_modified_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hikkake_modified_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hikkake_modified_free(handle: *mut HikkakeModified) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HiLoActivator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hi_lo_activator_free`. #[no_mangle] pub extern "C" fn wickra_hi_lo_activator_new(period: usize) -> *mut HiLoActivator { match HiLoActivator::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_hi_lo_activator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hi_lo_activator_update( handle: *mut HiLoActivator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_hi_lo_activator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_hi_lo_activator_batch( handle: *mut HiLoActivator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hi_lo_activator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hi_lo_activator_reset(handle: *mut HiLoActivator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hi_lo_activator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hi_lo_activator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hi_lo_activator_free(handle: *mut HiLoActivator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HomingPigeon` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_homing_pigeon_free`. #[no_mangle] pub extern "C" fn wickra_homing_pigeon_new() -> *mut HomingPigeon { Box::into_raw(Box::new(HomingPigeon::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_homing_pigeon_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_homing_pigeon_update( handle: *mut HomingPigeon, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_homing_pigeon_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_homing_pigeon_batch( handle: *mut HomingPigeon, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_homing_pigeon_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_homing_pigeon_reset(handle: *mut HomingPigeon) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_homing_pigeon_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_homing_pigeon_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_homing_pigeon_free(handle: *mut HomingPigeon) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `IdenticalThreeCrows` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_identical_three_crows_free`. #[no_mangle] pub extern "C" fn wickra_identical_three_crows_new() -> *mut IdenticalThreeCrows { Box::into_raw(Box::new(IdenticalThreeCrows::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_identical_three_crows_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_identical_three_crows_update( handle: *mut IdenticalThreeCrows, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_identical_three_crows_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_identical_three_crows_batch( handle: *mut IdenticalThreeCrows, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_identical_three_crows_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_identical_three_crows_reset(handle: *mut IdenticalThreeCrows) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_identical_three_crows_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_identical_three_crows_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_identical_three_crows_free(handle: *mut IdenticalThreeCrows) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `InNeck` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_in_neck_free`. #[no_mangle] pub extern "C" fn wickra_in_neck_new() -> *mut InNeck { Box::into_raw(Box::new(InNeck::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_in_neck_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_in_neck_update( handle: *mut InNeck, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_in_neck_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_in_neck_batch( handle: *mut InNeck, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_in_neck_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_in_neck_reset(handle: *mut InNeck) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_in_neck_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_in_neck_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_in_neck_free(handle: *mut InNeck) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Inertia` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_inertia_free`. #[no_mangle] pub extern "C" fn wickra_inertia_new(rvi_period: usize, linreg_period: usize) -> *mut Inertia { match Inertia::new(rvi_period, linreg_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_inertia_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inertia_update( handle: *mut Inertia, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_inertia_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_inertia_batch( handle: *mut Inertia, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_inertia_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inertia_reset(handle: *mut Inertia) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_inertia_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_inertia_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inertia_free(handle: *mut Inertia) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `IntradayIntensity` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_intraday_intensity_free`. #[no_mangle] pub extern "C" fn wickra_intraday_intensity_new() -> *mut IntradayIntensity { Box::into_raw(Box::new(IntradayIntensity::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_intraday_intensity_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_intensity_update( handle: *mut IntradayIntensity, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_intraday_intensity_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_intensity_batch( handle: *mut IntradayIntensity, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_intraday_intensity_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_intensity_reset(handle: *mut IntradayIntensity) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_intraday_intensity_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_intraday_intensity_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_intensity_free(handle: *mut IntradayIntensity) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `IntradayMomentumIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_intraday_momentum_index_free`. #[no_mangle] pub extern "C" fn wickra_intraday_momentum_index_new(period: usize) -> *mut IntradayMomentumIndex { match IntradayMomentumIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_intraday_momentum_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_momentum_index_update( handle: *mut IntradayMomentumIndex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_intraday_momentum_index_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_momentum_index_batch( handle: *mut IntradayMomentumIndex, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_intraday_momentum_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_momentum_index_reset(handle: *mut IntradayMomentumIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_intraday_momentum_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_intraday_momentum_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_momentum_index_free(handle: *mut IntradayMomentumIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `InvertedHammer` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_inverted_hammer_free`. #[no_mangle] pub extern "C" fn wickra_inverted_hammer_new() -> *mut InvertedHammer { Box::into_raw(Box::new(InvertedHammer::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_inverted_hammer_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inverted_hammer_update( handle: *mut InvertedHammer, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_inverted_hammer_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_inverted_hammer_batch( handle: *mut InvertedHammer, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_inverted_hammer_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inverted_hammer_reset(handle: *mut InvertedHammer) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_inverted_hammer_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_inverted_hammer_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_inverted_hammer_free(handle: *mut InvertedHammer) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Kicking` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kicking_free`. #[no_mangle] pub extern "C" fn wickra_kicking_new() -> *mut Kicking { Box::into_raw(Box::new(Kicking::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_kicking_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_update( handle: *mut Kicking, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_kicking_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_batch( handle: *mut Kicking, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kicking_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_reset(handle: *mut Kicking) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kicking_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kicking_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_free(handle: *mut Kicking) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KickingByLength` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kicking_by_length_free`. #[no_mangle] pub extern "C" fn wickra_kicking_by_length_new() -> *mut KickingByLength { Box::into_raw(Box::new(KickingByLength::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_kicking_by_length_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_by_length_update( handle: *mut KickingByLength, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_kicking_by_length_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_by_length_batch( handle: *mut KickingByLength, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kicking_by_length_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_by_length_reset(handle: *mut KickingByLength) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kicking_by_length_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kicking_by_length_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kicking_by_length_free(handle: *mut KickingByLength) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Kvo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kvo_free`. #[no_mangle] pub extern "C" fn wickra_kvo_new(fast: usize, slow: usize) -> *mut Kvo { match Kvo::new(fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_kvo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kvo_update( handle: *mut Kvo, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_kvo_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_kvo_batch( handle: *mut Kvo, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kvo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kvo_reset(handle: *mut Kvo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kvo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kvo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kvo_free(handle: *mut Kvo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LadderBottom` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ladder_bottom_free`. #[no_mangle] pub extern "C" fn wickra_ladder_bottom_new() -> *mut LadderBottom { Box::into_raw(Box::new(LadderBottom::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_ladder_bottom_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ladder_bottom_update( handle: *mut LadderBottom, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_ladder_bottom_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_ladder_bottom_batch( handle: *mut LadderBottom, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ladder_bottom_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ladder_bottom_reset(handle: *mut LadderBottom) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ladder_bottom_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ladder_bottom_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ladder_bottom_free(handle: *mut LadderBottom) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LongLeggedDoji` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_long_legged_doji_free`. #[no_mangle] pub extern "C" fn wickra_long_legged_doji_new() -> *mut LongLeggedDoji { Box::into_raw(Box::new(LongLeggedDoji::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_long_legged_doji_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_legged_doji_update( handle: *mut LongLeggedDoji, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_long_legged_doji_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_long_legged_doji_batch( handle: *mut LongLeggedDoji, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_long_legged_doji_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_legged_doji_reset(handle: *mut LongLeggedDoji) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_long_legged_doji_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_long_legged_doji_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_legged_doji_free(handle: *mut LongLeggedDoji) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LongLine` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_long_line_free`. #[no_mangle] pub extern "C" fn wickra_long_line_new() -> *mut LongLine { Box::into_raw(Box::new(LongLine::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_long_line_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_line_update( handle: *mut LongLine, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_long_line_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_long_line_batch( handle: *mut LongLine, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_long_line_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_line_reset(handle: *mut LongLine) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_long_line_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_long_line_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_line_free(handle: *mut LongLine) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MarketFacilitationIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_market_facilitation_index_free`. #[no_mangle] pub extern "C" fn wickra_market_facilitation_index_new() -> *mut MarketFacilitationIndex { Box::into_raw(Box::new(MarketFacilitationIndex::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_market_facilitation_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_market_facilitation_index_update( handle: *mut MarketFacilitationIndex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_market_facilitation_index_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_market_facilitation_index_batch( handle: *mut MarketFacilitationIndex, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_market_facilitation_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_market_facilitation_index_reset( handle: *mut MarketFacilitationIndex, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_market_facilitation_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_market_facilitation_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_market_facilitation_index_free( handle: *mut MarketFacilitationIndex, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Marubozu` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_marubozu_free`. #[no_mangle] pub extern "C" fn wickra_marubozu_new() -> *mut Marubozu { Box::into_raw(Box::new(Marubozu::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_marubozu_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_marubozu_update( handle: *mut Marubozu, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_marubozu_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_marubozu_batch( handle: *mut Marubozu, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_marubozu_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_marubozu_reset(handle: *mut Marubozu) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_marubozu_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_marubozu_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_marubozu_free(handle: *mut Marubozu) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MassIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mass_index_free`. #[no_mangle] pub extern "C" fn wickra_mass_index_new(ema_period: usize, sum_period: usize) -> *mut MassIndex { match MassIndex::new(ema_period, sum_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_mass_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mass_index_update( handle: *mut MassIndex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_mass_index_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_mass_index_batch( handle: *mut MassIndex, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mass_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mass_index_reset(handle: *mut MassIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mass_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mass_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mass_index_free(handle: *mut MassIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MatHold` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mat_hold_free`. #[no_mangle] pub extern "C" fn wickra_mat_hold_new() -> *mut MatHold { Box::into_raw(Box::new(MatHold::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_mat_hold_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mat_hold_update( handle: *mut MatHold, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_mat_hold_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_mat_hold_batch( handle: *mut MatHold, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mat_hold_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mat_hold_reset(handle: *mut MatHold) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mat_hold_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mat_hold_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mat_hold_free(handle: *mut MatHold) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MatchingLow` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_matching_low_free`. #[no_mangle] pub extern "C" fn wickra_matching_low_new() -> *mut MatchingLow { Box::into_raw(Box::new(MatchingLow::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_matching_low_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_matching_low_update( handle: *mut MatchingLow, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_matching_low_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_matching_low_batch( handle: *mut MatchingLow, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_matching_low_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_matching_low_reset(handle: *mut MatchingLow) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_matching_low_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_matching_low_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_matching_low_free(handle: *mut MatchingLow) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MedianPrice` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_median_price_free`. #[no_mangle] pub extern "C" fn wickra_median_price_new() -> *mut MedianPrice { Box::into_raw(Box::new(MedianPrice::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_median_price_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_price_update( handle: *mut MedianPrice, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_median_price_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_median_price_batch( handle: *mut MedianPrice, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_median_price_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_price_reset(handle: *mut MedianPrice) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_median_price_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_median_price_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_price_free(handle: *mut MedianPrice) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Mfi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mfi_free`. #[no_mangle] pub extern "C" fn wickra_mfi_new(period: usize) -> *mut Mfi { match Mfi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_mfi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mfi_update( handle: *mut Mfi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_mfi_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_mfi_batch( handle: *mut Mfi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mfi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mfi_reset(handle: *mut Mfi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mfi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mfi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mfi_free(handle: *mut Mfi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MidPrice` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mid_price_free`. #[no_mangle] pub extern "C" fn wickra_mid_price_new(period: usize) -> *mut MidPrice { match MidPrice::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_mid_price_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mid_price_update( handle: *mut MidPrice, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_mid_price_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_mid_price_batch( handle: *mut MidPrice, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mid_price_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mid_price_reset(handle: *mut MidPrice) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mid_price_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mid_price_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mid_price_free(handle: *mut MidPrice) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MinusDi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_minus_di_free`. #[no_mangle] pub extern "C" fn wickra_minus_di_new(period: usize) -> *mut MinusDi { match MinusDi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_minus_di_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_minus_di_update( handle: *mut MinusDi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_minus_di_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_minus_di_batch( handle: *mut MinusDi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_minus_di_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_minus_di_reset(handle: *mut MinusDi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_minus_di_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_minus_di_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_minus_di_free(handle: *mut MinusDi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MinusDm` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_minus_dm_free`. #[no_mangle] pub extern "C" fn wickra_minus_dm_new(period: usize) -> *mut MinusDm { match MinusDm::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_minus_dm_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_minus_dm_update( handle: *mut MinusDm, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_minus_dm_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_minus_dm_batch( handle: *mut MinusDm, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_minus_dm_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_minus_dm_reset(handle: *mut MinusDm) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_minus_dm_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_minus_dm_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_minus_dm_free(handle: *mut MinusDm) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MorningDojiStar` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_morning_doji_star_free`. #[no_mangle] pub extern "C" fn wickra_morning_doji_star_new() -> *mut MorningDojiStar { Box::into_raw(Box::new(MorningDojiStar::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_morning_doji_star_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_morning_doji_star_update( handle: *mut MorningDojiStar, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_morning_doji_star_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_morning_doji_star_batch( handle: *mut MorningDojiStar, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_morning_doji_star_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_morning_doji_star_reset(handle: *mut MorningDojiStar) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_morning_doji_star_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_morning_doji_star_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_morning_doji_star_free(handle: *mut MorningDojiStar) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MorningEveningStar` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_morning_evening_star_free`. #[no_mangle] pub extern "C" fn wickra_morning_evening_star_new() -> *mut MorningEveningStar { Box::into_raw(Box::new(MorningEveningStar::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_morning_evening_star_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_morning_evening_star_update( handle: *mut MorningEveningStar, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_morning_evening_star_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_morning_evening_star_batch( handle: *mut MorningEveningStar, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_morning_evening_star_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_morning_evening_star_reset(handle: *mut MorningEveningStar) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_morning_evening_star_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_morning_evening_star_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_morning_evening_star_free(handle: *mut MorningEveningStar) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `NakedPoc` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_naked_poc_free`. #[no_mangle] pub extern "C" fn wickra_naked_poc_new(session_len: usize, bins: usize) -> *mut NakedPoc { match NakedPoc::new(session_len, bins) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_naked_poc_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_naked_poc_update( handle: *mut NakedPoc, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_naked_poc_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_naked_poc_batch( handle: *mut NakedPoc, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_naked_poc_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_naked_poc_reset(handle: *mut NakedPoc) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_naked_poc_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_naked_poc_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_naked_poc_free(handle: *mut NakedPoc) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Natr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_natr_free`. #[no_mangle] pub extern "C" fn wickra_natr_new(period: usize) -> *mut Natr { match Natr::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_natr_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_natr_update( handle: *mut Natr, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_natr_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_natr_batch( handle: *mut Natr, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_natr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_natr_reset(handle: *mut Natr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_natr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_natr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_natr_free(handle: *mut Natr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `NewPriceLines` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_new_price_lines_free`. #[no_mangle] pub extern "C" fn wickra_new_price_lines_new(count: usize) -> *mut NewPriceLines { match NewPriceLines::new(count) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_new_price_lines_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_new_price_lines_update( handle: *mut NewPriceLines, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_new_price_lines_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_new_price_lines_batch( handle: *mut NewPriceLines, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_new_price_lines_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_new_price_lines_reset(handle: *mut NewPriceLines) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_new_price_lines_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_new_price_lines_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_new_price_lines_free(handle: *mut NewPriceLines) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Nvi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_nvi_free`. #[no_mangle] pub extern "C" fn wickra_nvi_new() -> *mut Nvi { Box::into_raw(Box::new(Nvi::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_nvi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_nvi_update( handle: *mut Nvi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_nvi_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_nvi_batch( handle: *mut Nvi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_nvi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_nvi_reset(handle: *mut Nvi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_nvi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_nvi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_nvi_free(handle: *mut Nvi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Obv` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_obv_free`. #[no_mangle] pub extern "C" fn wickra_obv_new() -> *mut Obv { Box::into_raw(Box::new(Obv::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_obv_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_obv_update( handle: *mut Obv, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_obv_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_obv_batch( handle: *mut Obv, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_obv_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_obv_reset(handle: *mut Obv) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_obv_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_obv_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_obv_free(handle: *mut Obv) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OnNeck` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_on_neck_free`. #[no_mangle] pub extern "C" fn wickra_on_neck_new() -> *mut OnNeck { Box::into_raw(Box::new(OnNeck::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_on_neck_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_on_neck_update( handle: *mut OnNeck, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_on_neck_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_on_neck_batch( handle: *mut OnNeck, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_on_neck_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_on_neck_reset(handle: *mut OnNeck) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_on_neck_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_on_neck_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_on_neck_free(handle: *mut OnNeck) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OpeningMarubozu` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_opening_marubozu_free`. #[no_mangle] pub extern "C" fn wickra_opening_marubozu_new() -> *mut OpeningMarubozu { Box::into_raw(Box::new(OpeningMarubozu::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_opening_marubozu_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_opening_marubozu_update( handle: *mut OpeningMarubozu, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_opening_marubozu_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_opening_marubozu_batch( handle: *mut OpeningMarubozu, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_opening_marubozu_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_opening_marubozu_reset(handle: *mut OpeningMarubozu) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_opening_marubozu_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_opening_marubozu_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_opening_marubozu_free(handle: *mut OpeningMarubozu) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OvernightGap` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_overnight_gap_free`. #[no_mangle] pub extern "C" fn wickra_overnight_gap_new(utc_offset_minutes: i32) -> *mut OvernightGap { Box::into_raw(Box::new(OvernightGap::new(utc_offset_minutes))) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_overnight_gap_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_overnight_gap_update( handle: *mut OvernightGap, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_overnight_gap_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_overnight_gap_batch( handle: *mut OvernightGap, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_overnight_gap_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_overnight_gap_reset(handle: *mut OvernightGap) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_overnight_gap_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_overnight_gap_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_overnight_gap_free(handle: *mut OvernightGap) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ParkinsonVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_parkinson_volatility_free`. #[no_mangle] pub extern "C" fn wickra_parkinson_volatility_new( period: usize, trading_periods: usize, ) -> *mut ParkinsonVolatility { match ParkinsonVolatility::new(period, trading_periods) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_parkinson_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_parkinson_volatility_update( handle: *mut ParkinsonVolatility, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_parkinson_volatility_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_parkinson_volatility_batch( handle: *mut ParkinsonVolatility, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_parkinson_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_parkinson_volatility_reset(handle: *mut ParkinsonVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_parkinson_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_parkinson_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_parkinson_volatility_free(handle: *mut ParkinsonVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Pgo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pgo_free`. #[no_mangle] pub extern "C" fn wickra_pgo_new(period: usize) -> *mut Pgo { match Pgo::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_pgo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pgo_update( handle: *mut Pgo, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_pgo_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_pgo_batch( handle: *mut Pgo, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pgo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pgo_reset(handle: *mut Pgo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pgo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pgo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pgo_free(handle: *mut Pgo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PiercingDarkCloud` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_piercing_dark_cloud_free`. #[no_mangle] pub extern "C" fn wickra_piercing_dark_cloud_new() -> *mut PiercingDarkCloud { Box::into_raw(Box::new(PiercingDarkCloud::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_piercing_dark_cloud_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_piercing_dark_cloud_update( handle: *mut PiercingDarkCloud, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_piercing_dark_cloud_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_piercing_dark_cloud_batch( handle: *mut PiercingDarkCloud, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_piercing_dark_cloud_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_piercing_dark_cloud_reset(handle: *mut PiercingDarkCloud) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_piercing_dark_cloud_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_piercing_dark_cloud_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_piercing_dark_cloud_free(handle: *mut PiercingDarkCloud) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PivotReversal` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pivot_reversal_free`. #[no_mangle] pub extern "C" fn wickra_pivot_reversal_new(left: usize, right: usize) -> *mut PivotReversal { match PivotReversal::new(left, right) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_pivot_reversal_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pivot_reversal_update( handle: *mut PivotReversal, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_pivot_reversal_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_pivot_reversal_batch( handle: *mut PivotReversal, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pivot_reversal_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pivot_reversal_reset(handle: *mut PivotReversal) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pivot_reversal_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pivot_reversal_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pivot_reversal_free(handle: *mut PivotReversal) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PlusDi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_plus_di_free`. #[no_mangle] pub extern "C" fn wickra_plus_di_new(period: usize) -> *mut PlusDi { match PlusDi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_plus_di_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_plus_di_update( handle: *mut PlusDi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_plus_di_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_plus_di_batch( handle: *mut PlusDi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_plus_di_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_plus_di_reset(handle: *mut PlusDi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_plus_di_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_plus_di_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_plus_di_free(handle: *mut PlusDi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PlusDm` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_plus_dm_free`. #[no_mangle] pub extern "C" fn wickra_plus_dm_new(period: usize) -> *mut PlusDm { match PlusDm::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_plus_dm_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_plus_dm_update( handle: *mut PlusDm, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_plus_dm_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_plus_dm_batch( handle: *mut PlusDm, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_plus_dm_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_plus_dm_reset(handle: *mut PlusDm) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_plus_dm_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_plus_dm_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_plus_dm_free(handle: *mut PlusDm) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ProfileShape` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_profile_shape_free`. #[no_mangle] pub extern "C" fn wickra_profile_shape_new(period: usize, bins: usize) -> *mut ProfileShape { match ProfileShape::new(period, bins) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_profile_shape_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_profile_shape_update( handle: *mut ProfileShape, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_profile_shape_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_profile_shape_batch( handle: *mut ProfileShape, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_profile_shape_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_profile_shape_reset(handle: *mut ProfileShape) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_profile_shape_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_profile_shape_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_profile_shape_free(handle: *mut ProfileShape) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ProjectionOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_projection_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_projection_oscillator_new(period: usize) -> *mut ProjectionOscillator { match ProjectionOscillator::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_projection_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_projection_oscillator_update( handle: *mut ProjectionOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_projection_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_projection_oscillator_batch( handle: *mut ProjectionOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_projection_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_projection_oscillator_reset(handle: *mut ProjectionOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_projection_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_projection_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_projection_oscillator_free(handle: *mut ProjectionOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Psar` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_psar_free`. #[no_mangle] pub extern "C" fn wickra_psar_new(af_start: f64, af_step: f64, af_max: f64) -> *mut Psar { match Psar::new(af_start, af_step, af_max) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_psar_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_psar_update( handle: *mut Psar, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_psar_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_psar_batch( handle: *mut Psar, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_psar_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_psar_reset(handle: *mut Psar) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_psar_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_psar_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_psar_free(handle: *mut Psar) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Pvi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pvi_free`. #[no_mangle] pub extern "C" fn wickra_pvi_new() -> *mut Pvi { Box::into_raw(Box::new(Pvi::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_pvi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pvi_update( handle: *mut Pvi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_pvi_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_pvi_batch( handle: *mut Pvi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pvi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pvi_reset(handle: *mut Pvi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pvi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pvi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pvi_free(handle: *mut Pvi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Qstick` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_qstick_free`. #[no_mangle] pub extern "C" fn wickra_qstick_new(period: usize) -> *mut Qstick { match Qstick::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_qstick_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_qstick_update( handle: *mut Qstick, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_qstick_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_qstick_batch( handle: *mut Qstick, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_qstick_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_qstick_reset(handle: *mut Qstick) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_qstick_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_qstick_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_qstick_free(handle: *mut Qstick) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RectangleRange` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rectangle_range_free`. #[no_mangle] pub extern "C" fn wickra_rectangle_range_new() -> *mut RectangleRange { Box::into_raw(Box::new(RectangleRange::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_rectangle_range_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rectangle_range_update( handle: *mut RectangleRange, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_rectangle_range_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_rectangle_range_batch( handle: *mut RectangleRange, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rectangle_range_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rectangle_range_reset(handle: *mut RectangleRange) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rectangle_range_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rectangle_range_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rectangle_range_free(handle: *mut RectangleRange) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RickshawMan` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rickshaw_man_free`. #[no_mangle] pub extern "C" fn wickra_rickshaw_man_new() -> *mut RickshawMan { Box::into_raw(Box::new(RickshawMan::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_rickshaw_man_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rickshaw_man_update( handle: *mut RickshawMan, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_rickshaw_man_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_rickshaw_man_batch( handle: *mut RickshawMan, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rickshaw_man_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rickshaw_man_reset(handle: *mut RickshawMan) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rickshaw_man_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rickshaw_man_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rickshaw_man_free(handle: *mut RickshawMan) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RisingThreeMethods` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rising_three_methods_free`. #[no_mangle] pub extern "C" fn wickra_rising_three_methods_new() -> *mut RisingThreeMethods { Box::into_raw(Box::new(RisingThreeMethods::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_rising_three_methods_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rising_three_methods_update( handle: *mut RisingThreeMethods, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_rising_three_methods_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_rising_three_methods_batch( handle: *mut RisingThreeMethods, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rising_three_methods_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rising_three_methods_reset(handle: *mut RisingThreeMethods) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rising_three_methods_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rising_three_methods_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rising_three_methods_free(handle: *mut RisingThreeMethods) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RogersSatchellVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rogers_satchell_volatility_free`. #[no_mangle] pub extern "C" fn wickra_rogers_satchell_volatility_new( period: usize, trading_periods: usize, ) -> *mut RogersSatchellVolatility { match RogersSatchellVolatility::new(period, trading_periods) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_rogers_satchell_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rogers_satchell_volatility_update( handle: *mut RogersSatchellVolatility, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_rogers_satchell_volatility_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_rogers_satchell_volatility_batch( handle: *mut RogersSatchellVolatility, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rogers_satchell_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rogers_satchell_volatility_reset( handle: *mut RogersSatchellVolatility, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rogers_satchell_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rogers_satchell_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rogers_satchell_volatility_free( handle: *mut RogersSatchellVolatility, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rvi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rvi_free`. #[no_mangle] pub extern "C" fn wickra_rvi_new(period: usize) -> *mut Rvi { match Rvi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_rvi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_update( handle: *mut Rvi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_rvi_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_batch( handle: *mut Rvi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rvi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_reset(handle: *mut Rvi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rvi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rvi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rvi_free(handle: *mut Rvi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SarExt` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_sar_ext_free`. #[no_mangle] pub extern "C" fn wickra_sar_ext_new( start_value: f64, offset_on_reverse: f64, accel_init_long: f64, accel_long: f64, accel_max_long: f64, accel_init_short: f64, accel_short: f64, accel_max_short: f64, ) -> *mut SarExt { match SarExt::new( start_value, offset_on_reverse, accel_init_long, accel_long, accel_max_long, accel_init_short, accel_short, accel_max_short, ) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_sar_ext_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sar_ext_update( handle: *mut SarExt, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_sar_ext_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_sar_ext_batch( handle: *mut SarExt, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_sar_ext_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sar_ext_reset(handle: *mut SarExt) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_sar_ext_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_sar_ext_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_sar_ext_free(handle: *mut SarExt) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SeasonalZScore` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_seasonal_z_score_free`. #[no_mangle] pub extern "C" fn wickra_seasonal_z_score_new(utc_offset_minutes: i32) -> *mut SeasonalZScore { Box::into_raw(Box::new(SeasonalZScore::new(utc_offset_minutes))) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_seasonal_z_score_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_seasonal_z_score_update( handle: *mut SeasonalZScore, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_seasonal_z_score_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_seasonal_z_score_batch( handle: *mut SeasonalZScore, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_seasonal_z_score_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_seasonal_z_score_reset(handle: *mut SeasonalZScore) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_seasonal_z_score_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_seasonal_z_score_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_seasonal_z_score_free(handle: *mut SeasonalZScore) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SeparatingLines` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_separating_lines_free`. #[no_mangle] pub extern "C" fn wickra_separating_lines_new() -> *mut SeparatingLines { Box::into_raw(Box::new(SeparatingLines::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_separating_lines_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_separating_lines_update( handle: *mut SeparatingLines, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_separating_lines_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_separating_lines_batch( handle: *mut SeparatingLines, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_separating_lines_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_separating_lines_reset(handle: *mut SeparatingLines) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_separating_lines_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_separating_lines_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_separating_lines_free(handle: *mut SeparatingLines) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SessionVwap` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_session_vwap_free`. #[no_mangle] pub extern "C" fn wickra_session_vwap_new(utc_offset_minutes: i32) -> *mut SessionVwap { Box::into_raw(Box::new(SessionVwap::new(utc_offset_minutes))) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_session_vwap_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_vwap_update( handle: *mut SessionVwap, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_session_vwap_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_session_vwap_batch( handle: *mut SessionVwap, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_session_vwap_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_vwap_reset(handle: *mut SessionVwap) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_session_vwap_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_session_vwap_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_vwap_free(handle: *mut SessionVwap) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Shark` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_shark_free`. #[no_mangle] pub extern "C" fn wickra_shark_new() -> *mut Shark { Box::into_raw(Box::new(Shark::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_shark_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shark_update( handle: *mut Shark, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_shark_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_shark_batch( handle: *mut Shark, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_shark_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shark_reset(handle: *mut Shark) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_shark_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_shark_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shark_free(handle: *mut Shark) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ShootingStar` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_shooting_star_free`. #[no_mangle] pub extern "C" fn wickra_shooting_star_new() -> *mut ShootingStar { Box::into_raw(Box::new(ShootingStar::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_shooting_star_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shooting_star_update( handle: *mut ShootingStar, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_shooting_star_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_shooting_star_batch( handle: *mut ShootingStar, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_shooting_star_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shooting_star_reset(handle: *mut ShootingStar) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_shooting_star_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_shooting_star_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_shooting_star_free(handle: *mut ShootingStar) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ShortLine` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_short_line_free`. #[no_mangle] pub extern "C" fn wickra_short_line_new() -> *mut ShortLine { Box::into_raw(Box::new(ShortLine::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_short_line_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_short_line_update( handle: *mut ShortLine, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_short_line_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_short_line_batch( handle: *mut ShortLine, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_short_line_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_short_line_reset(handle: *mut ShortLine) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_short_line_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_short_line_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_short_line_free(handle: *mut ShortLine) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SinglePrints` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_single_prints_free`. #[no_mangle] pub extern "C" fn wickra_single_prints_new(period: usize, bins: usize) -> *mut SinglePrints { match SinglePrints::new(period, bins) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_single_prints_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_single_prints_update( handle: *mut SinglePrints, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_single_prints_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_single_prints_batch( handle: *mut SinglePrints, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_single_prints_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_single_prints_reset(handle: *mut SinglePrints) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_single_prints_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_single_prints_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_single_prints_free(handle: *mut SinglePrints) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Smi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_smi_free`. #[no_mangle] pub extern "C" fn wickra_smi_new(period: usize, d_period: usize, d2_period: usize) -> *mut Smi { match Smi::new(period, d_period, d2_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_smi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smi_update( handle: *mut Smi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_smi_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_smi_batch( handle: *mut Smi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_smi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smi_reset(handle: *mut Smi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_smi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_smi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smi_free(handle: *mut Smi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SpinningTop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_spinning_top_free`. #[no_mangle] pub extern "C" fn wickra_spinning_top_new() -> *mut SpinningTop { Box::into_raw(Box::new(SpinningTop::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_spinning_top_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spinning_top_update( handle: *mut SpinningTop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_spinning_top_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_spinning_top_batch( handle: *mut SpinningTop, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_spinning_top_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spinning_top_reset(handle: *mut SpinningTop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_spinning_top_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_spinning_top_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spinning_top_free(handle: *mut SpinningTop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StalledPattern` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_stalled_pattern_free`. #[no_mangle] pub extern "C" fn wickra_stalled_pattern_new() -> *mut StalledPattern { Box::into_raw(Box::new(StalledPattern::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_stalled_pattern_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stalled_pattern_update( handle: *mut StalledPattern, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_stalled_pattern_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_stalled_pattern_batch( handle: *mut StalledPattern, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_stalled_pattern_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stalled_pattern_reset(handle: *mut StalledPattern) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_stalled_pattern_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_stalled_pattern_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stalled_pattern_free(handle: *mut StalledPattern) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StickSandwich` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_stick_sandwich_free`. #[no_mangle] pub extern "C" fn wickra_stick_sandwich_new() -> *mut StickSandwich { Box::into_raw(Box::new(StickSandwich::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_stick_sandwich_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stick_sandwich_update( handle: *mut StickSandwich, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_stick_sandwich_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_stick_sandwich_batch( handle: *mut StickSandwich, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_stick_sandwich_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stick_sandwich_reset(handle: *mut StickSandwich) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_stick_sandwich_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_stick_sandwich_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stick_sandwich_free(handle: *mut StickSandwich) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StochasticCci` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_stochastic_cci_free`. #[no_mangle] pub extern "C" fn wickra_stochastic_cci_new(period: usize) -> *mut StochasticCci { match StochasticCci::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_stochastic_cci_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stochastic_cci_update( handle: *mut StochasticCci, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_stochastic_cci_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_stochastic_cci_batch( handle: *mut StochasticCci, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_stochastic_cci_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stochastic_cci_reset(handle: *mut StochasticCci) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_stochastic_cci_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_stochastic_cci_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stochastic_cci_free(handle: *mut StochasticCci) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Takuri` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_takuri_free`. #[no_mangle] pub extern "C" fn wickra_takuri_new() -> *mut Takuri { Box::into_raw(Box::new(Takuri::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_takuri_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_takuri_update( handle: *mut Takuri, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_takuri_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_takuri_batch( handle: *mut Takuri, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_takuri_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_takuri_reset(handle: *mut Takuri) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_takuri_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_takuri_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_takuri_free(handle: *mut Takuri) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TasukiGap` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tasuki_gap_free`. #[no_mangle] pub extern "C" fn wickra_tasuki_gap_new() -> *mut TasukiGap { Box::into_raw(Box::new(TasukiGap::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_tasuki_gap_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tasuki_gap_update( handle: *mut TasukiGap, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_tasuki_gap_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_tasuki_gap_batch( handle: *mut TasukiGap, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tasuki_gap_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tasuki_gap_reset(handle: *mut TasukiGap) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tasuki_gap_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tasuki_gap_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tasuki_gap_free(handle: *mut TasukiGap) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdCamouflage` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_camouflage_free`. #[no_mangle] pub extern "C" fn wickra_td_camouflage_new() -> *mut TdCamouflage { Box::into_raw(Box::new(TdCamouflage::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_camouflage_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_camouflage_update( handle: *mut TdCamouflage, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_camouflage_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_camouflage_batch( handle: *mut TdCamouflage, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_camouflage_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_camouflage_reset(handle: *mut TdCamouflage) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_camouflage_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_camouflage_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_camouflage_free(handle: *mut TdCamouflage) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdClop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_clop_free`. #[no_mangle] pub extern "C" fn wickra_td_clop_new() -> *mut TdClop { Box::into_raw(Box::new(TdClop::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_clop_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_clop_update( handle: *mut TdClop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_clop_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_clop_batch( handle: *mut TdClop, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_clop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_clop_reset(handle: *mut TdClop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_clop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_clop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_clop_free(handle: *mut TdClop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdClopwin` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_clopwin_free`. #[no_mangle] pub extern "C" fn wickra_td_clopwin_new() -> *mut TdClopwin { Box::into_raw(Box::new(TdClopwin::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_clopwin_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_clopwin_update( handle: *mut TdClopwin, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_clopwin_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_clopwin_batch( handle: *mut TdClopwin, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_clopwin_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_clopwin_reset(handle: *mut TdClopwin) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_clopwin_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_clopwin_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_clopwin_free(handle: *mut TdClopwin) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdCombo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_combo_free`. #[no_mangle] pub extern "C" fn wickra_td_combo_new( setup_lookback: usize, setup_target: usize, countdown_lookback: usize, countdown_target: usize, ) -> *mut TdCombo { match TdCombo::new( setup_lookback, setup_target, countdown_lookback, countdown_target, ) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_combo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_combo_update( handle: *mut TdCombo, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_combo_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_combo_batch( handle: *mut TdCombo, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_combo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_combo_reset(handle: *mut TdCombo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_combo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_combo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_combo_free(handle: *mut TdCombo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdCountdown` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_countdown_free`. #[no_mangle] pub extern "C" fn wickra_td_countdown_new( setup_lookback: usize, setup_target: usize, countdown_lookback: usize, countdown_target: usize, ) -> *mut TdCountdown { match TdCountdown::new( setup_lookback, setup_target, countdown_lookback, countdown_target, ) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_countdown_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_countdown_update( handle: *mut TdCountdown, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_countdown_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_countdown_batch( handle: *mut TdCountdown, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_countdown_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_countdown_reset(handle: *mut TdCountdown) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_countdown_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_countdown_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_countdown_free(handle: *mut TdCountdown) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdDeMarker` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_de_marker_free`. #[no_mangle] pub extern "C" fn wickra_td_de_marker_new(period: usize) -> *mut TdDeMarker { match TdDeMarker::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_de_marker_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_de_marker_update( handle: *mut TdDeMarker, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_de_marker_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_de_marker_batch( handle: *mut TdDeMarker, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_de_marker_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_de_marker_reset(handle: *mut TdDeMarker) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_de_marker_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_de_marker_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_de_marker_free(handle: *mut TdDeMarker) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdDifferential` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_differential_free`. #[no_mangle] pub extern "C" fn wickra_td_differential_new() -> *mut TdDifferential { Box::into_raw(Box::new(TdDifferential::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_differential_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_differential_update( handle: *mut TdDifferential, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_differential_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_differential_batch( handle: *mut TdDifferential, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_differential_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_differential_reset(handle: *mut TdDifferential) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_differential_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_differential_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_differential_free(handle: *mut TdDifferential) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdDWave` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_d_wave_free`. #[no_mangle] pub extern "C" fn wickra_td_d_wave_new(strength: usize) -> *mut TdDWave { match TdDWave::new(strength) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_d_wave_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_d_wave_update( handle: *mut TdDWave, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_d_wave_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_d_wave_batch( handle: *mut TdDWave, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_d_wave_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_d_wave_reset(handle: *mut TdDWave) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_d_wave_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_d_wave_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_d_wave_free(handle: *mut TdDWave) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdOpen` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_open_free`. #[no_mangle] pub extern "C" fn wickra_td_open_new() -> *mut TdOpen { Box::into_raw(Box::new(TdOpen::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_open_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_open_update( handle: *mut TdOpen, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_open_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_open_batch( handle: *mut TdOpen, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_open_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_open_reset(handle: *mut TdOpen) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_open_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_open_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_open_free(handle: *mut TdOpen) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdPressure` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_pressure_free`. #[no_mangle] pub extern "C" fn wickra_td_pressure_new(period: usize) -> *mut TdPressure { match TdPressure::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_pressure_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_pressure_update( handle: *mut TdPressure, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_pressure_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_pressure_batch( handle: *mut TdPressure, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_pressure_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_pressure_reset(handle: *mut TdPressure) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_pressure_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_pressure_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_pressure_free(handle: *mut TdPressure) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdPropulsion` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_propulsion_free`. #[no_mangle] pub extern "C" fn wickra_td_propulsion_new() -> *mut TdPropulsion { Box::into_raw(Box::new(TdPropulsion::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_propulsion_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_propulsion_update( handle: *mut TdPropulsion, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_propulsion_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_propulsion_batch( handle: *mut TdPropulsion, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_propulsion_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_propulsion_reset(handle: *mut TdPropulsion) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_propulsion_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_propulsion_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_propulsion_free(handle: *mut TdPropulsion) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdRei` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_rei_free`. #[no_mangle] pub extern "C" fn wickra_td_rei_new(period: usize) -> *mut TdRei { match TdRei::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_rei_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_rei_update( handle: *mut TdRei, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_rei_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_rei_batch( handle: *mut TdRei, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_rei_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_rei_reset(handle: *mut TdRei) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_rei_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_rei_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_rei_free(handle: *mut TdRei) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdSetup` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_setup_free`. #[no_mangle] pub extern "C" fn wickra_td_setup_new(lookback: usize, target: usize) -> *mut TdSetup { match TdSetup::new(lookback, target) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_setup_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_setup_update( handle: *mut TdSetup, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_setup_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_setup_batch( handle: *mut TdSetup, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_setup_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_setup_reset(handle: *mut TdSetup) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_setup_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_setup_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_setup_free(handle: *mut TdSetup) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdTrap` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_trap_free`. #[no_mangle] pub extern "C" fn wickra_td_trap_new() -> *mut TdTrap { Box::into_raw(Box::new(TdTrap::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_td_trap_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_trap_update( handle: *mut TdTrap, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_td_trap_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_td_trap_batch( handle: *mut TdTrap, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_trap_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_trap_reset(handle: *mut TdTrap) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_trap_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_trap_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_trap_free(handle: *mut TdTrap) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeDrives` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_drives_free`. #[no_mangle] pub extern "C" fn wickra_three_drives_new() -> *mut ThreeDrives { Box::into_raw(Box::new(ThreeDrives::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_three_drives_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_drives_update( handle: *mut ThreeDrives, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_three_drives_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_drives_batch( handle: *mut ThreeDrives, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_drives_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_drives_reset(handle: *mut ThreeDrives) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_drives_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_drives_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_drives_free(handle: *mut ThreeDrives) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeInside` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_inside_free`. #[no_mangle] pub extern "C" fn wickra_three_inside_new() -> *mut ThreeInside { Box::into_raw(Box::new(ThreeInside::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_three_inside_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_inside_update( handle: *mut ThreeInside, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_three_inside_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_inside_batch( handle: *mut ThreeInside, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_inside_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_inside_reset(handle: *mut ThreeInside) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_inside_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_inside_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_inside_free(handle: *mut ThreeInside) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeLineBreak` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_line_break_free`. #[no_mangle] pub extern "C" fn wickra_three_line_break_new(lines: usize) -> *mut ThreeLineBreak { match ThreeLineBreak::new(lines) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_three_line_break_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_break_update( handle: *mut ThreeLineBreak, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_three_line_break_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_break_batch( handle: *mut ThreeLineBreak, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_line_break_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_break_reset(handle: *mut ThreeLineBreak) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_line_break_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_line_break_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_break_free(handle: *mut ThreeLineBreak) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeLineStrike` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_line_strike_free`. #[no_mangle] pub extern "C" fn wickra_three_line_strike_new() -> *mut ThreeLineStrike { Box::into_raw(Box::new(ThreeLineStrike::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_three_line_strike_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_strike_update( handle: *mut ThreeLineStrike, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_three_line_strike_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_strike_batch( handle: *mut ThreeLineStrike, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_line_strike_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_strike_reset(handle: *mut ThreeLineStrike) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_line_strike_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_line_strike_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_strike_free(handle: *mut ThreeLineStrike) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeOutside` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_outside_free`. #[no_mangle] pub extern "C" fn wickra_three_outside_new() -> *mut ThreeOutside { Box::into_raw(Box::new(ThreeOutside::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_three_outside_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_outside_update( handle: *mut ThreeOutside, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_three_outside_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_outside_batch( handle: *mut ThreeOutside, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_outside_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_outside_reset(handle: *mut ThreeOutside) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_outside_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_outside_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_outside_free(handle: *mut ThreeOutside) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeSoldiersOrCrows` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_soldiers_or_crows_free`. #[no_mangle] pub extern "C" fn wickra_three_soldiers_or_crows_new() -> *mut ThreeSoldiersOrCrows { Box::into_raw(Box::new(ThreeSoldiersOrCrows::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_three_soldiers_or_crows_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_soldiers_or_crows_update( handle: *mut ThreeSoldiersOrCrows, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_three_soldiers_or_crows_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_soldiers_or_crows_batch( handle: *mut ThreeSoldiersOrCrows, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_soldiers_or_crows_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_soldiers_or_crows_reset(handle: *mut ThreeSoldiersOrCrows) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_soldiers_or_crows_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_soldiers_or_crows_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_soldiers_or_crows_free(handle: *mut ThreeSoldiersOrCrows) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeStarsInSouth` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_stars_in_south_free`. #[no_mangle] pub extern "C" fn wickra_three_stars_in_south_new() -> *mut ThreeStarsInSouth { Box::into_raw(Box::new(ThreeStarsInSouth::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_three_stars_in_south_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_stars_in_south_update( handle: *mut ThreeStarsInSouth, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_three_stars_in_south_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_stars_in_south_batch( handle: *mut ThreeStarsInSouth, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_stars_in_south_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_stars_in_south_reset(handle: *mut ThreeStarsInSouth) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_stars_in_south_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_stars_in_south_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_stars_in_south_free(handle: *mut ThreeStarsInSouth) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Thrusting` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_thrusting_free`. #[no_mangle] pub extern "C" fn wickra_thrusting_new() -> *mut Thrusting { Box::into_raw(Box::new(Thrusting::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_thrusting_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_thrusting_update( handle: *mut Thrusting, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_thrusting_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_thrusting_batch( handle: *mut Thrusting, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_thrusting_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_thrusting_reset(handle: *mut Thrusting) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_thrusting_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_thrusting_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_thrusting_free(handle: *mut Thrusting) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TimeBasedStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_time_based_stop_free`. #[no_mangle] pub extern "C" fn wickra_time_based_stop_new(max_bars: usize) -> *mut TimeBasedStop { match TimeBasedStop::new(max_bars) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_time_based_stop_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_time_based_stop_update( handle: *mut TimeBasedStop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_time_based_stop_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_time_based_stop_batch( handle: *mut TimeBasedStop, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_time_based_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_time_based_stop_reset(handle: *mut TimeBasedStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_time_based_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_time_based_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_time_based_stop_free(handle: *mut TimeBasedStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TowerTopBottom` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tower_top_bottom_free`. #[no_mangle] pub extern "C" fn wickra_tower_top_bottom_new() -> *mut TowerTopBottom { Box::into_raw(Box::new(TowerTopBottom::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_tower_top_bottom_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tower_top_bottom_update( handle: *mut TowerTopBottom, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_tower_top_bottom_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_tower_top_bottom_batch( handle: *mut TowerTopBottom, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tower_top_bottom_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tower_top_bottom_reset(handle: *mut TowerTopBottom) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tower_top_bottom_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tower_top_bottom_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tower_top_bottom_free(handle: *mut TowerTopBottom) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TradeVolumeIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trade_volume_index_free`. #[no_mangle] pub extern "C" fn wickra_trade_volume_index_new(min_tick: f64) -> *mut TradeVolumeIndex { match TradeVolumeIndex::new(min_tick) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_trade_volume_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_volume_index_update( handle: *mut TradeVolumeIndex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_trade_volume_index_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_trade_volume_index_batch( handle: *mut TradeVolumeIndex, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trade_volume_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_volume_index_reset(handle: *mut TradeVolumeIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trade_volume_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trade_volume_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_volume_index_free(handle: *mut TradeVolumeIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Triangle` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_triangle_free`. #[no_mangle] pub extern "C" fn wickra_triangle_new() -> *mut Triangle { Box::into_raw(Box::new(Triangle::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_triangle_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_triangle_update( handle: *mut Triangle, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_triangle_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_triangle_batch( handle: *mut Triangle, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_triangle_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_triangle_reset(handle: *mut Triangle) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_triangle_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_triangle_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_triangle_free(handle: *mut Triangle) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TripleTopBottom` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_triple_top_bottom_free`. #[no_mangle] pub extern "C" fn wickra_triple_top_bottom_new() -> *mut TripleTopBottom { Box::into_raw(Box::new(TripleTopBottom::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_triple_top_bottom_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_triple_top_bottom_update( handle: *mut TripleTopBottom, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_triple_top_bottom_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_triple_top_bottom_batch( handle: *mut TripleTopBottom, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_triple_top_bottom_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_triple_top_bottom_reset(handle: *mut TripleTopBottom) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_triple_top_bottom_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_triple_top_bottom_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_triple_top_bottom_free(handle: *mut TripleTopBottom) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Tristar` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tristar_free`. #[no_mangle] pub extern "C" fn wickra_tristar_new() -> *mut Tristar { Box::into_raw(Box::new(Tristar::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_tristar_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tristar_update( handle: *mut Tristar, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_tristar_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_tristar_batch( handle: *mut Tristar, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tristar_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tristar_reset(handle: *mut Tristar) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tristar_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tristar_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tristar_free(handle: *mut Tristar) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TrueRange` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_true_range_free`. #[no_mangle] pub extern "C" fn wickra_true_range_new() -> *mut TrueRange { Box::into_raw(Box::new(TrueRange::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_true_range_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_true_range_update( handle: *mut TrueRange, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_true_range_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_true_range_batch( handle: *mut TrueRange, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_true_range_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_true_range_reset(handle: *mut TrueRange) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_true_range_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_true_range_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_true_range_free(handle: *mut TrueRange) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Tsv` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tsv_free`. #[no_mangle] pub extern "C" fn wickra_tsv_new(period: usize) -> *mut Tsv { match Tsv::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_tsv_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsv_update( handle: *mut Tsv, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_tsv_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_tsv_batch( handle: *mut Tsv, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tsv_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsv_reset(handle: *mut Tsv) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tsv_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tsv_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tsv_free(handle: *mut Tsv) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TtmTrend` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ttm_trend_free`. #[no_mangle] pub extern "C" fn wickra_ttm_trend_new(period: usize) -> *mut TtmTrend { match TtmTrend::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_ttm_trend_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ttm_trend_update( handle: *mut TtmTrend, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_ttm_trend_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_ttm_trend_batch( handle: *mut TtmTrend, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ttm_trend_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ttm_trend_reset(handle: *mut TtmTrend) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ttm_trend_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ttm_trend_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ttm_trend_free(handle: *mut TtmTrend) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TurnOfMonth` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_turn_of_month_free`. #[no_mangle] pub extern "C" fn wickra_turn_of_month_new( n_first: u32, n_last: u32, utc_offset_minutes: i32, ) -> *mut TurnOfMonth { match TurnOfMonth::new(n_first, n_last, utc_offset_minutes) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_turn_of_month_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_turn_of_month_update( handle: *mut TurnOfMonth, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_turn_of_month_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_turn_of_month_batch( handle: *mut TurnOfMonth, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_turn_of_month_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_turn_of_month_reset(handle: *mut TurnOfMonth) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_turn_of_month_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_turn_of_month_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_turn_of_month_free(handle: *mut TurnOfMonth) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Tweezer` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tweezer_free`. #[no_mangle] pub extern "C" fn wickra_tweezer_new() -> *mut Tweezer { Box::into_raw(Box::new(Tweezer::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_tweezer_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tweezer_update( handle: *mut Tweezer, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_tweezer_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_tweezer_batch( handle: *mut Tweezer, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tweezer_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tweezer_reset(handle: *mut Tweezer) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tweezer_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tweezer_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tweezer_free(handle: *mut Tweezer) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TwiggsMoneyFlow` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_twiggs_money_flow_free`. #[no_mangle] pub extern "C" fn wickra_twiggs_money_flow_new(period: usize) -> *mut TwiggsMoneyFlow { match TwiggsMoneyFlow::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_twiggs_money_flow_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_twiggs_money_flow_update( handle: *mut TwiggsMoneyFlow, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_twiggs_money_flow_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_twiggs_money_flow_batch( handle: *mut TwiggsMoneyFlow, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_twiggs_money_flow_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_twiggs_money_flow_reset(handle: *mut TwiggsMoneyFlow) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_twiggs_money_flow_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_twiggs_money_flow_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_twiggs_money_flow_free(handle: *mut TwiggsMoneyFlow) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TwoCrows` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_two_crows_free`. #[no_mangle] pub extern "C" fn wickra_two_crows_new() -> *mut TwoCrows { Box::into_raw(Box::new(TwoCrows::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_two_crows_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_two_crows_update( handle: *mut TwoCrows, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_two_crows_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_two_crows_batch( handle: *mut TwoCrows, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_two_crows_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_two_crows_reset(handle: *mut TwoCrows) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_two_crows_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_two_crows_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_two_crows_free(handle: *mut TwoCrows) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TypicalPrice` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_typical_price_free`. #[no_mangle] pub extern "C" fn wickra_typical_price_new() -> *mut TypicalPrice { Box::into_raw(Box::new(TypicalPrice::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_typical_price_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_typical_price_update( handle: *mut TypicalPrice, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_typical_price_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_typical_price_batch( handle: *mut TypicalPrice, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_typical_price_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_typical_price_reset(handle: *mut TypicalPrice) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_typical_price_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_typical_price_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_typical_price_free(handle: *mut TypicalPrice) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UltimateOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ultimate_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_ultimate_oscillator_new( short: usize, mid: usize, long: usize, ) -> *mut UltimateOscillator { match UltimateOscillator::new(short, mid, long) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_ultimate_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ultimate_oscillator_update( handle: *mut UltimateOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_ultimate_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_ultimate_oscillator_batch( handle: *mut UltimateOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ultimate_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ultimate_oscillator_reset(handle: *mut UltimateOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ultimate_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ultimate_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ultimate_oscillator_free(handle: *mut UltimateOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UniqueThreeRiver` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_unique_three_river_free`. #[no_mangle] pub extern "C" fn wickra_unique_three_river_new() -> *mut UniqueThreeRiver { Box::into_raw(Box::new(UniqueThreeRiver::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_unique_three_river_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_unique_three_river_update( handle: *mut UniqueThreeRiver, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_unique_three_river_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_unique_three_river_batch( handle: *mut UniqueThreeRiver, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_unique_three_river_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_unique_three_river_reset(handle: *mut UniqueThreeRiver) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_unique_three_river_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_unique_three_river_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_unique_three_river_free(handle: *mut UniqueThreeRiver) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UpsideGapThreeMethods` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_upside_gap_three_methods_free`. #[no_mangle] pub extern "C" fn wickra_upside_gap_three_methods_new() -> *mut UpsideGapThreeMethods { Box::into_raw(Box::new(UpsideGapThreeMethods::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_upside_gap_three_methods_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_three_methods_update( handle: *mut UpsideGapThreeMethods, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_upside_gap_three_methods_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_three_methods_batch( handle: *mut UpsideGapThreeMethods, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_upside_gap_three_methods_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_three_methods_reset(handle: *mut UpsideGapThreeMethods) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_upside_gap_three_methods_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_upside_gap_three_methods_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_three_methods_free(handle: *mut UpsideGapThreeMethods) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UpsideGapTwoCrows` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_upside_gap_two_crows_free`. #[no_mangle] pub extern "C" fn wickra_upside_gap_two_crows_new() -> *mut UpsideGapTwoCrows { Box::into_raw(Box::new(UpsideGapTwoCrows::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_upside_gap_two_crows_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_two_crows_update( handle: *mut UpsideGapTwoCrows, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_upside_gap_two_crows_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_two_crows_batch( handle: *mut UpsideGapTwoCrows, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_upside_gap_two_crows_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_two_crows_reset(handle: *mut UpsideGapTwoCrows) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_upside_gap_two_crows_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_upside_gap_two_crows_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_upside_gap_two_crows_free(handle: *mut UpsideGapTwoCrows) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolatilityRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volatility_ratio_free`. #[no_mangle] pub extern "C" fn wickra_volatility_ratio_new(period: usize) -> *mut VolatilityRatio { match VolatilityRatio::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_volatility_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_ratio_update( handle: *mut VolatilityRatio, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_volatility_ratio_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_ratio_batch( handle: *mut VolatilityRatio, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volatility_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_ratio_reset(handle: *mut VolatilityRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volatility_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volatility_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_ratio_free(handle: *mut VolatilityRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VoltyStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volty_stop_free`. #[no_mangle] pub extern "C" fn wickra_volty_stop_new(atr_period: usize, multiplier: f64) -> *mut VoltyStop { match VoltyStop::new(atr_period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_volty_stop_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volty_stop_update( handle: *mut VoltyStop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_volty_stop_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_volty_stop_batch( handle: *mut VoltyStop, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volty_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volty_stop_reset(handle: *mut VoltyStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volty_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volty_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volty_stop_free(handle: *mut VoltyStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumeOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_volume_oscillator_new(fast: usize, slow: usize) -> *mut VolumeOscillator { match VolumeOscillator::new(fast, slow) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_volume_oscillator_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_oscillator_update( handle: *mut VolumeOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_volume_oscillator_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_volume_oscillator_batch( handle: *mut VolumeOscillator, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_oscillator_reset(handle: *mut VolumeOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_oscillator_free(handle: *mut VolumeOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumeRsi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_rsi_free`. #[no_mangle] pub extern "C" fn wickra_volume_rsi_new(period: usize) -> *mut VolumeRsi { match VolumeRsi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_volume_rsi_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_rsi_update( handle: *mut VolumeRsi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_volume_rsi_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_volume_rsi_batch( handle: *mut VolumeRsi, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_rsi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_rsi_reset(handle: *mut VolumeRsi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_rsi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_rsi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_rsi_free(handle: *mut VolumeRsi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumePriceTrend` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_price_trend_free`. #[no_mangle] pub extern "C" fn wickra_volume_price_trend_new() -> *mut VolumePriceTrend { Box::into_raw(Box::new(VolumePriceTrend::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_volume_price_trend_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_price_trend_update( handle: *mut VolumePriceTrend, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_volume_price_trend_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_volume_price_trend_batch( handle: *mut VolumePriceTrend, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_price_trend_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_price_trend_reset(handle: *mut VolumePriceTrend) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_price_trend_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_price_trend_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_price_trend_free(handle: *mut VolumePriceTrend) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Vwap` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vwap_free`. #[no_mangle] pub extern "C" fn wickra_vwap_new() -> *mut Vwap { Box::into_raw(Box::new(Vwap::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_vwap_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwap_update( handle: *mut Vwap, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_vwap_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_vwap_batch( handle: *mut Vwap, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vwap_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwap_reset(handle: *mut Vwap) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vwap_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vwap_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwap_free(handle: *mut Vwap) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollingVwap` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rolling_vwap_free`. #[no_mangle] pub extern "C" fn wickra_rolling_vwap_new(period: usize) -> *mut RollingVwap { match RollingVwap::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_rolling_vwap_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_vwap_update( handle: *mut RollingVwap, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_rolling_vwap_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_vwap_batch( handle: *mut RollingVwap, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rolling_vwap_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_vwap_reset(handle: *mut RollingVwap) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rolling_vwap_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rolling_vwap_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rolling_vwap_free(handle: *mut RollingVwap) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Vwma` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vwma_free`. #[no_mangle] pub extern "C" fn wickra_vwma_new(period: usize) -> *mut Vwma { match Vwma::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_vwma_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwma_update( handle: *mut Vwma, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_vwma_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_vwma_batch( handle: *mut Vwma, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vwma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwma_reset(handle: *mut Vwma) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vwma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vwma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwma_free(handle: *mut Vwma) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Vzo` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vzo_free`. #[no_mangle] pub extern "C" fn wickra_vzo_new(period: usize) -> *mut Vzo { match Vzo::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_vzo_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vzo_update( handle: *mut Vzo, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_vzo_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_vzo_batch( handle: *mut Vzo, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vzo_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vzo_reset(handle: *mut Vzo) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vzo_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vzo_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vzo_free(handle: *mut Vzo) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Wad` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_wad_free`. #[no_mangle] pub extern "C" fn wickra_wad_new() -> *mut Wad { Box::into_raw(Box::new(Wad::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_wad_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wad_update( handle: *mut Wad, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_wad_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_wad_batch( handle: *mut Wad, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_wad_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wad_reset(handle: *mut Wad) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_wad_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_wad_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wad_free(handle: *mut Wad) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Wedge` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_wedge_free`. #[no_mangle] pub extern "C" fn wickra_wedge_new() -> *mut Wedge { Box::into_raw(Box::new(Wedge::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_wedge_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wedge_update( handle: *mut Wedge, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_wedge_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_wedge_batch( handle: *mut Wedge, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_wedge_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wedge_reset(handle: *mut Wedge) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_wedge_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_wedge_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wedge_free(handle: *mut Wedge) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WeightedClose` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_weighted_close_free`. #[no_mangle] pub extern "C" fn wickra_weighted_close_new() -> *mut WeightedClose { Box::into_raw(Box::new(WeightedClose::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_weighted_close_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_weighted_close_update( handle: *mut WeightedClose, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_weighted_close_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_weighted_close_batch( handle: *mut WeightedClose, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_weighted_close_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_weighted_close_reset(handle: *mut WeightedClose) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_weighted_close_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_weighted_close_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_weighted_close_free(handle: *mut WeightedClose) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WickRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_wick_ratio_free`. #[no_mangle] pub extern "C" fn wickra_wick_ratio_new() -> *mut WickRatio { Box::into_raw(Box::new(WickRatio::new())) } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_wick_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wick_ratio_update( handle: *mut WickRatio, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_wick_ratio_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_wick_ratio_batch( handle: *mut WickRatio, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_wick_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wick_ratio_reset(handle: *mut WickRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_wick_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_wick_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wick_ratio_free(handle: *mut WickRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WilliamsR` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_williams_r_free`. #[no_mangle] pub extern "C" fn wickra_williams_r_new(period: usize) -> *mut WilliamsR { match WilliamsR::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_williams_r_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_williams_r_update( handle: *mut WilliamsR, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_williams_r_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_williams_r_batch( handle: *mut WilliamsR, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_williams_r_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_williams_r_reset(handle: *mut WilliamsR) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_williams_r_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_williams_r_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_williams_r_free(handle: *mut WilliamsR) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `YangZhangVolatility` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_yang_zhang_volatility_free`. #[no_mangle] pub extern "C" fn wickra_yang_zhang_volatility_new( period: usize, trading_periods: usize, ) -> *mut YangZhangVolatility { match YangZhangVolatility::new(period, trading_periods) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_yang_zhang_volatility_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_yang_zhang_volatility_update( handle: *mut YangZhangVolatility, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_yang_zhang_volatility_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_yang_zhang_volatility_batch( handle: *mut YangZhangVolatility, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_yang_zhang_volatility_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_yang_zhang_volatility_reset(handle: *mut YangZhangVolatility) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_yang_zhang_volatility_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_yang_zhang_volatility_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_yang_zhang_volatility_free(handle: *mut YangZhangVolatility) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `YoyoExit` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_yoyo_exit_free`. #[no_mangle] pub extern "C" fn wickra_yoyo_exit_new(atr_period: usize, multiplier: f64) -> *mut YoyoExit { match YoyoExit::new(atr_period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; returns the output, or `NaN` during warmup / on a `NULL` /// handle / if the candle is invalid (e.g. `high < low`). /// /// # Safety /// `handle` must be a valid pointer from `wickra_yoyo_exit_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_yoyo_exit_update( handle: *mut YoyoExit, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match Candle::new(open, high, low, close, volume, timestamp) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Run over the OHLCV series (`open/high/low/close/volume/timestamp[0..n]`) into /// `out[0..n]` (`NaN` at warmup or on an invalid candle). /// /// # Safety /// `handle` valid (from `wickra_yoyo_exit_new`, not freed); every input pointer and `out` /// cover `n` elements. #[no_mangle] pub unsafe extern "C" fn wickra_yoyo_exit_batch( handle: *mut YoyoExit, open: *const f64, high: *const f64, low: *const f64, close: *const f64, volume: *const f64, timestamp: *const i64, out: *mut f64, n: usize, ) { if handle.is_null() || open.is_null() || high.is_null() || low.is_null() || close.is_null() || volume.is_null() || timestamp.is_null() || out.is_null() { return; } let ind = &mut *handle; let opens = slice::from_raw_parts(open, n); let highs = slice::from_raw_parts(high, n); let lows = slice::from_raw_parts(low, n); let closes = slice::from_raw_parts(close, n); let volumes = slice::from_raw_parts(volume, n); let timestamps = slice::from_raw_parts(timestamp, n); let outputs = slice::from_raw_parts_mut(out, n); for (idx, slot) in outputs.iter_mut().enumerate() { *slot = match Candle::new( opens[idx], highs[idx], lows[idx], closes[idx], volumes[idx], timestamps[idx], ) { Ok(candle) => ind.update(candle).unwrap_or(f64::NAN), Err(_) => f64::NAN, }; } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_yoyo_exit_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_yoyo_exit_reset(handle: *mut YoyoExit) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_yoyo_exit_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_yoyo_exit_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_yoyo_exit_free(handle: *mut YoyoExit) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Trade-input indicators (microstructure) ===== /// Create a `AmihudIlliquidity` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_amihud_illiquidity_free`. #[no_mangle] pub extern "C" fn wickra_amihud_illiquidity_new(period: usize) -> *mut AmihudIlliquidity { match AmihudIlliquidity::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_amihud_illiquidity_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_amihud_illiquidity_update( handle: *mut AmihudIlliquidity, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_amihud_illiquidity_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_amihud_illiquidity_reset(handle: *mut AmihudIlliquidity) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_amihud_illiquidity_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_amihud_illiquidity_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_amihud_illiquidity_free(handle: *mut AmihudIlliquidity) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CumulativeVolumeDelta` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cumulative_volume_delta_free`. #[no_mangle] pub extern "C" fn wickra_cumulative_volume_delta_new() -> *mut CumulativeVolumeDelta { Box::into_raw(Box::new(CumulativeVolumeDelta::new())) } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_cumulative_volume_delta_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cumulative_volume_delta_update( handle: *mut CumulativeVolumeDelta, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cumulative_volume_delta_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cumulative_volume_delta_reset(handle: *mut CumulativeVolumeDelta) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cumulative_volume_delta_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cumulative_volume_delta_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cumulative_volume_delta_free(handle: *mut CumulativeVolumeDelta) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Pin` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_pin_free`. #[no_mangle] pub extern "C" fn wickra_pin_new(window: usize) -> *mut Pin { match Pin::new(window) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_pin_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pin_update( handle: *mut Pin, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_pin_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pin_reset(handle: *mut Pin) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_pin_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_pin_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_pin_free(handle: *mut Pin) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RollMeasure` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_roll_measure_free`. #[no_mangle] pub extern "C" fn wickra_roll_measure_new(period: usize) -> *mut RollMeasure { match RollMeasure::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_roll_measure_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roll_measure_update( handle: *mut RollMeasure, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_roll_measure_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roll_measure_reset(handle: *mut RollMeasure) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_roll_measure_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_roll_measure_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_roll_measure_free(handle: *mut RollMeasure) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SignedVolume` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_signed_volume_free`. #[no_mangle] pub extern "C" fn wickra_signed_volume_new() -> *mut SignedVolume { Box::into_raw(Box::new(SignedVolume::new())) } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_signed_volume_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_signed_volume_update( handle: *mut SignedVolume, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_signed_volume_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_signed_volume_reset(handle: *mut SignedVolume) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_signed_volume_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_signed_volume_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_signed_volume_free(handle: *mut SignedVolume) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TradeImbalance` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trade_imbalance_free`. #[no_mangle] pub extern "C" fn wickra_trade_imbalance_new(window: usize) -> *mut TradeImbalance { match TradeImbalance::new(window) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_trade_imbalance_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_imbalance_update( handle: *mut TradeImbalance, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trade_imbalance_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_imbalance_reset(handle: *mut TradeImbalance) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trade_imbalance_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trade_imbalance_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_imbalance_free(handle: *mut TradeImbalance) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TradeSignAutocorrelation` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trade_sign_autocorrelation_free`. #[no_mangle] pub extern "C" fn wickra_trade_sign_autocorrelation_new( period: usize, ) -> *mut TradeSignAutocorrelation { match TradeSignAutocorrelation::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_trade_sign_autocorrelation_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_sign_autocorrelation_update( handle: *mut TradeSignAutocorrelation, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trade_sign_autocorrelation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_sign_autocorrelation_reset( handle: *mut TradeSignAutocorrelation, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trade_sign_autocorrelation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trade_sign_autocorrelation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trade_sign_autocorrelation_free( handle: *mut TradeSignAutocorrelation, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Vpin` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vpin_free`. #[no_mangle] pub extern "C" fn wickra_vpin_new(bucket_volume: f64, num_buckets: usize) -> *mut Vpin { match Vpin::new(bucket_volume, num_buckets) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade; returns the output, or `NaN` during warmup / on a `NULL` handle / /// if the trade is invalid (non-positive price, negative size). /// /// # Safety /// `handle` must be a valid pointer from `wickra_vpin_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vpin_update( handle: *mut Vpin, price: f64, size: f64, is_buy: bool, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; match Trade::new(price, size, side, timestamp) { Ok(trade) => ind.update(trade).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vpin_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vpin_reset(handle: *mut Vpin) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vpin_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vpin_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vpin_free(handle: *mut Vpin) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Trade+quote-input indicators (microstructure) ===== /// Create a `EffectiveSpread` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_effective_spread_free`. #[no_mangle] pub extern "C" fn wickra_effective_spread_new() -> *mut EffectiveSpread { Box::into_raw(Box::new(EffectiveSpread::new())) } /// Feed one trade with the prevailing mid price; returns the output, or `NaN` during /// warmup / on a `NULL` handle / if the trade or mid is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_effective_spread_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_effective_spread_update( handle: *mut EffectiveSpread, price: f64, size: f64, is_buy: bool, timestamp: i64, mid: f64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; let Ok(trade) = Trade::new(price, size, side, timestamp) else { return f64::NAN; }; match TradeQuote::new(trade, mid) { Ok(quote) => ind.update(quote).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_effective_spread_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_effective_spread_reset(handle: *mut EffectiveSpread) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_effective_spread_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_effective_spread_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_effective_spread_free(handle: *mut EffectiveSpread) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KylesLambda` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kyles_lambda_free`. #[no_mangle] pub extern "C" fn wickra_kyles_lambda_new(window: usize) -> *mut KylesLambda { match KylesLambda::new(window) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade with the prevailing mid price; returns the output, or `NaN` during /// warmup / on a `NULL` handle / if the trade or mid is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_kyles_lambda_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kyles_lambda_update( handle: *mut KylesLambda, price: f64, size: f64, is_buy: bool, timestamp: i64, mid: f64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; let Ok(trade) = Trade::new(price, size, side, timestamp) else { return f64::NAN; }; match TradeQuote::new(trade, mid) { Ok(quote) => ind.update(quote).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kyles_lambda_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kyles_lambda_reset(handle: *mut KylesLambda) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kyles_lambda_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kyles_lambda_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kyles_lambda_free(handle: *mut KylesLambda) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RealizedSpread` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_realized_spread_free`. #[no_mangle] pub extern "C" fn wickra_realized_spread_new(horizon: usize) -> *mut RealizedSpread { match RealizedSpread::new(horizon) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade with the prevailing mid price; returns the output, or `NaN` during /// warmup / on a `NULL` handle / if the trade or mid is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_realized_spread_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_realized_spread_update( handle: *mut RealizedSpread, price: f64, size: f64, is_buy: bool, timestamp: i64, mid: f64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; let side = if is_buy { Side::Buy } else { Side::Sell }; let Ok(trade) = Trade::new(price, size, side, timestamp) else { return f64::NAN; }; match TradeQuote::new(trade, mid) { Ok(quote) => ind.update(quote).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_realized_spread_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_realized_spread_reset(handle: *mut RealizedSpread) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_realized_spread_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_realized_spread_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_realized_spread_free(handle: *mut RealizedSpread) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Derivatives-tick-input indicators ===== /// Create a `CalendarSpread` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_calendar_spread_free`. #[no_mangle] pub extern "C" fn wickra_calendar_spread_new() -> *mut CalendarSpread { Box::into_raw(Box::new(CalendarSpread::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_calendar_spread_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_calendar_spread_update( handle: *mut CalendarSpread, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_calendar_spread_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_calendar_spread_reset(handle: *mut CalendarSpread) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_calendar_spread_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_calendar_spread_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_calendar_spread_free(handle: *mut CalendarSpread) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `EstimatedLeverageRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_estimated_leverage_ratio_free`. #[no_mangle] pub extern "C" fn wickra_estimated_leverage_ratio_new() -> *mut EstimatedLeverageRatio { Box::into_raw(Box::new(EstimatedLeverageRatio::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_estimated_leverage_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_estimated_leverage_ratio_update( handle: *mut EstimatedLeverageRatio, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_estimated_leverage_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_estimated_leverage_ratio_reset( handle: *mut EstimatedLeverageRatio, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_estimated_leverage_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_estimated_leverage_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_estimated_leverage_ratio_free(handle: *mut EstimatedLeverageRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FundingBasis` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_funding_basis_free`. #[no_mangle] pub extern "C" fn wickra_funding_basis_new() -> *mut FundingBasis { Box::into_raw(Box::new(FundingBasis::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_funding_basis_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_basis_update( handle: *mut FundingBasis, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_funding_basis_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_basis_reset(handle: *mut FundingBasis) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_funding_basis_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_funding_basis_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_basis_free(handle: *mut FundingBasis) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FundingImpliedApr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_funding_implied_apr_free`. #[no_mangle] pub extern "C" fn wickra_funding_implied_apr_new( intervals_per_year: f64, ) -> *mut FundingImpliedApr { match FundingImpliedApr::new(intervals_per_year) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_funding_implied_apr_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_implied_apr_update( handle: *mut FundingImpliedApr, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_funding_implied_apr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_implied_apr_reset(handle: *mut FundingImpliedApr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_funding_implied_apr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_funding_implied_apr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_implied_apr_free(handle: *mut FundingImpliedApr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FundingRate` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_funding_rate_free`. #[no_mangle] pub extern "C" fn wickra_funding_rate_new() -> *mut FundingRate { Box::into_raw(Box::new(FundingRate::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_funding_rate_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_update( handle: *mut FundingRate, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_funding_rate_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_reset(handle: *mut FundingRate) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_funding_rate_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_funding_rate_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_free(handle: *mut FundingRate) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FundingRateMean` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_funding_rate_mean_free`. #[no_mangle] pub extern "C" fn wickra_funding_rate_mean_new(window: usize) -> *mut FundingRateMean { match FundingRateMean::new(window) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_funding_rate_mean_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_mean_update( handle: *mut FundingRateMean, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_funding_rate_mean_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_mean_reset(handle: *mut FundingRateMean) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_funding_rate_mean_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_funding_rate_mean_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_mean_free(handle: *mut FundingRateMean) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FundingRateZScore` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_funding_rate_z_score_free`. #[no_mangle] pub extern "C" fn wickra_funding_rate_z_score_new(window: usize) -> *mut FundingRateZScore { match FundingRateZScore::new(window) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_funding_rate_z_score_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_z_score_update( handle: *mut FundingRateZScore, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_funding_rate_z_score_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_z_score_reset(handle: *mut FundingRateZScore) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_funding_rate_z_score_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_funding_rate_z_score_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_funding_rate_z_score_free(handle: *mut FundingRateZScore) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LongShortRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_long_short_ratio_free`. #[no_mangle] pub extern "C" fn wickra_long_short_ratio_new() -> *mut LongShortRatio { Box::into_raw(Box::new(LongShortRatio::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_long_short_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_short_ratio_update( handle: *mut LongShortRatio, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_long_short_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_short_ratio_reset(handle: *mut LongShortRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_long_short_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_long_short_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_long_short_ratio_free(handle: *mut LongShortRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OpenInterestDelta` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_open_interest_delta_free`. #[no_mangle] pub extern "C" fn wickra_open_interest_delta_new() -> *mut OpenInterestDelta { Box::into_raw(Box::new(OpenInterestDelta::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_open_interest_delta_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_open_interest_delta_update( handle: *mut OpenInterestDelta, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_open_interest_delta_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_open_interest_delta_reset(handle: *mut OpenInterestDelta) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_open_interest_delta_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_open_interest_delta_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_open_interest_delta_free(handle: *mut OpenInterestDelta) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OIPriceDivergence` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_oi_price_divergence_free`. #[no_mangle] pub extern "C" fn wickra_oi_price_divergence_new(window: usize) -> *mut OIPriceDivergence { match OIPriceDivergence::new(window) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_oi_price_divergence_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_price_divergence_update( handle: *mut OIPriceDivergence, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_oi_price_divergence_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_price_divergence_reset(handle: *mut OIPriceDivergence) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_oi_price_divergence_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_oi_price_divergence_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_price_divergence_free(handle: *mut OIPriceDivergence) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OiToVolumeRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_oi_to_volume_ratio_free`. #[no_mangle] pub extern "C" fn wickra_oi_to_volume_ratio_new() -> *mut OiToVolumeRatio { Box::into_raw(Box::new(OiToVolumeRatio::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_oi_to_volume_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_to_volume_ratio_update( handle: *mut OiToVolumeRatio, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_oi_to_volume_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_to_volume_ratio_reset(handle: *mut OiToVolumeRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_oi_to_volume_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_oi_to_volume_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_to_volume_ratio_free(handle: *mut OiToVolumeRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OIWeighted` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_oi_weighted_free`. #[no_mangle] pub extern "C" fn wickra_oi_weighted_new() -> *mut OIWeighted { Box::into_raw(Box::new(OIWeighted::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_oi_weighted_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_weighted_update( handle: *mut OIWeighted, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_oi_weighted_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_weighted_reset(handle: *mut OIWeighted) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_oi_weighted_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_oi_weighted_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_oi_weighted_free(handle: *mut OIWeighted) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OpenInterestMomentum` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_open_interest_momentum_free`. #[no_mangle] pub extern "C" fn wickra_open_interest_momentum_new(period: usize) -> *mut OpenInterestMomentum { match OpenInterestMomentum::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_open_interest_momentum_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_open_interest_momentum_update( handle: *mut OpenInterestMomentum, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_open_interest_momentum_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_open_interest_momentum_reset(handle: *mut OpenInterestMomentum) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_open_interest_momentum_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_open_interest_momentum_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_open_interest_momentum_free(handle: *mut OpenInterestMomentum) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PerpetualPremiumIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_perpetual_premium_index_free`. #[no_mangle] pub extern "C" fn wickra_perpetual_premium_index_new() -> *mut PerpetualPremiumIndex { Box::into_raw(Box::new(PerpetualPremiumIndex::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_perpetual_premium_index_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_perpetual_premium_index_update( handle: *mut PerpetualPremiumIndex, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_perpetual_premium_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_perpetual_premium_index_reset(handle: *mut PerpetualPremiumIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_perpetual_premium_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_perpetual_premium_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_perpetual_premium_index_free(handle: *mut PerpetualPremiumIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TakerBuySellRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_taker_buy_sell_ratio_free`. #[no_mangle] pub extern "C" fn wickra_taker_buy_sell_ratio_new() -> *mut TakerBuySellRatio { Box::into_raw(Box::new(TakerBuySellRatio::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_taker_buy_sell_ratio_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_taker_buy_sell_ratio_update( handle: *mut TakerBuySellRatio, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_taker_buy_sell_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_taker_buy_sell_ratio_reset(handle: *mut TakerBuySellRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_taker_buy_sell_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_taker_buy_sell_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_taker_buy_sell_ratio_free(handle: *mut TakerBuySellRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TermStructureBasis` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_term_structure_basis_free`. #[no_mangle] pub extern "C" fn wickra_term_structure_basis_new() -> *mut TermStructureBasis { Box::into_raw(Box::new(TermStructureBasis::new())) } /// Feed one derivatives tick; returns the output, or `NaN` during warmup / on a /// `NULL` handle / if the tick is invalid. /// /// # Safety /// `handle` must be a valid pointer from `wickra_term_structure_basis_new` (not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_term_structure_basis_update( handle: *mut TermStructureBasis, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; match DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) { Ok(tick) => ind.update(tick).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_term_structure_basis_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_term_structure_basis_reset(handle: *mut TermStructureBasis) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_term_structure_basis_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_term_structure_basis_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_term_structure_basis_free(handle: *mut TermStructureBasis) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Order-book-input indicators (microstructure) ===== /// Create a `DepthSlope` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_depth_slope_free`. #[no_mangle] pub extern "C" fn wickra_depth_slope_new() -> *mut DepthSlope { Box::into_raw(Box::new(DepthSlope::new())) } /// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the /// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. /// /// # Safety /// `handle` valid (from `wickra_depth_slope_new`, not freed) or `NULL`. `bid_price`/`bid_size` /// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. #[no_mangle] pub unsafe extern "C" fn wickra_depth_slope_update( handle: *mut DepthSlope, bid_price: *const f64, bid_size: *const f64, n_bids: usize, ask_price: *const f64, ask_size: *const f64, n_asks: usize, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { return f64::NAN; } let bid_px = slice::from_raw_parts(bid_price, n_bids); let bid_sz = slice::from_raw_parts(bid_size, n_bids); let ask_px = slice::from_raw_parts(ask_price, n_asks); let ask_sz = slice::from_raw_parts(ask_size, n_asks); let mut bids = Vec::with_capacity(n_bids); for (&px, &sz) in bid_px.iter().zip(bid_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; bids.push(level); } let mut asks = Vec::with_capacity(n_asks); for (&px, &sz) in ask_px.iter().zip(ask_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; asks.push(level); } match OrderBook::new(bids, asks) { Ok(book) => ind.update(book).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_depth_slope_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_depth_slope_reset(handle: *mut DepthSlope) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_depth_slope_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_depth_slope_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_depth_slope_free(handle: *mut DepthSlope) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Microprice` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_microprice_free`. #[no_mangle] pub extern "C" fn wickra_microprice_new() -> *mut Microprice { Box::into_raw(Box::new(Microprice::new())) } /// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the /// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. /// /// # Safety /// `handle` valid (from `wickra_microprice_new`, not freed) or `NULL`. `bid_price`/`bid_size` /// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. #[no_mangle] pub unsafe extern "C" fn wickra_microprice_update( handle: *mut Microprice, bid_price: *const f64, bid_size: *const f64, n_bids: usize, ask_price: *const f64, ask_size: *const f64, n_asks: usize, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { return f64::NAN; } let bid_px = slice::from_raw_parts(bid_price, n_bids); let bid_sz = slice::from_raw_parts(bid_size, n_bids); let ask_px = slice::from_raw_parts(ask_price, n_asks); let ask_sz = slice::from_raw_parts(ask_size, n_asks); let mut bids = Vec::with_capacity(n_bids); for (&px, &sz) in bid_px.iter().zip(bid_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; bids.push(level); } let mut asks = Vec::with_capacity(n_asks); for (&px, &sz) in ask_px.iter().zip(ask_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; asks.push(level); } match OrderBook::new(bids, asks) { Ok(book) => ind.update(book).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_microprice_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_microprice_reset(handle: *mut Microprice) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_microprice_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_microprice_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_microprice_free(handle: *mut Microprice) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OrderBookImbalanceFull` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_order_book_imbalance_full_free`. #[no_mangle] pub extern "C" fn wickra_order_book_imbalance_full_new() -> *mut OrderBookImbalanceFull { Box::into_raw(Box::new(OrderBookImbalanceFull::new())) } /// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the /// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. /// /// # Safety /// `handle` valid (from `wickra_order_book_imbalance_full_new`, not freed) or `NULL`. `bid_price`/`bid_size` /// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_full_update( handle: *mut OrderBookImbalanceFull, bid_price: *const f64, bid_size: *const f64, n_bids: usize, ask_price: *const f64, ask_size: *const f64, n_asks: usize, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { return f64::NAN; } let bid_px = slice::from_raw_parts(bid_price, n_bids); let bid_sz = slice::from_raw_parts(bid_size, n_bids); let ask_px = slice::from_raw_parts(ask_price, n_asks); let ask_sz = slice::from_raw_parts(ask_size, n_asks); let mut bids = Vec::with_capacity(n_bids); for (&px, &sz) in bid_px.iter().zip(bid_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; bids.push(level); } let mut asks = Vec::with_capacity(n_asks); for (&px, &sz) in ask_px.iter().zip(ask_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; asks.push(level); } match OrderBook::new(bids, asks) { Ok(book) => ind.update(book).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_order_book_imbalance_full_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_full_reset( handle: *mut OrderBookImbalanceFull, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_order_book_imbalance_full_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_order_book_imbalance_full_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_full_free( handle: *mut OrderBookImbalanceFull, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OrderBookImbalanceTop1` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_order_book_imbalance_top1_free`. #[no_mangle] pub extern "C" fn wickra_order_book_imbalance_top1_new() -> *mut OrderBookImbalanceTop1 { Box::into_raw(Box::new(OrderBookImbalanceTop1::new())) } /// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the /// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. /// /// # Safety /// `handle` valid (from `wickra_order_book_imbalance_top1_new`, not freed) or `NULL`. `bid_price`/`bid_size` /// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_top1_update( handle: *mut OrderBookImbalanceTop1, bid_price: *const f64, bid_size: *const f64, n_bids: usize, ask_price: *const f64, ask_size: *const f64, n_asks: usize, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { return f64::NAN; } let bid_px = slice::from_raw_parts(bid_price, n_bids); let bid_sz = slice::from_raw_parts(bid_size, n_bids); let ask_px = slice::from_raw_parts(ask_price, n_asks); let ask_sz = slice::from_raw_parts(ask_size, n_asks); let mut bids = Vec::with_capacity(n_bids); for (&px, &sz) in bid_px.iter().zip(bid_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; bids.push(level); } let mut asks = Vec::with_capacity(n_asks); for (&px, &sz) in ask_px.iter().zip(ask_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; asks.push(level); } match OrderBook::new(bids, asks) { Ok(book) => ind.update(book).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_order_book_imbalance_top1_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_top1_reset( handle: *mut OrderBookImbalanceTop1, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_order_book_imbalance_top1_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_order_book_imbalance_top1_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_top1_free( handle: *mut OrderBookImbalanceTop1, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OrderBookImbalanceTopN` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_order_book_imbalance_top_n_free`. #[no_mangle] pub extern "C" fn wickra_order_book_imbalance_top_n_new( levels: usize, ) -> *mut OrderBookImbalanceTopN { match OrderBookImbalanceTopN::new(levels) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the /// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. /// /// # Safety /// `handle` valid (from `wickra_order_book_imbalance_top_n_new`, not freed) or `NULL`. `bid_price`/`bid_size` /// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_top_n_update( handle: *mut OrderBookImbalanceTopN, bid_price: *const f64, bid_size: *const f64, n_bids: usize, ask_price: *const f64, ask_size: *const f64, n_asks: usize, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { return f64::NAN; } let bid_px = slice::from_raw_parts(bid_price, n_bids); let bid_sz = slice::from_raw_parts(bid_size, n_bids); let ask_px = slice::from_raw_parts(ask_price, n_asks); let ask_sz = slice::from_raw_parts(ask_size, n_asks); let mut bids = Vec::with_capacity(n_bids); for (&px, &sz) in bid_px.iter().zip(bid_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; bids.push(level); } let mut asks = Vec::with_capacity(n_asks); for (&px, &sz) in ask_px.iter().zip(ask_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; asks.push(level); } match OrderBook::new(bids, asks) { Ok(book) => ind.update(book).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_order_book_imbalance_top_n_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_top_n_reset( handle: *mut OrderBookImbalanceTopN, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_order_book_imbalance_top_n_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_order_book_imbalance_top_n_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_book_imbalance_top_n_free( handle: *mut OrderBookImbalanceTopN, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OrderFlowImbalance` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_order_flow_imbalance_free`. #[no_mangle] pub extern "C" fn wickra_order_flow_imbalance_new(period: usize) -> *mut OrderFlowImbalance { match OrderFlowImbalance::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the /// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. /// /// # Safety /// `handle` valid (from `wickra_order_flow_imbalance_new`, not freed) or `NULL`. `bid_price`/`bid_size` /// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. #[no_mangle] pub unsafe extern "C" fn wickra_order_flow_imbalance_update( handle: *mut OrderFlowImbalance, bid_price: *const f64, bid_size: *const f64, n_bids: usize, ask_price: *const f64, ask_size: *const f64, n_asks: usize, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { return f64::NAN; } let bid_px = slice::from_raw_parts(bid_price, n_bids); let bid_sz = slice::from_raw_parts(bid_size, n_bids); let ask_px = slice::from_raw_parts(ask_price, n_asks); let ask_sz = slice::from_raw_parts(ask_size, n_asks); let mut bids = Vec::with_capacity(n_bids); for (&px, &sz) in bid_px.iter().zip(bid_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; bids.push(level); } let mut asks = Vec::with_capacity(n_asks); for (&px, &sz) in ask_px.iter().zip(ask_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; asks.push(level); } match OrderBook::new(bids, asks) { Ok(book) => ind.update(book).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_order_flow_imbalance_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_flow_imbalance_reset(handle: *mut OrderFlowImbalance) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_order_flow_imbalance_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_order_flow_imbalance_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_order_flow_imbalance_free(handle: *mut OrderFlowImbalance) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `QuotedSpread` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_quoted_spread_free`. #[no_mangle] pub extern "C" fn wickra_quoted_spread_new() -> *mut QuotedSpread { Box::into_raw(Box::new(QuotedSpread::new())) } /// Feed one order-book snapshot as parallel bid/ask price+size arrays; returns the /// output, or `NaN` during warmup / on a `NULL` handle / if a level or the book is invalid. /// /// # Safety /// `handle` valid (from `wickra_quoted_spread_new`, not freed) or `NULL`. `bid_price`/`bid_size` /// each cover `n_bids`; `ask_price`/`ask_size` each cover `n_asks`. #[no_mangle] pub unsafe extern "C" fn wickra_quoted_spread_update( handle: *mut QuotedSpread, bid_price: *const f64, bid_size: *const f64, n_bids: usize, ask_price: *const f64, ask_size: *const f64, n_asks: usize, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if bid_price.is_null() || bid_size.is_null() || ask_price.is_null() || ask_size.is_null() { return f64::NAN; } let bid_px = slice::from_raw_parts(bid_price, n_bids); let bid_sz = slice::from_raw_parts(bid_size, n_bids); let ask_px = slice::from_raw_parts(ask_price, n_asks); let ask_sz = slice::from_raw_parts(ask_size, n_asks); let mut bids = Vec::with_capacity(n_bids); for (&px, &sz) in bid_px.iter().zip(bid_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; bids.push(level); } let mut asks = Vec::with_capacity(n_asks); for (&px, &sz) in ask_px.iter().zip(ask_sz) { let Ok(level) = Level::new(px, sz) else { return f64::NAN; }; asks.push(level); } match OrderBook::new(bids, asks) { Ok(book) => ind.update(book).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_quoted_spread_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_quoted_spread_reset(handle: *mut QuotedSpread) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_quoted_spread_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_quoted_spread_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_quoted_spread_free(handle: *mut QuotedSpread) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Cross-section-input indicators (market breadth) ===== /// Create a `AbsoluteBreadthIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_absolute_breadth_index_free`. #[no_mangle] pub extern "C" fn wickra_absolute_breadth_index_new() -> *mut AbsoluteBreadthIndex { Box::into_raw(Box::new(AbsoluteBreadthIndex::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_absolute_breadth_index_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_absolute_breadth_index_update( handle: *mut AbsoluteBreadthIndex, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_absolute_breadth_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_absolute_breadth_index_reset(handle: *mut AbsoluteBreadthIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_absolute_breadth_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_absolute_breadth_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_absolute_breadth_index_free(handle: *mut AbsoluteBreadthIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdVolumeLine` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ad_volume_line_free`. #[no_mangle] pub extern "C" fn wickra_ad_volume_line_new() -> *mut AdVolumeLine { Box::into_raw(Box::new(AdVolumeLine::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_ad_volume_line_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_ad_volume_line_update( handle: *mut AdVolumeLine, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ad_volume_line_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ad_volume_line_reset(handle: *mut AdVolumeLine) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ad_volume_line_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ad_volume_line_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ad_volume_line_free(handle: *mut AdVolumeLine) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdvanceDecline` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_advance_decline_free`. #[no_mangle] pub extern "C" fn wickra_advance_decline_new() -> *mut AdvanceDecline { Box::into_raw(Box::new(AdvanceDecline::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_advance_decline_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_advance_decline_update( handle: *mut AdvanceDecline, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_advance_decline_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_advance_decline_reset(handle: *mut AdvanceDecline) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_advance_decline_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_advance_decline_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_advance_decline_free(handle: *mut AdvanceDecline) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AdvanceDeclineRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_advance_decline_ratio_free`. #[no_mangle] pub extern "C" fn wickra_advance_decline_ratio_new() -> *mut AdvanceDeclineRatio { Box::into_raw(Box::new(AdvanceDeclineRatio::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_advance_decline_ratio_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_advance_decline_ratio_update( handle: *mut AdvanceDeclineRatio, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_advance_decline_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_advance_decline_ratio_reset(handle: *mut AdvanceDeclineRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_advance_decline_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_advance_decline_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_advance_decline_ratio_free(handle: *mut AdvanceDeclineRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BreadthThrust` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_breadth_thrust_free`. #[no_mangle] pub extern "C" fn wickra_breadth_thrust_new(period: usize) -> *mut BreadthThrust { match BreadthThrust::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_breadth_thrust_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_breadth_thrust_update( handle: *mut BreadthThrust, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_breadth_thrust_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_breadth_thrust_reset(handle: *mut BreadthThrust) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_breadth_thrust_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_breadth_thrust_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_breadth_thrust_free(handle: *mut BreadthThrust) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BullishPercentIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_bullish_percent_index_free`. #[no_mangle] pub extern "C" fn wickra_bullish_percent_index_new() -> *mut BullishPercentIndex { Box::into_raw(Box::new(BullishPercentIndex::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_bullish_percent_index_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_bullish_percent_index_update( handle: *mut BullishPercentIndex, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_bullish_percent_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bullish_percent_index_reset(handle: *mut BullishPercentIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_bullish_percent_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_bullish_percent_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bullish_percent_index_free(handle: *mut BullishPercentIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CumulativeVolumeIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cumulative_volume_index_free`. #[no_mangle] pub extern "C" fn wickra_cumulative_volume_index_new() -> *mut CumulativeVolumeIndex { Box::into_raw(Box::new(CumulativeVolumeIndex::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_cumulative_volume_index_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_cumulative_volume_index_update( handle: *mut CumulativeVolumeIndex, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cumulative_volume_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cumulative_volume_index_reset(handle: *mut CumulativeVolumeIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cumulative_volume_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cumulative_volume_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cumulative_volume_index_free(handle: *mut CumulativeVolumeIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HighLowIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_high_low_index_free`. #[no_mangle] pub extern "C" fn wickra_high_low_index_new(period: usize) -> *mut HighLowIndex { match HighLowIndex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_high_low_index_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_high_low_index_update( handle: *mut HighLowIndex, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_high_low_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_index_reset(handle: *mut HighLowIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_high_low_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_high_low_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_index_free(handle: *mut HighLowIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `McClellanOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mc_clellan_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_mc_clellan_oscillator_new() -> *mut McClellanOscillator { Box::into_raw(Box::new(McClellanOscillator::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_mc_clellan_oscillator_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_mc_clellan_oscillator_update( handle: *mut McClellanOscillator, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mc_clellan_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mc_clellan_oscillator_reset(handle: *mut McClellanOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mc_clellan_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mc_clellan_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mc_clellan_oscillator_free(handle: *mut McClellanOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `McClellanSummationIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mc_clellan_summation_index_free`. #[no_mangle] pub extern "C" fn wickra_mc_clellan_summation_index_new() -> *mut McClellanSummationIndex { Box::into_raw(Box::new(McClellanSummationIndex::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_mc_clellan_summation_index_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_mc_clellan_summation_index_update( handle: *mut McClellanSummationIndex, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mc_clellan_summation_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mc_clellan_summation_index_reset( handle: *mut McClellanSummationIndex, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mc_clellan_summation_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mc_clellan_summation_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mc_clellan_summation_index_free( handle: *mut McClellanSummationIndex, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `NewHighsNewLows` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_new_highs_new_lows_free`. #[no_mangle] pub extern "C" fn wickra_new_highs_new_lows_new() -> *mut NewHighsNewLows { Box::into_raw(Box::new(NewHighsNewLows::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_new_highs_new_lows_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_new_highs_new_lows_update( handle: *mut NewHighsNewLows, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_new_highs_new_lows_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_new_highs_new_lows_reset(handle: *mut NewHighsNewLows) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_new_highs_new_lows_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_new_highs_new_lows_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_new_highs_new_lows_free(handle: *mut NewHighsNewLows) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PercentAboveMa` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_percent_above_ma_free`. #[no_mangle] pub extern "C" fn wickra_percent_above_ma_new() -> *mut PercentAboveMa { Box::into_raw(Box::new(PercentAboveMa::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_percent_above_ma_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_percent_above_ma_update( handle: *mut PercentAboveMa, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_percent_above_ma_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percent_above_ma_reset(handle: *mut PercentAboveMa) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_percent_above_ma_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_percent_above_ma_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_percent_above_ma_free(handle: *mut PercentAboveMa) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TickIndex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tick_index_free`. #[no_mangle] pub extern "C" fn wickra_tick_index_new() -> *mut TickIndex { Box::into_raw(Box::new(TickIndex::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_tick_index_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_tick_index_update( handle: *mut TickIndex, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tick_index_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tick_index_reset(handle: *mut TickIndex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tick_index_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tick_index_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tick_index_free(handle: *mut TickIndex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Trin` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_trin_free`. #[no_mangle] pub extern "C" fn wickra_trin_new() -> *mut Trin { Box::into_raw(Box::new(Trin::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_trin_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_trin_update( handle: *mut Trin, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_trin_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trin_reset(handle: *mut Trin) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_trin_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_trin_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_trin_free(handle: *mut Trin) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `UpDownVolumeRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_up_down_volume_ratio_free`. #[no_mangle] pub extern "C" fn wickra_up_down_volume_ratio_new() -> *mut UpDownVolumeRatio { Box::into_raw(Box::new(UpDownVolumeRatio::new())) } /// Feed one cross-sectional snapshot as parallel per-member arrays; returns the output, /// or `NaN` during warmup / on a `NULL` handle / if the snapshot is invalid. /// /// # Safety /// `handle` valid (from `wickra_up_down_volume_ratio_new`, not freed) or `NULL`. Every member array /// covers `n` elements. #[no_mangle] #[allow(clippy::needless_range_loop)] pub unsafe extern "C" fn wickra_up_down_volume_ratio_update( handle: *mut UpDownVolumeRatio, change: *const f64, volume: *const f64, new_high: *const bool, new_low: *const bool, above_ma: *const bool, on_buy_signal: *const bool, n: usize, timestamp: i64, ) -> f64 { let Some(ind) = handle.as_mut() else { return f64::NAN; }; if change.is_null() || volume.is_null() || new_high.is_null() || new_low.is_null() || above_ma.is_null() || on_buy_signal.is_null() { return f64::NAN; } let changes = slice::from_raw_parts(change, n); let volumes = slice::from_raw_parts(volume, n); let new_highs = slice::from_raw_parts(new_high, n); let new_lows = slice::from_raw_parts(new_low, n); let above_mas = slice::from_raw_parts(above_ma, n); let on_buy_signals = slice::from_raw_parts(on_buy_signal, n); let mut members = Vec::with_capacity(n); for idx in 0..n { let mut member = Member::new(changes[idx], volumes[idx], new_highs[idx], new_lows[idx]); member.above_ma = above_mas[idx]; member.on_buy_signal = on_buy_signals[idx]; members.push(member); } match CrossSection::new(members, timestamp) { Ok(snapshot) => ind.update(snapshot).unwrap_or(f64::NAN), Err(_) => f64::NAN, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_up_down_volume_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_up_down_volume_ratio_reset(handle: *mut UpDownVolumeRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_up_down_volume_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_up_down_volume_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_up_down_volume_ratio_free(handle: *mut UpDownVolumeRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Multi-output indicators (fixed-size struct via out-param) ===== /// C-ABI view of `AccelerationBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAccelerationBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `AdxOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAdxOutput { pub plus_di: f64, pub minus_di: f64, pub adx: f64, } /// C-ABI view of `AlligatorOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAlligatorOutput { pub jaw: f64, pub teeth: f64, pub lips: f64, } /// C-ABI view of `AndrewsPitchforkOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAndrewsPitchforkOutput { pub median: f64, pub upper: f64, pub lower: f64, } /// C-ABI view of `AroonOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAroonOutput { pub up: f64, pub down: f64, } /// C-ABI view of `AtrBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAtrBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `AtrRatchetOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAtrRatchetOutput { pub value: f64, pub direction: f64, } /// C-ABI view of `AutoFibOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraAutoFibOutput { pub level_0: f64, pub level_236: f64, pub level_382: f64, pub level_500: f64, pub level_618: f64, pub level_786: f64, pub level_1000: f64, } /// C-ABI view of `BollingerOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraBollingerOutput { pub upper: f64, pub middle: f64, pub lower: f64, pub stddev: f64, } /// C-ABI view of `BomarBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraBomarBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `CamarillaPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraCamarillaPivotsOutput { pub pp: f64, pub r1: f64, pub r2: f64, pub r3: f64, pub r4: f64, pub s1: f64, pub s2: f64, pub s3: f64, pub s4: f64, } /// C-ABI view of `CandleVolumeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraCandleVolumeOutput { pub body: f64, pub width: f64, } /// C-ABI view of `CentralPivotRangeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraCentralPivotRangeOutput { pub pivot: f64, pub tc: f64, pub bc: f64, } /// C-ABI view of `ChandeKrollStopOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraChandeKrollStopOutput { pub stop_long: f64, pub stop_short: f64, } /// C-ABI view of `ChandelierExitOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraChandelierExitOutput { pub long_stop: f64, pub short_stop: f64, } /// C-ABI view of `ClassicPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraClassicPivotsOutput { pub pp: f64, pub r1: f64, pub r2: f64, pub r3: f64, pub s1: f64, pub s2: f64, pub s3: f64, } /// C-ABI view of `CointegrationOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraCointegrationOutput { pub hedge_ratio: f64, pub spread: f64, pub adf_stat: f64, } /// C-ABI view of `CompositeProfileOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraCompositeProfileOutput { pub poc: f64, pub vah: f64, pub val: f64, } /// C-ABI view of `DemarkPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraDemarkPivotsOutput { pub pp: f64, pub r1: f64, pub s1: f64, } /// C-ABI view of `DonchianOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraDonchianOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `DonchianStopOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraDonchianStopOutput { pub stop_long: f64, pub stop_short: f64, } /// C-ABI view of `DoubleBollingerOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraDoubleBollingerOutput { pub upper_outer: f64, pub upper_inner: f64, pub middle: f64, pub lower_inner: f64, pub lower_outer: f64, } /// C-ABI view of `ElderRayOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraElderRayOutput { pub bull_power: f64, pub bear_power: f64, } /// C-ABI view of `ElderSafeZoneOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraElderSafeZoneOutput { pub value: f64, pub direction: f64, } /// C-ABI view of `EquivolumeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraEquivolumeOutput { pub height: f64, pub width: f64, } /// C-ABI view of `FibArcsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibArcsOutput { pub arc_382: f64, pub arc_500: f64, pub arc_618: f64, } /// C-ABI view of `FibChannelOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibChannelOutput { pub base: f64, pub level_618: f64, pub level_1000: f64, pub level_1618: f64, } /// C-ABI view of `FibConfluenceOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibConfluenceOutput { pub price: f64, pub strength: f64, } /// C-ABI view of `FibExtensionOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibExtensionOutput { pub level_1272: f64, pub level_1414: f64, pub level_1618: f64, pub level_2000: f64, pub level_2618: f64, } /// C-ABI view of `FibFanOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibFanOutput { pub fan_382: f64, pub fan_500: f64, pub fan_618: f64, } /// C-ABI view of `FibProjectionOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibProjectionOutput { pub level_618: f64, pub level_1000: f64, pub level_1618: f64, pub level_2618: f64, } /// C-ABI view of `FibRetracementOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibRetracementOutput { pub level_0: f64, pub level_236: f64, pub level_382: f64, pub level_500: f64, pub level_618: f64, pub level_786: f64, pub level_1000: f64, } /// C-ABI view of `FibTimeZonesOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibTimeZonesOutput { pub on_zone: f64, pub bars_to_next: f64, } /// C-ABI view of `FibonacciPivotsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFibonacciPivotsOutput { pub pp: f64, pub r1: f64, pub r2: f64, pub r3: f64, pub s1: f64, pub s2: f64, pub s3: f64, } /// C-ABI view of `FractalChaosBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFractalChaosBandsOutput { pub upper: f64, pub lower: f64, } /// C-ABI view of `GatorOscillatorOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraGatorOscillatorOutput { pub upper: f64, pub lower: f64, } /// C-ABI view of `GoldenPocketOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraGoldenPocketOutput { pub low: f64, pub mid: f64, pub high: f64, } /// C-ABI view of `HeikinAshiOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraHeikinAshiOutput { pub open: f64, pub high: f64, pub low: f64, pub close: f64, } /// C-ABI view of `HighLowVolumeNodesOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraHighLowVolumeNodesOutput { pub hvn: f64, pub lvn: f64, } /// C-ABI view of `HtPhasorOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraHtPhasorOutput { pub inphase: f64, pub quadrature: f64, } /// C-ABI view of `HurstChannelOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraHurstChannelOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `IchimokuOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraIchimokuOutput { pub tenkan: f64, pub kijun: f64, pub senkou_a: f64, pub senkou_b: f64, pub chikou: f64, } /// C-ABI view of `InitialBalanceOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraInitialBalanceOutput { pub high: f64, pub low: f64, } /// C-ABI view of `KalmanHedgeRatioOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraKalmanHedgeRatioOutput { pub hedge_ratio: f64, pub intercept: f64, pub spread: f64, } /// C-ABI view of `KaseDevStopOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraKaseDevStopOutput { pub value: f64, pub direction: f64, } /// C-ABI view of `KasePermissionStochasticOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraKasePermissionStochasticOutput { pub fast: f64, pub slow: f64, } /// C-ABI view of `KeltnerOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraKeltnerOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `KstOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraKstOutput { pub kst: f64, pub signal: f64, } /// C-ABI view of `LeadLagCrossCorrelationOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraLeadLagCrossCorrelationOutput { pub lag: i64, pub correlation: f64, } /// C-ABI view of `LinRegChannelOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraLinRegChannelOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `LiquidationFeaturesOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraLiquidationFeaturesOutput { pub long: f64, pub short: f64, pub net: f64, pub total: f64, pub imbalance: f64, } /// C-ABI view of `MaEnvelopeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraMaEnvelopeOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `MacdOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraMacdOutput { pub macd: f64, pub signal: f64, pub histogram: f64, } /// C-ABI view of `MamaOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraMamaOutput { pub mama: f64, pub fama: f64, } /// C-ABI view of `MedianChannelOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraMedianChannelOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `ModifiedMaStopOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraModifiedMaStopOutput { pub value: f64, pub direction: f64, } /// C-ABI view of `MurreyMathLinesOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraMurreyMathLinesOutput { pub mm8_8: f64, pub mm7_8: f64, pub mm6_8: f64, pub mm5_8: f64, pub mm4_8: f64, pub mm3_8: f64, pub mm2_8: f64, pub mm1_8: f64, pub mm0_8: f64, } /// C-ABI view of `NrtrOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraNrtrOutput { pub value: f64, pub direction: f64, } /// C-ABI view of `OpeningRangeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraOpeningRangeOutput { pub high: f64, pub low: f64, pub breakout_distance: f64, } /// C-ABI view of `OvernightIntradayReturnOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraOvernightIntradayReturnOutput { pub overnight: f64, pub intraday: f64, } /// C-ABI view of `ProjectionBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraProjectionBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `QqeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraQqeOutput { pub rsi_ma: f64, pub trailing_line: f64, } /// C-ABI view of `QuartileBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraQuartileBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `RelativeStrengthOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraRelativeStrengthOutput { pub ratio: f64, pub ratio_ma: f64, pub ratio_rsi: f64, } /// C-ABI view of `RwiOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraRwiOutput { pub high: f64, pub low: f64, } /// C-ABI view of `SessionHighLowOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraSessionHighLowOutput { pub high: f64, pub low: f64, } /// C-ABI view of `SessionRangeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraSessionRangeOutput { pub asia: f64, pub eu: f64, pub us: f64, } /// C-ABI view of `SmoothedHeikinAshiOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraSmoothedHeikinAshiOutput { pub open: f64, pub high: f64, pub low: f64, pub close: f64, } /// C-ABI view of `SpreadBollingerBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraSpreadBollingerBandsOutput { pub middle: f64, pub upper: f64, pub lower: f64, pub percent_b: f64, } /// C-ABI view of `StandardErrorBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraStandardErrorBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `StarcBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraStarcBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, } /// C-ABI view of `StochasticOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraStochasticOutput { pub k: f64, pub d: f64, } /// C-ABI view of `SuperTrendOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraSuperTrendOutput { pub value: f64, pub direction: f64, } /// C-ABI view of `TdLinesOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTdLinesOutput { pub resistance: f64, pub support: f64, } /// C-ABI view of `TdMovingAverageOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTdMovingAverageOutput { pub st1: f64, pub st2: f64, } /// C-ABI view of `TdRangeProjectionOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTdRangeProjectionOutput { pub high: f64, pub low: f64, } /// C-ABI view of `TdRiskLevelOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTdRiskLevelOutput { pub buy_risk: f64, pub sell_risk: f64, } /// C-ABI view of `TdSequentialOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTdSequentialOutput { pub setup: f64, pub countdown: f64, pub direction: f64, } /// C-ABI view of `TtmSqueezeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTtmSqueezeOutput { pub squeeze: f64, pub momentum: f64, } /// C-ABI view of `ValueAreaOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraValueAreaOutput { pub poc: f64, pub vah: f64, pub val: f64, } /// C-ABI view of `VolatilityConeOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraVolatilityConeOutput { pub current: f64, pub min: f64, pub median: f64, pub max: f64, pub percentile: f64, } /// C-ABI view of `VolumeWeightedMacdOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraVolumeWeightedMacdOutput { pub macd: f64, pub signal: f64, pub histogram: f64, } /// C-ABI view of `VolumeWeightedSrOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraVolumeWeightedSrOutput { pub support: f64, pub resistance: f64, } /// C-ABI view of `VortexOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraVortexOutput { pub plus: f64, pub minus: f64, } /// C-ABI view of `VwapStdDevBandsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraVwapStdDevBandsOutput { pub upper: f64, pub middle: f64, pub lower: f64, pub stddev: f64, } /// C-ABI view of `WaveTrendOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraWaveTrendOutput { pub wt1: f64, pub wt2: f64, } /// C-ABI view of `WilliamsFractalsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraWilliamsFractalsOutput { pub up: f64, pub down: f64, } /// C-ABI view of `WoodiePivotsOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraWoodiePivotsOutput { pub pp: f64, pub r1: f64, pub r2: f64, pub s1: f64, pub s2: f64, } /// C-ABI view of `ZeroLagMacdOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraZeroLagMacdOutput { pub macd: f64, pub signal: f64, pub histogram: f64, } /// C-ABI view of `ZigZagOutput` (fixed-size; `Option` fields surface as `NaN`). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraZigZagOutput { pub swing: f64, pub direction: f64, } /// Create a `AccelerationBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_acceleration_bands_free`. #[no_mangle] pub extern "C" fn wickra_acceleration_bands_new( period: usize, factor: f64, ) -> *mut AccelerationBands { match AccelerationBands::new(period, factor) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_acceleration_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_acceleration_bands_update( handle: *mut AccelerationBands, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAccelerationBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAccelerationBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_acceleration_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_acceleration_bands_reset(handle: *mut AccelerationBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_acceleration_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_acceleration_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_acceleration_bands_free(handle: *mut AccelerationBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Adx` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_adx_free`. #[no_mangle] pub extern "C" fn wickra_adx_new(period: usize) -> *mut Adx { match Adx::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_adx_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adx_update( handle: *mut Adx, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAdxOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAdxOutput { plus_di: out_val.plus_di, minus_di: out_val.minus_di, adx: out_val.adx, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_adx_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adx_reset(handle: *mut Adx) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_adx_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_adx_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_adx_free(handle: *mut Adx) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Alligator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_alligator_free`. #[no_mangle] pub extern "C" fn wickra_alligator_new( jaw_period: usize, teeth_period: usize, lips_period: usize, ) -> *mut Alligator { match Alligator::new(jaw_period, teeth_period, lips_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_alligator_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alligator_update( handle: *mut Alligator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAlligatorOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAlligatorOutput { jaw: out_val.jaw, teeth: out_val.teeth, lips: out_val.lips, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_alligator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alligator_reset(handle: *mut Alligator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_alligator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_alligator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_alligator_free(handle: *mut Alligator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AndrewsPitchfork` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_andrews_pitchfork_free`. #[no_mangle] pub extern "C" fn wickra_andrews_pitchfork_new(strength: usize) -> *mut AndrewsPitchfork { match AndrewsPitchfork::new(strength) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_andrews_pitchfork_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_andrews_pitchfork_update( handle: *mut AndrewsPitchfork, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAndrewsPitchforkOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAndrewsPitchforkOutput { median: out_val.median, upper: out_val.upper, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_andrews_pitchfork_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_andrews_pitchfork_reset(handle: *mut AndrewsPitchfork) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_andrews_pitchfork_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_andrews_pitchfork_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_andrews_pitchfork_free(handle: *mut AndrewsPitchfork) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Aroon` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_aroon_free`. #[no_mangle] pub extern "C" fn wickra_aroon_new(period: usize) -> *mut Aroon { match Aroon::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_aroon_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_aroon_update( handle: *mut Aroon, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAroonOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAroonOutput { up: out_val.up, down: out_val.down, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_aroon_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_aroon_reset(handle: *mut Aroon) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_aroon_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_aroon_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_aroon_free(handle: *mut Aroon) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AtrBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_atr_bands_free`. #[no_mangle] pub extern "C" fn wickra_atr_bands_new(period: usize, multiplier: f64) -> *mut AtrBands { match AtrBands::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_atr_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_bands_update( handle: *mut AtrBands, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAtrBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAtrBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_atr_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_bands_reset(handle: *mut AtrBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_atr_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_atr_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_bands_free(handle: *mut AtrBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AtrRatchet` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_atr_ratchet_free`. #[no_mangle] pub extern "C" fn wickra_atr_ratchet_new( atr_period: usize, start_mult: f64, increment: f64, ) -> *mut AtrRatchet { match AtrRatchet::new(atr_period, start_mult, increment) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_atr_ratchet_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_ratchet_update( handle: *mut AtrRatchet, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAtrRatchetOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAtrRatchetOutput { value: out_val.value, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_atr_ratchet_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_ratchet_reset(handle: *mut AtrRatchet) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_atr_ratchet_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_atr_ratchet_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_atr_ratchet_free(handle: *mut AtrRatchet) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `AutoFib` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_auto_fib_free`. #[no_mangle] pub extern "C" fn wickra_auto_fib_new() -> *mut AutoFib { Box::into_raw(Box::new(AutoFib::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_auto_fib_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_auto_fib_update( handle: *mut AutoFib, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraAutoFibOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraAutoFibOutput { level_0: out_val.level_0, level_236: out_val.level_236, level_382: out_val.level_382, level_500: out_val.level_500, level_618: out_val.level_618, level_786: out_val.level_786, level_1000: out_val.level_1000, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_auto_fib_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_auto_fib_reset(handle: *mut AutoFib) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_auto_fib_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_auto_fib_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_auto_fib_free(handle: *mut AutoFib) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BollingerBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_bollinger_bands_free`. #[no_mangle] pub extern "C" fn wickra_bollinger_bands_new( period: usize, multiplier: f64, ) -> *mut BollingerBands { match BollingerBands::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_bollinger_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bollinger_bands_update( handle: *mut BollingerBands, value: f64, out: *mut WickraBollingerOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraBollingerOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, stddev: out_val.stddev, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_bollinger_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bollinger_bands_reset(handle: *mut BollingerBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_bollinger_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_bollinger_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bollinger_bands_free(handle: *mut BollingerBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `BomarBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_bomar_bands_free`. #[no_mangle] pub extern "C" fn wickra_bomar_bands_new(period: usize, coverage: f64) -> *mut BomarBands { match BomarBands::new(period, coverage) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_bomar_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bomar_bands_update( handle: *mut BomarBands, value: f64, out: *mut WickraBomarBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraBomarBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_bomar_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bomar_bands_reset(handle: *mut BomarBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_bomar_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_bomar_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_bomar_bands_free(handle: *mut BomarBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Camarilla` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_camarilla_free`. #[no_mangle] pub extern "C" fn wickra_camarilla_new() -> *mut Camarilla { Box::into_raw(Box::new(Camarilla::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_camarilla_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_camarilla_update( handle: *mut Camarilla, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraCamarillaPivotsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraCamarillaPivotsOutput { pp: out_val.pp, r1: out_val.r1, r2: out_val.r2, r3: out_val.r3, r4: out_val.r4, s1: out_val.s1, s2: out_val.s2, s3: out_val.s3, s4: out_val.s4, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_camarilla_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_camarilla_reset(handle: *mut Camarilla) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_camarilla_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_camarilla_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_camarilla_free(handle: *mut Camarilla) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CandleVolume` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_candle_volume_free`. #[no_mangle] pub extern "C" fn wickra_candle_volume_new(period: usize) -> *mut CandleVolume { match CandleVolume::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_candle_volume_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_candle_volume_update( handle: *mut CandleVolume, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraCandleVolumeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraCandleVolumeOutput { body: out_val.body, width: out_val.width, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_candle_volume_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_candle_volume_reset(handle: *mut CandleVolume) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_candle_volume_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_candle_volume_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_candle_volume_free(handle: *mut CandleVolume) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CentralPivotRange` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_central_pivot_range_free`. #[no_mangle] pub extern "C" fn wickra_central_pivot_range_new() -> *mut CentralPivotRange { Box::into_raw(Box::new(CentralPivotRange::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_central_pivot_range_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_central_pivot_range_update( handle: *mut CentralPivotRange, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraCentralPivotRangeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraCentralPivotRangeOutput { pivot: out_val.pivot, tc: out_val.tc, bc: out_val.bc, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_central_pivot_range_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_central_pivot_range_reset(handle: *mut CentralPivotRange) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_central_pivot_range_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_central_pivot_range_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_central_pivot_range_free(handle: *mut CentralPivotRange) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ChandeKrollStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_chande_kroll_stop_free`. #[no_mangle] pub extern "C" fn wickra_chande_kroll_stop_new( atr_period: usize, atr_multiplier: f64, stop_period: usize, ) -> *mut ChandeKrollStop { match ChandeKrollStop::new(atr_period, atr_multiplier, stop_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_chande_kroll_stop_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chande_kroll_stop_update( handle: *mut ChandeKrollStop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraChandeKrollStopOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraChandeKrollStopOutput { stop_long: out_val.stop_long, stop_short: out_val.stop_short, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_chande_kroll_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chande_kroll_stop_reset(handle: *mut ChandeKrollStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_chande_kroll_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_chande_kroll_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chande_kroll_stop_free(handle: *mut ChandeKrollStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ChandelierExit` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_chandelier_exit_free`. #[no_mangle] pub extern "C" fn wickra_chandelier_exit_new( period: usize, multiplier: f64, ) -> *mut ChandelierExit { match ChandelierExit::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_chandelier_exit_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chandelier_exit_update( handle: *mut ChandelierExit, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraChandelierExitOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraChandelierExitOutput { long_stop: out_val.long_stop, short_stop: out_val.short_stop, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_chandelier_exit_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chandelier_exit_reset(handle: *mut ChandelierExit) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_chandelier_exit_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_chandelier_exit_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_chandelier_exit_free(handle: *mut ChandelierExit) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ClassicPivots` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_classic_pivots_free`. #[no_mangle] pub extern "C" fn wickra_classic_pivots_new() -> *mut ClassicPivots { Box::into_raw(Box::new(ClassicPivots::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_classic_pivots_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_classic_pivots_update( handle: *mut ClassicPivots, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraClassicPivotsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraClassicPivotsOutput { pp: out_val.pp, r1: out_val.r1, r2: out_val.r2, r3: out_val.r3, s1: out_val.s1, s2: out_val.s2, s3: out_val.s3, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_classic_pivots_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_classic_pivots_reset(handle: *mut ClassicPivots) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_classic_pivots_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_classic_pivots_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_classic_pivots_free(handle: *mut ClassicPivots) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Cointegration` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_cointegration_free`. #[no_mangle] pub extern "C" fn wickra_cointegration_new(period: usize, adf_lags: usize) -> *mut Cointegration { match Cointegration::new(period, adf_lags) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_cointegration_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cointegration_update( handle: *mut Cointegration, x: f64, y: f64, out: *mut WickraCointegrationOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update((x, y)) { Some(out_val) => { *out = WickraCointegrationOutput { hedge_ratio: out_val.hedge_ratio, spread: out_val.spread, adf_stat: out_val.adf_stat, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_cointegration_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cointegration_reset(handle: *mut Cointegration) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_cointegration_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_cointegration_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_cointegration_free(handle: *mut Cointegration) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `CompositeProfile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_composite_profile_free`. #[no_mangle] pub extern "C" fn wickra_composite_profile_new( period: usize, bins: usize, value_area_pct: f64, ) -> *mut CompositeProfile { match CompositeProfile::new(period, bins, value_area_pct) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_composite_profile_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_composite_profile_update( handle: *mut CompositeProfile, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraCompositeProfileOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraCompositeProfileOutput { poc: out_val.poc, vah: out_val.vah, val: out_val.val, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_composite_profile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_composite_profile_reset(handle: *mut CompositeProfile) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_composite_profile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_composite_profile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_composite_profile_free(handle: *mut CompositeProfile) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DemarkPivots` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_demark_pivots_free`. #[no_mangle] pub extern "C" fn wickra_demark_pivots_new() -> *mut DemarkPivots { Box::into_raw(Box::new(DemarkPivots::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_demark_pivots_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_demark_pivots_update( handle: *mut DemarkPivots, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraDemarkPivotsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraDemarkPivotsOutput { pp: out_val.pp, r1: out_val.r1, s1: out_val.s1, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_demark_pivots_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_demark_pivots_reset(handle: *mut DemarkPivots) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_demark_pivots_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_demark_pivots_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_demark_pivots_free(handle: *mut DemarkPivots) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Donchian` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_donchian_free`. #[no_mangle] pub extern "C" fn wickra_donchian_new(period: usize) -> *mut Donchian { match Donchian::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_donchian_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_donchian_update( handle: *mut Donchian, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraDonchianOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraDonchianOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_donchian_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_donchian_reset(handle: *mut Donchian) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_donchian_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_donchian_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_donchian_free(handle: *mut Donchian) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DonchianStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_donchian_stop_free`. #[no_mangle] pub extern "C" fn wickra_donchian_stop_new(period: usize) -> *mut DonchianStop { match DonchianStop::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_donchian_stop_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_donchian_stop_update( handle: *mut DonchianStop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraDonchianStopOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraDonchianStopOutput { stop_long: out_val.stop_long, stop_short: out_val.stop_short, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_donchian_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_donchian_stop_reset(handle: *mut DonchianStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_donchian_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_donchian_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_donchian_stop_free(handle: *mut DonchianStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `DoubleBollinger` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_double_bollinger_free`. #[no_mangle] pub extern "C" fn wickra_double_bollinger_new( period: usize, k_inner: f64, k_outer: f64, ) -> *mut DoubleBollinger { match DoubleBollinger::new(period, k_inner, k_outer) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_double_bollinger_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_double_bollinger_update( handle: *mut DoubleBollinger, value: f64, out: *mut WickraDoubleBollingerOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraDoubleBollingerOutput { upper_outer: out_val.upper_outer, upper_inner: out_val.upper_inner, middle: out_val.middle, lower_inner: out_val.lower_inner, lower_outer: out_val.lower_outer, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_double_bollinger_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_double_bollinger_reset(handle: *mut DoubleBollinger) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_double_bollinger_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_double_bollinger_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_double_bollinger_free(handle: *mut DoubleBollinger) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ElderRay` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_elder_ray_free`. #[no_mangle] pub extern "C" fn wickra_elder_ray_new(period: usize) -> *mut ElderRay { match ElderRay::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_elder_ray_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_ray_update( handle: *mut ElderRay, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraElderRayOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraElderRayOutput { bull_power: out_val.bull_power, bear_power: out_val.bear_power, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_elder_ray_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_ray_reset(handle: *mut ElderRay) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_elder_ray_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_elder_ray_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_ray_free(handle: *mut ElderRay) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ElderSafeZone` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_elder_safe_zone_free`. #[no_mangle] pub extern "C" fn wickra_elder_safe_zone_new(period: usize, coeff: f64) -> *mut ElderSafeZone { match ElderSafeZone::new(period, coeff) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_elder_safe_zone_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_safe_zone_update( handle: *mut ElderSafeZone, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraElderSafeZoneOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraElderSafeZoneOutput { value: out_val.value, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_elder_safe_zone_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_safe_zone_reset(handle: *mut ElderSafeZone) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_elder_safe_zone_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_elder_safe_zone_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_elder_safe_zone_free(handle: *mut ElderSafeZone) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Equivolume` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_equivolume_free`. #[no_mangle] pub extern "C" fn wickra_equivolume_new(period: usize) -> *mut Equivolume { match Equivolume::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_equivolume_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_equivolume_update( handle: *mut Equivolume, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraEquivolumeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraEquivolumeOutput { height: out_val.height, width: out_val.width, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_equivolume_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_equivolume_reset(handle: *mut Equivolume) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_equivolume_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_equivolume_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_equivolume_free(handle: *mut Equivolume) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibArcs` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_arcs_free`. #[no_mangle] pub extern "C" fn wickra_fib_arcs_new() -> *mut FibArcs { Box::into_raw(Box::new(FibArcs::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_arcs_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_arcs_update( handle: *mut FibArcs, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibArcsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibArcsOutput { arc_382: out_val.arc_382, arc_500: out_val.arc_500, arc_618: out_val.arc_618, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_arcs_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_arcs_reset(handle: *mut FibArcs) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_arcs_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_arcs_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_arcs_free(handle: *mut FibArcs) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibChannel` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_channel_free`. #[no_mangle] pub extern "C" fn wickra_fib_channel_new() -> *mut FibChannel { Box::into_raw(Box::new(FibChannel::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_channel_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_channel_update( handle: *mut FibChannel, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibChannelOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibChannelOutput { base: out_val.base, level_618: out_val.level_618, level_1000: out_val.level_1000, level_1618: out_val.level_1618, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_channel_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_channel_reset(handle: *mut FibChannel) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_channel_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_channel_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_channel_free(handle: *mut FibChannel) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibConfluence` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_confluence_free`. #[no_mangle] pub extern "C" fn wickra_fib_confluence_new() -> *mut FibConfluence { Box::into_raw(Box::new(FibConfluence::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_confluence_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_confluence_update( handle: *mut FibConfluence, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibConfluenceOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibConfluenceOutput { price: out_val.price, strength: out_val.strength, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_confluence_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_confluence_reset(handle: *mut FibConfluence) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_confluence_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_confluence_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_confluence_free(handle: *mut FibConfluence) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibExtension` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_extension_free`. #[no_mangle] pub extern "C" fn wickra_fib_extension_new() -> *mut FibExtension { Box::into_raw(Box::new(FibExtension::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_extension_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_extension_update( handle: *mut FibExtension, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibExtensionOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibExtensionOutput { level_1272: out_val.level_1272, level_1414: out_val.level_1414, level_1618: out_val.level_1618, level_2000: out_val.level_2000, level_2618: out_val.level_2618, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_extension_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_extension_reset(handle: *mut FibExtension) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_extension_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_extension_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_extension_free(handle: *mut FibExtension) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibFan` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_fan_free`. #[no_mangle] pub extern "C" fn wickra_fib_fan_new() -> *mut FibFan { Box::into_raw(Box::new(FibFan::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_fan_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_fan_update( handle: *mut FibFan, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibFanOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibFanOutput { fan_382: out_val.fan_382, fan_500: out_val.fan_500, fan_618: out_val.fan_618, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_fan_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_fan_reset(handle: *mut FibFan) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_fan_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_fan_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_fan_free(handle: *mut FibFan) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibProjection` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_projection_free`. #[no_mangle] pub extern "C" fn wickra_fib_projection_new() -> *mut FibProjection { Box::into_raw(Box::new(FibProjection::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_projection_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_projection_update( handle: *mut FibProjection, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibProjectionOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibProjectionOutput { level_618: out_val.level_618, level_1000: out_val.level_1000, level_1618: out_val.level_1618, level_2618: out_val.level_2618, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_projection_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_projection_reset(handle: *mut FibProjection) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_projection_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_projection_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_projection_free(handle: *mut FibProjection) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibRetracement` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_retracement_free`. #[no_mangle] pub extern "C" fn wickra_fib_retracement_new() -> *mut FibRetracement { Box::into_raw(Box::new(FibRetracement::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_retracement_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_retracement_update( handle: *mut FibRetracement, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibRetracementOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibRetracementOutput { level_0: out_val.level_0, level_236: out_val.level_236, level_382: out_val.level_382, level_500: out_val.level_500, level_618: out_val.level_618, level_786: out_val.level_786, level_1000: out_val.level_1000, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_retracement_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_retracement_reset(handle: *mut FibRetracement) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_retracement_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_retracement_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_retracement_free(handle: *mut FibRetracement) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibTimeZones` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fib_time_zones_free`. #[no_mangle] pub extern "C" fn wickra_fib_time_zones_new() -> *mut FibTimeZones { Box::into_raw(Box::new(FibTimeZones::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fib_time_zones_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_time_zones_update( handle: *mut FibTimeZones, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibTimeZonesOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibTimeZonesOutput { on_zone: out_val.on_zone, bars_to_next: out_val.bars_to_next, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fib_time_zones_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_time_zones_reset(handle: *mut FibTimeZones) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fib_time_zones_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fib_time_zones_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fib_time_zones_free(handle: *mut FibTimeZones) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FibonacciPivots` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fibonacci_pivots_free`. #[no_mangle] pub extern "C" fn wickra_fibonacci_pivots_new() -> *mut FibonacciPivots { Box::into_raw(Box::new(FibonacciPivots::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fibonacci_pivots_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fibonacci_pivots_update( handle: *mut FibonacciPivots, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFibonacciPivotsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFibonacciPivotsOutput { pp: out_val.pp, r1: out_val.r1, r2: out_val.r2, r3: out_val.r3, s1: out_val.s1, s2: out_val.s2, s3: out_val.s3, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fibonacci_pivots_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fibonacci_pivots_reset(handle: *mut FibonacciPivots) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fibonacci_pivots_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fibonacci_pivots_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fibonacci_pivots_free(handle: *mut FibonacciPivots) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `FractalChaosBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_fractal_chaos_bands_free`. #[no_mangle] pub extern "C" fn wickra_fractal_chaos_bands_new(k: usize) -> *mut FractalChaosBands { match FractalChaosBands::new(k) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_fractal_chaos_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fractal_chaos_bands_update( handle: *mut FractalChaosBands, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraFractalChaosBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraFractalChaosBandsOutput { upper: out_val.upper, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_fractal_chaos_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fractal_chaos_bands_reset(handle: *mut FractalChaosBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_fractal_chaos_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_fractal_chaos_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_fractal_chaos_bands_free(handle: *mut FractalChaosBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GatorOscillator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_gator_oscillator_free`. #[no_mangle] pub extern "C" fn wickra_gator_oscillator_new( jaw_period: usize, teeth_period: usize, lips_period: usize, ) -> *mut GatorOscillator { match GatorOscillator::new(jaw_period, teeth_period, lips_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_gator_oscillator_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gator_oscillator_update( handle: *mut GatorOscillator, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraGatorOscillatorOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraGatorOscillatorOutput { upper: out_val.upper, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_gator_oscillator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gator_oscillator_reset(handle: *mut GatorOscillator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_gator_oscillator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_gator_oscillator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_gator_oscillator_free(handle: *mut GatorOscillator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `GoldenPocket` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_golden_pocket_free`. #[no_mangle] pub extern "C" fn wickra_golden_pocket_new() -> *mut GoldenPocket { Box::into_raw(Box::new(GoldenPocket::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_golden_pocket_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_golden_pocket_update( handle: *mut GoldenPocket, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraGoldenPocketOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraGoldenPocketOutput { low: out_val.low, mid: out_val.mid, high: out_val.high, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_golden_pocket_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_golden_pocket_reset(handle: *mut GoldenPocket) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_golden_pocket_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_golden_pocket_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_golden_pocket_free(handle: *mut GoldenPocket) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HeikinAshi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_heikin_ashi_free`. #[no_mangle] pub extern "C" fn wickra_heikin_ashi_new() -> *mut HeikinAshi { Box::into_raw(Box::new(HeikinAshi::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_heikin_ashi_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_heikin_ashi_update( handle: *mut HeikinAshi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraHeikinAshiOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraHeikinAshiOutput { open: out_val.open, high: out_val.high, low: out_val.low, close: out_val.close, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_heikin_ashi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_heikin_ashi_reset(handle: *mut HeikinAshi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_heikin_ashi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_heikin_ashi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_heikin_ashi_free(handle: *mut HeikinAshi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HighLowVolumeNodes` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_high_low_volume_nodes_free`. #[no_mangle] pub extern "C" fn wickra_high_low_volume_nodes_new( period: usize, bins: usize, ) -> *mut HighLowVolumeNodes { match HighLowVolumeNodes::new(period, bins) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_high_low_volume_nodes_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_volume_nodes_update( handle: *mut HighLowVolumeNodes, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraHighLowVolumeNodesOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraHighLowVolumeNodesOutput { hvn: out_val.hvn, lvn: out_val.lvn, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_high_low_volume_nodes_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_volume_nodes_reset(handle: *mut HighLowVolumeNodes) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_high_low_volume_nodes_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_high_low_volume_nodes_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_high_low_volume_nodes_free(handle: *mut HighLowVolumeNodes) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HtPhasor` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ht_phasor_free`. #[no_mangle] pub extern "C" fn wickra_ht_phasor_new() -> *mut HtPhasor { Box::into_raw(Box::new(HtPhasor::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_ht_phasor_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_phasor_update( handle: *mut HtPhasor, value: f64, out: *mut WickraHtPhasorOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraHtPhasorOutput { inphase: out_val.inphase, quadrature: out_val.quadrature, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ht_phasor_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_phasor_reset(handle: *mut HtPhasor) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ht_phasor_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ht_phasor_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ht_phasor_free(handle: *mut HtPhasor) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `HurstChannel` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_hurst_channel_free`. #[no_mangle] pub extern "C" fn wickra_hurst_channel_new(period: usize, multiplier: f64) -> *mut HurstChannel { match HurstChannel::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_hurst_channel_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hurst_channel_update( handle: *mut HurstChannel, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraHurstChannelOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraHurstChannelOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_hurst_channel_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hurst_channel_reset(handle: *mut HurstChannel) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_hurst_channel_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_hurst_channel_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_hurst_channel_free(handle: *mut HurstChannel) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Ichimoku` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ichimoku_free`. #[no_mangle] pub extern "C" fn wickra_ichimoku_new( tenkan_period: usize, kijun_period: usize, senkou_b_period: usize, displacement: usize, ) -> *mut Ichimoku { match Ichimoku::new(tenkan_period, kijun_period, senkou_b_period, displacement) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_ichimoku_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ichimoku_update( handle: *mut Ichimoku, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraIchimokuOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraIchimokuOutput { tenkan: out_val.tenkan.unwrap_or(f64::NAN), kijun: out_val.kijun.unwrap_or(f64::NAN), senkou_a: out_val.senkou_a.unwrap_or(f64::NAN), senkou_b: out_val.senkou_b.unwrap_or(f64::NAN), chikou: out_val.chikou.unwrap_or(f64::NAN), }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ichimoku_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ichimoku_reset(handle: *mut Ichimoku) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ichimoku_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ichimoku_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ichimoku_free(handle: *mut Ichimoku) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `InitialBalance` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_initial_balance_free`. #[no_mangle] pub extern "C" fn wickra_initial_balance_new(period: usize) -> *mut InitialBalance { match InitialBalance::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_initial_balance_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_initial_balance_update( handle: *mut InitialBalance, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraInitialBalanceOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraInitialBalanceOutput { high: out_val.high, low: out_val.low, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_initial_balance_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_initial_balance_reset(handle: *mut InitialBalance) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_initial_balance_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_initial_balance_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_initial_balance_free(handle: *mut InitialBalance) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KalmanHedgeRatio` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kalman_hedge_ratio_free`. #[no_mangle] pub extern "C" fn wickra_kalman_hedge_ratio_new( delta: f64, observation_var: f64, ) -> *mut KalmanHedgeRatio { match KalmanHedgeRatio::new(delta, observation_var) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_kalman_hedge_ratio_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kalman_hedge_ratio_update( handle: *mut KalmanHedgeRatio, x: f64, y: f64, out: *mut WickraKalmanHedgeRatioOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update((x, y)) { Some(out_val) => { *out = WickraKalmanHedgeRatioOutput { hedge_ratio: out_val.hedge_ratio, intercept: out_val.intercept, spread: out_val.spread, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kalman_hedge_ratio_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kalman_hedge_ratio_reset(handle: *mut KalmanHedgeRatio) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kalman_hedge_ratio_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kalman_hedge_ratio_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kalman_hedge_ratio_free(handle: *mut KalmanHedgeRatio) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KaseDevStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kase_dev_stop_free`. #[no_mangle] pub extern "C" fn wickra_kase_dev_stop_new(period: usize, dev: f64) -> *mut KaseDevStop { match KaseDevStop::new(period, dev) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_kase_dev_stop_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kase_dev_stop_update( handle: *mut KaseDevStop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraKaseDevStopOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraKaseDevStopOutput { value: out_val.value, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kase_dev_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kase_dev_stop_reset(handle: *mut KaseDevStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kase_dev_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kase_dev_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kase_dev_stop_free(handle: *mut KaseDevStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KasePermissionStochastic` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kase_permission_stochastic_free`. #[no_mangle] pub extern "C" fn wickra_kase_permission_stochastic_new( length: usize, smooth: usize, ) -> *mut KasePermissionStochastic { match KasePermissionStochastic::new(length, smooth) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_kase_permission_stochastic_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kase_permission_stochastic_update( handle: *mut KasePermissionStochastic, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraKasePermissionStochasticOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraKasePermissionStochasticOutput { fast: out_val.fast, slow: out_val.slow, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kase_permission_stochastic_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kase_permission_stochastic_reset( handle: *mut KasePermissionStochastic, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kase_permission_stochastic_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kase_permission_stochastic_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kase_permission_stochastic_free( handle: *mut KasePermissionStochastic, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Keltner` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_keltner_free`. #[no_mangle] pub extern "C" fn wickra_keltner_new( ema_period: usize, atr_period: usize, multiplier: f64, ) -> *mut Keltner { match Keltner::new(ema_period, atr_period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_keltner_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_keltner_update( handle: *mut Keltner, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraKeltnerOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraKeltnerOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_keltner_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_keltner_reset(handle: *mut Keltner) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_keltner_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_keltner_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_keltner_free(handle: *mut Keltner) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Kst` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_kst_free`. #[no_mangle] pub extern "C" fn wickra_kst_new( roc1: usize, roc2: usize, roc3: usize, roc4: usize, sma1: usize, sma2: usize, sma3: usize, sma4: usize, signal: usize, ) -> *mut Kst { match Kst::new(roc1, roc2, roc3, roc4, sma1, sma2, sma3, sma4, signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_kst_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kst_update( handle: *mut Kst, value: f64, out: *mut WickraKstOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraKstOutput { kst: out_val.kst, signal: out_val.signal, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kst_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kst_reset(handle: *mut Kst) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kst_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kst_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kst_free(handle: *mut Kst) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LeadLagCrossCorrelation` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_lead_lag_cross_correlation_free`. #[no_mangle] pub extern "C" fn wickra_lead_lag_cross_correlation_new( window: usize, max_lag: usize, ) -> *mut LeadLagCrossCorrelation { match LeadLagCrossCorrelation::new(window, max_lag) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_lead_lag_cross_correlation_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lead_lag_cross_correlation_update( handle: *mut LeadLagCrossCorrelation, x: f64, y: f64, out: *mut WickraLeadLagCrossCorrelationOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update((x, y)) { Some(out_val) => { *out = WickraLeadLagCrossCorrelationOutput { lag: out_val.lag, correlation: out_val.correlation, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_lead_lag_cross_correlation_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lead_lag_cross_correlation_reset( handle: *mut LeadLagCrossCorrelation, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_lead_lag_cross_correlation_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_lead_lag_cross_correlation_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lead_lag_cross_correlation_free( handle: *mut LeadLagCrossCorrelation, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LinRegChannel` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_lin_reg_channel_free`. #[no_mangle] pub extern "C" fn wickra_lin_reg_channel_new(period: usize, multiplier: f64) -> *mut LinRegChannel { match LinRegChannel::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_lin_reg_channel_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_channel_update( handle: *mut LinRegChannel, value: f64, out: *mut WickraLinRegChannelOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraLinRegChannelOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_lin_reg_channel_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_channel_reset(handle: *mut LinRegChannel) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_lin_reg_channel_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_lin_reg_channel_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_lin_reg_channel_free(handle: *mut LinRegChannel) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `LiquidationFeatures` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_liquidation_features_free`. #[no_mangle] pub extern "C" fn wickra_liquidation_features_new() -> *mut LiquidationFeatures { Box::into_raw(Box::new(LiquidationFeatures::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_liquidation_features_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_liquidation_features_update( handle: *mut LiquidationFeatures, funding_rate: f64, mark_price: f64, index_price: f64, futures_price: f64, open_interest: f64, long_size: f64, short_size: f64, taker_buy_volume: f64, taker_sell_volume: f64, long_liquidation: f64, short_liquidation: f64, timestamp: i64, out: *mut WickraLiquidationFeaturesOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = DerivativesTick::new( funding_rate, mark_price, index_price, futures_price, open_interest, long_size, short_size, taker_buy_volume, taker_sell_volume, long_liquidation, short_liquidation, timestamp, ) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraLiquidationFeaturesOutput { long: out_val.long, short: out_val.short, net: out_val.net, total: out_val.total, imbalance: out_val.imbalance, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_liquidation_features_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_liquidation_features_reset(handle: *mut LiquidationFeatures) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_liquidation_features_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_liquidation_features_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_liquidation_features_free(handle: *mut LiquidationFeatures) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MaEnvelope` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ma_envelope_free`. #[no_mangle] pub extern "C" fn wickra_ma_envelope_new(period: usize, percent: f64) -> *mut MaEnvelope { match MaEnvelope::new(period, percent) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_ma_envelope_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ma_envelope_update( handle: *mut MaEnvelope, value: f64, out: *mut WickraMaEnvelopeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraMaEnvelopeOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ma_envelope_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ma_envelope_reset(handle: *mut MaEnvelope) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ma_envelope_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ma_envelope_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ma_envelope_free(handle: *mut MaEnvelope) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MacdIndicator` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_macd_indicator_free`. #[no_mangle] pub extern "C" fn wickra_macd_indicator_new( fast: usize, slow: usize, signal: usize, ) -> *mut MacdIndicator { match MacdIndicator::new(fast, slow, signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_macd_indicator_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_indicator_update( handle: *mut MacdIndicator, value: f64, out: *mut WickraMacdOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraMacdOutput { macd: out_val.macd, signal: out_val.signal, histogram: out_val.histogram, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_macd_indicator_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_indicator_reset(handle: *mut MacdIndicator) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_macd_indicator_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_macd_indicator_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_indicator_free(handle: *mut MacdIndicator) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MacdFix` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_macd_fix_free`. #[no_mangle] pub extern "C" fn wickra_macd_fix_new(signal: usize) -> *mut MacdFix { match MacdFix::new(signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_macd_fix_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_fix_update( handle: *mut MacdFix, value: f64, out: *mut WickraMacdOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraMacdOutput { macd: out_val.macd, signal: out_val.signal, histogram: out_val.histogram, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_macd_fix_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_fix_reset(handle: *mut MacdFix) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_macd_fix_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_macd_fix_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_fix_free(handle: *mut MacdFix) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Mama` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_mama_free`. #[no_mangle] pub extern "C" fn wickra_mama_new(fast_limit: f64, slow_limit: f64) -> *mut Mama { match Mama::new(fast_limit, slow_limit) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_mama_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mama_update( handle: *mut Mama, value: f64, out: *mut WickraMamaOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraMamaOutput { mama: out_val.mama, fama: out_val.fama, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_mama_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mama_reset(handle: *mut Mama) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_mama_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_mama_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_mama_free(handle: *mut Mama) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MedianChannel` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_median_channel_free`. #[no_mangle] pub extern "C" fn wickra_median_channel_new(period: usize, multiplier: f64) -> *mut MedianChannel { match MedianChannel::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_median_channel_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_channel_update( handle: *mut MedianChannel, value: f64, out: *mut WickraMedianChannelOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraMedianChannelOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_median_channel_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_channel_reset(handle: *mut MedianChannel) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_median_channel_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_median_channel_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_median_channel_free(handle: *mut MedianChannel) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ModifiedMaStop` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_modified_ma_stop_free`. #[no_mangle] pub extern "C" fn wickra_modified_ma_stop_new(period: usize) -> *mut ModifiedMaStop { match ModifiedMaStop::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_modified_ma_stop_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_modified_ma_stop_update( handle: *mut ModifiedMaStop, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraModifiedMaStopOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraModifiedMaStopOutput { value: out_val.value, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_modified_ma_stop_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_modified_ma_stop_reset(handle: *mut ModifiedMaStop) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_modified_ma_stop_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_modified_ma_stop_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_modified_ma_stop_free(handle: *mut ModifiedMaStop) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `MurreyMathLines` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_murrey_math_lines_free`. #[no_mangle] pub extern "C" fn wickra_murrey_math_lines_new(period: usize) -> *mut MurreyMathLines { match MurreyMathLines::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_murrey_math_lines_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_murrey_math_lines_update( handle: *mut MurreyMathLines, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraMurreyMathLinesOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraMurreyMathLinesOutput { mm8_8: out_val.mm8_8, mm7_8: out_val.mm7_8, mm6_8: out_val.mm6_8, mm5_8: out_val.mm5_8, mm4_8: out_val.mm4_8, mm3_8: out_val.mm3_8, mm2_8: out_val.mm2_8, mm1_8: out_val.mm1_8, mm0_8: out_val.mm0_8, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_murrey_math_lines_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_murrey_math_lines_reset(handle: *mut MurreyMathLines) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_murrey_math_lines_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_murrey_math_lines_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_murrey_math_lines_free(handle: *mut MurreyMathLines) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Nrtr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_nrtr_free`. #[no_mangle] pub extern "C" fn wickra_nrtr_new(pct: f64) -> *mut Nrtr { match Nrtr::new(pct) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_nrtr_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_nrtr_update( handle: *mut Nrtr, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraNrtrOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraNrtrOutput { value: out_val.value, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_nrtr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_nrtr_reset(handle: *mut Nrtr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_nrtr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_nrtr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_nrtr_free(handle: *mut Nrtr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OpeningRange` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_opening_range_free`. #[no_mangle] pub extern "C" fn wickra_opening_range_new(period: usize) -> *mut OpeningRange { match OpeningRange::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_opening_range_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_opening_range_update( handle: *mut OpeningRange, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraOpeningRangeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraOpeningRangeOutput { high: out_val.high, low: out_val.low, breakout_distance: out_val.breakout_distance, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_opening_range_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_opening_range_reset(handle: *mut OpeningRange) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_opening_range_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_opening_range_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_opening_range_free(handle: *mut OpeningRange) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `OvernightIntradayReturn` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_overnight_intraday_return_free`. #[no_mangle] pub extern "C" fn wickra_overnight_intraday_return_new( utc_offset_minutes: i32, ) -> *mut OvernightIntradayReturn { Box::into_raw(Box::new(OvernightIntradayReturn::new(utc_offset_minutes))) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_overnight_intraday_return_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_overnight_intraday_return_update( handle: *mut OvernightIntradayReturn, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraOvernightIntradayReturnOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraOvernightIntradayReturnOutput { overnight: out_val.overnight, intraday: out_val.intraday, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_overnight_intraday_return_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_overnight_intraday_return_reset( handle: *mut OvernightIntradayReturn, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_overnight_intraday_return_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_overnight_intraday_return_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_overnight_intraday_return_free( handle: *mut OvernightIntradayReturn, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ProjectionBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_projection_bands_free`. #[no_mangle] pub extern "C" fn wickra_projection_bands_new(period: usize) -> *mut ProjectionBands { match ProjectionBands::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_projection_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_projection_bands_update( handle: *mut ProjectionBands, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraProjectionBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraProjectionBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_projection_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_projection_bands_reset(handle: *mut ProjectionBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_projection_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_projection_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_projection_bands_free(handle: *mut ProjectionBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Qqe` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_qqe_free`. #[no_mangle] pub extern "C" fn wickra_qqe_new(rsi_period: usize, smoothing: usize, factor: f64) -> *mut Qqe { match Qqe::new(rsi_period, smoothing, factor) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_qqe_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_qqe_update( handle: *mut Qqe, value: f64, out: *mut WickraQqeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraQqeOutput { rsi_ma: out_val.rsi_ma, trailing_line: out_val.trailing_line, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_qqe_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_qqe_reset(handle: *mut Qqe) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_qqe_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_qqe_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_qqe_free(handle: *mut Qqe) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `QuartileBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_quartile_bands_free`. #[no_mangle] pub extern "C" fn wickra_quartile_bands_new(period: usize) -> *mut QuartileBands { match QuartileBands::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_quartile_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_quartile_bands_update( handle: *mut QuartileBands, value: f64, out: *mut WickraQuartileBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraQuartileBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_quartile_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_quartile_bands_reset(handle: *mut QuartileBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_quartile_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_quartile_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_quartile_bands_free(handle: *mut QuartileBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RelativeStrengthAB` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_relative_strength_ab_free`. #[no_mangle] pub extern "C" fn wickra_relative_strength_ab_new( ma_period: usize, rsi_period: usize, ) -> *mut RelativeStrengthAB { match RelativeStrengthAB::new(ma_period, rsi_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_relative_strength_ab_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_relative_strength_ab_update( handle: *mut RelativeStrengthAB, x: f64, y: f64, out: *mut WickraRelativeStrengthOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update((x, y)) { Some(out_val) => { *out = WickraRelativeStrengthOutput { ratio: out_val.ratio, ratio_ma: out_val.ratio_ma, ratio_rsi: out_val.ratio_rsi, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_relative_strength_ab_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_relative_strength_ab_reset(handle: *mut RelativeStrengthAB) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_relative_strength_ab_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_relative_strength_ab_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_relative_strength_ab_free(handle: *mut RelativeStrengthAB) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Rwi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_rwi_free`. #[no_mangle] pub extern "C" fn wickra_rwi_new(period: usize) -> *mut Rwi { match Rwi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_rwi_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rwi_update( handle: *mut Rwi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraRwiOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraRwiOutput { high: out_val.high, low: out_val.low, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_rwi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rwi_reset(handle: *mut Rwi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_rwi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_rwi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_rwi_free(handle: *mut Rwi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SessionHighLow` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_session_high_low_free`. #[no_mangle] pub extern "C" fn wickra_session_high_low_new(utc_offset_minutes: i32) -> *mut SessionHighLow { Box::into_raw(Box::new(SessionHighLow::new(utc_offset_minutes))) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_session_high_low_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_high_low_update( handle: *mut SessionHighLow, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraSessionHighLowOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraSessionHighLowOutput { high: out_val.high, low: out_val.low, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_session_high_low_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_high_low_reset(handle: *mut SessionHighLow) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_session_high_low_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_session_high_low_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_high_low_free(handle: *mut SessionHighLow) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SessionRange` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_session_range_free`. #[no_mangle] pub extern "C" fn wickra_session_range_new(utc_offset_minutes: i32) -> *mut SessionRange { Box::into_raw(Box::new(SessionRange::new(utc_offset_minutes))) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_session_range_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_range_update( handle: *mut SessionRange, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraSessionRangeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraSessionRangeOutput { asia: out_val.asia, eu: out_val.eu, us: out_val.us, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_session_range_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_range_reset(handle: *mut SessionRange) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_session_range_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_session_range_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_session_range_free(handle: *mut SessionRange) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SmoothedHeikinAshi` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_smoothed_heikin_ashi_free`. #[no_mangle] pub extern "C" fn wickra_smoothed_heikin_ashi_new(period: usize) -> *mut SmoothedHeikinAshi { match SmoothedHeikinAshi::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_smoothed_heikin_ashi_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smoothed_heikin_ashi_update( handle: *mut SmoothedHeikinAshi, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraSmoothedHeikinAshiOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraSmoothedHeikinAshiOutput { open: out_val.open, high: out_val.high, low: out_val.low, close: out_val.close, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_smoothed_heikin_ashi_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smoothed_heikin_ashi_reset(handle: *mut SmoothedHeikinAshi) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_smoothed_heikin_ashi_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_smoothed_heikin_ashi_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_smoothed_heikin_ashi_free(handle: *mut SmoothedHeikinAshi) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SpreadBollingerBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_spread_bollinger_bands_free`. #[no_mangle] pub extern "C" fn wickra_spread_bollinger_bands_new( period: usize, num_std: f64, ) -> *mut SpreadBollingerBands { match SpreadBollingerBands::new(period, num_std) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_spread_bollinger_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_bollinger_bands_update( handle: *mut SpreadBollingerBands, x: f64, y: f64, out: *mut WickraSpreadBollingerBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update((x, y)) { Some(out_val) => { *out = WickraSpreadBollingerBandsOutput { middle: out_val.middle, upper: out_val.upper, lower: out_val.lower, percent_b: out_val.percent_b, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_spread_bollinger_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_bollinger_bands_reset(handle: *mut SpreadBollingerBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_spread_bollinger_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_spread_bollinger_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_spread_bollinger_bands_free(handle: *mut SpreadBollingerBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StandardErrorBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_standard_error_bands_free`. #[no_mangle] pub extern "C" fn wickra_standard_error_bands_new( period: usize, multiplier: f64, ) -> *mut StandardErrorBands { match StandardErrorBands::new(period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_standard_error_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_standard_error_bands_update( handle: *mut StandardErrorBands, value: f64, out: *mut WickraStandardErrorBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraStandardErrorBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_standard_error_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_standard_error_bands_reset(handle: *mut StandardErrorBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_standard_error_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_standard_error_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_standard_error_bands_free(handle: *mut StandardErrorBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `StarcBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_starc_bands_free`. #[no_mangle] pub extern "C" fn wickra_starc_bands_new( sma_period: usize, atr_period: usize, multiplier: f64, ) -> *mut StarcBands { match StarcBands::new(sma_period, atr_period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_starc_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_starc_bands_update( handle: *mut StarcBands, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraStarcBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraStarcBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_starc_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_starc_bands_reset(handle: *mut StarcBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_starc_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_starc_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_starc_bands_free(handle: *mut StarcBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Stochastic` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_stochastic_free`. #[no_mangle] pub extern "C" fn wickra_stochastic_new(k_period: usize, d_period: usize) -> *mut Stochastic { match Stochastic::new(k_period, d_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_stochastic_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stochastic_update( handle: *mut Stochastic, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraStochasticOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraStochasticOutput { k: out_val.k, d: out_val.d, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_stochastic_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stochastic_reset(handle: *mut Stochastic) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_stochastic_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_stochastic_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_stochastic_free(handle: *mut Stochastic) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `SuperTrend` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_super_trend_free`. #[no_mangle] pub extern "C" fn wickra_super_trend_new(atr_period: usize, multiplier: f64) -> *mut SuperTrend { match SuperTrend::new(atr_period, multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_super_trend_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_super_trend_update( handle: *mut SuperTrend, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraSuperTrendOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraSuperTrendOutput { value: out_val.value, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_super_trend_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_super_trend_reset(handle: *mut SuperTrend) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_super_trend_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_super_trend_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_super_trend_free(handle: *mut SuperTrend) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdLines` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_lines_free`. #[no_mangle] pub extern "C" fn wickra_td_lines_new(lookback: usize, target: usize) -> *mut TdLines { match TdLines::new(lookback, target) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_td_lines_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_lines_update( handle: *mut TdLines, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraTdLinesOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraTdLinesOutput { resistance: out_val.resistance, support: out_val.support, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_lines_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_lines_reset(handle: *mut TdLines) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_lines_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_lines_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_lines_free(handle: *mut TdLines) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdMovingAverage` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_moving_average_free`. #[no_mangle] pub extern "C" fn wickra_td_moving_average_new( period_st1: usize, period_st2: usize, ) -> *mut TdMovingAverage { match TdMovingAverage::new(period_st1, period_st2) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_td_moving_average_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_moving_average_update( handle: *mut TdMovingAverage, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraTdMovingAverageOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraTdMovingAverageOutput { st1: out_val.st1, st2: out_val.st2, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_moving_average_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_moving_average_reset(handle: *mut TdMovingAverage) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_moving_average_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_moving_average_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_moving_average_free(handle: *mut TdMovingAverage) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdRangeProjection` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_range_projection_free`. #[no_mangle] pub extern "C" fn wickra_td_range_projection_new() -> *mut TdRangeProjection { Box::into_raw(Box::new(TdRangeProjection::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_td_range_projection_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_range_projection_update( handle: *mut TdRangeProjection, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraTdRangeProjectionOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraTdRangeProjectionOutput { high: out_val.high, low: out_val.low, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_range_projection_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_range_projection_reset(handle: *mut TdRangeProjection) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_range_projection_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_range_projection_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_range_projection_free(handle: *mut TdRangeProjection) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdRiskLevel` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_risk_level_free`. #[no_mangle] pub extern "C" fn wickra_td_risk_level_new(lookback: usize, target: usize) -> *mut TdRiskLevel { match TdRiskLevel::new(lookback, target) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_td_risk_level_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_risk_level_update( handle: *mut TdRiskLevel, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraTdRiskLevelOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraTdRiskLevelOutput { buy_risk: out_val.buy_risk, sell_risk: out_val.sell_risk, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_risk_level_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_risk_level_reset(handle: *mut TdRiskLevel) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_risk_level_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_risk_level_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_risk_level_free(handle: *mut TdRiskLevel) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TdSequential` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_td_sequential_free`. #[no_mangle] pub extern "C" fn wickra_td_sequential_new( setup_lookback: usize, setup_target: usize, countdown_lookback: usize, countdown_target: usize, ) -> *mut TdSequential { match TdSequential::new( setup_lookback, setup_target, countdown_lookback, countdown_target, ) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_td_sequential_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_sequential_update( handle: *mut TdSequential, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraTdSequentialOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraTdSequentialOutput { setup: out_val.setup, countdown: out_val.countdown, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_td_sequential_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_sequential_reset(handle: *mut TdSequential) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_td_sequential_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_td_sequential_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_td_sequential_free(handle: *mut TdSequential) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TtmSqueeze` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_ttm_squeeze_free`. #[no_mangle] pub extern "C" fn wickra_ttm_squeeze_new( period: usize, bb_mult: f64, kc_mult: f64, ) -> *mut TtmSqueeze { match TtmSqueeze::new(period, bb_mult, kc_mult) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_ttm_squeeze_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ttm_squeeze_update( handle: *mut TtmSqueeze, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraTtmSqueezeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraTtmSqueezeOutput { squeeze: out_val.squeeze, momentum: out_val.momentum, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_ttm_squeeze_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ttm_squeeze_reset(handle: *mut TtmSqueeze) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_ttm_squeeze_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_ttm_squeeze_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_ttm_squeeze_free(handle: *mut TtmSqueeze) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ValueArea` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_value_area_free`. #[no_mangle] pub extern "C" fn wickra_value_area_new( period: usize, bin_count: usize, value_area_pct: f64, ) -> *mut ValueArea { match ValueArea::new(period, bin_count, value_area_pct) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_value_area_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_value_area_update( handle: *mut ValueArea, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraValueAreaOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraValueAreaOutput { poc: out_val.poc, vah: out_val.vah, val: out_val.val, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_value_area_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_value_area_reset(handle: *mut ValueArea) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_value_area_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_value_area_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_value_area_free(handle: *mut ValueArea) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolatilityCone` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volatility_cone_free`. #[no_mangle] pub extern "C" fn wickra_volatility_cone_new( window: usize, lookback: usize, ) -> *mut VolatilityCone { match VolatilityCone::new(window, lookback) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_volatility_cone_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_cone_update( handle: *mut VolatilityCone, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraVolatilityConeOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraVolatilityConeOutput { current: out_val.current, min: out_val.min, median: out_val.median, max: out_val.max, percentile: out_val.percentile, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volatility_cone_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_cone_reset(handle: *mut VolatilityCone) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volatility_cone_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volatility_cone_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volatility_cone_free(handle: *mut VolatilityCone) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumeWeightedMacd` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_weighted_macd_free`. #[no_mangle] pub extern "C" fn wickra_volume_weighted_macd_new( fast: usize, slow: usize, signal: usize, ) -> *mut VolumeWeightedMacd { match VolumeWeightedMacd::new(fast, slow, signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_volume_weighted_macd_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_weighted_macd_update( handle: *mut VolumeWeightedMacd, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraVolumeWeightedMacdOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraVolumeWeightedMacdOutput { macd: out_val.macd, signal: out_val.signal, histogram: out_val.histogram, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_weighted_macd_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_weighted_macd_reset(handle: *mut VolumeWeightedMacd) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_weighted_macd_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_weighted_macd_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_weighted_macd_free(handle: *mut VolumeWeightedMacd) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumeWeightedSr` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_weighted_sr_free`. #[no_mangle] pub extern "C" fn wickra_volume_weighted_sr_new(period: usize) -> *mut VolumeWeightedSr { match VolumeWeightedSr::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_volume_weighted_sr_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_weighted_sr_update( handle: *mut VolumeWeightedSr, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraVolumeWeightedSrOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraVolumeWeightedSrOutput { support: out_val.support, resistance: out_val.resistance, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_weighted_sr_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_weighted_sr_reset(handle: *mut VolumeWeightedSr) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_weighted_sr_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_weighted_sr_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_weighted_sr_free(handle: *mut VolumeWeightedSr) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `Vortex` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vortex_free`. #[no_mangle] pub extern "C" fn wickra_vortex_new(period: usize) -> *mut Vortex { match Vortex::new(period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_vortex_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vortex_update( handle: *mut Vortex, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraVortexOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraVortexOutput { plus: out_val.plus, minus: out_val.minus, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vortex_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vortex_reset(handle: *mut Vortex) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vortex_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vortex_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vortex_free(handle: *mut Vortex) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VwapStdDevBands` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_vwap_std_dev_bands_free`. #[no_mangle] pub extern "C" fn wickra_vwap_std_dev_bands_new(multiplier: f64) -> *mut VwapStdDevBands { match VwapStdDevBands::new(multiplier) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_vwap_std_dev_bands_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwap_std_dev_bands_update( handle: *mut VwapStdDevBands, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraVwapStdDevBandsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraVwapStdDevBandsOutput { upper: out_val.upper, middle: out_val.middle, lower: out_val.lower, stddev: out_val.stddev, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_vwap_std_dev_bands_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwap_std_dev_bands_reset(handle: *mut VwapStdDevBands) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_vwap_std_dev_bands_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_vwap_std_dev_bands_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_vwap_std_dev_bands_free(handle: *mut VwapStdDevBands) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WaveTrend` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_wave_trend_free`. #[no_mangle] pub extern "C" fn wickra_wave_trend_new( channel_period: usize, average_period: usize, signal_period: usize, ) -> *mut WaveTrend { match WaveTrend::new(channel_period, average_period, signal_period) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_wave_trend_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wave_trend_update( handle: *mut WaveTrend, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraWaveTrendOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraWaveTrendOutput { wt1: out_val.wt1, wt2: out_val.wt2, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_wave_trend_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wave_trend_reset(handle: *mut WaveTrend) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_wave_trend_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_wave_trend_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_wave_trend_free(handle: *mut WaveTrend) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WilliamsFractals` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_williams_fractals_free`. #[no_mangle] pub extern "C" fn wickra_williams_fractals_new() -> *mut WilliamsFractals { Box::into_raw(Box::new(WilliamsFractals::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_williams_fractals_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_williams_fractals_update( handle: *mut WilliamsFractals, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraWilliamsFractalsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraWilliamsFractalsOutput { up: out_val.up.unwrap_or(f64::NAN), down: out_val.down.unwrap_or(f64::NAN), }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_williams_fractals_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_williams_fractals_reset(handle: *mut WilliamsFractals) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_williams_fractals_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_williams_fractals_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_williams_fractals_free(handle: *mut WilliamsFractals) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `WoodiePivots` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_woodie_pivots_free`. #[no_mangle] pub extern "C" fn wickra_woodie_pivots_new() -> *mut WoodiePivots { Box::into_raw(Box::new(WoodiePivots::new())) } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_woodie_pivots_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_woodie_pivots_update( handle: *mut WoodiePivots, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraWoodiePivotsOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraWoodiePivotsOutput { pp: out_val.pp, r1: out_val.r1, r2: out_val.r2, s1: out_val.s1, s2: out_val.s2, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_woodie_pivots_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_woodie_pivots_reset(handle: *mut WoodiePivots) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_woodie_pivots_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_woodie_pivots_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_woodie_pivots_free(handle: *mut WoodiePivots) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ZeroLagMacd` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_zero_lag_macd_free`. #[no_mangle] pub extern "C" fn wickra_zero_lag_macd_new( fast: usize, slow: usize, signal: usize, ) -> *mut ZeroLagMacd { match ZeroLagMacd::new(fast, slow, signal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_zero_lag_macd_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zero_lag_macd_update( handle: *mut ZeroLagMacd, value: f64, out: *mut WickraZeroLagMacdOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraZeroLagMacdOutput { macd: out_val.macd, signal: out_val.signal, histogram: out_val.histogram, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_zero_lag_macd_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zero_lag_macd_reset(handle: *mut ZeroLagMacd) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_zero_lag_macd_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_zero_lag_macd_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zero_lag_macd_free(handle: *mut ZeroLagMacd) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ZigZag` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_zig_zag_free`. #[no_mangle] pub extern "C" fn wickra_zig_zag_new(threshold: f64) -> *mut ZigZag { match ZigZag::new(threshold) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input; on `true` the freshly computed multi-field output is written to /// `*out`. Returns `false` during warmup, on a `NULL` handle / `out`, or on invalid input. /// /// # Safety /// `handle` (from `wickra_zig_zag_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zig_zag_update( handle: *mut ZigZag, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraZigZagOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return false; }; match ind.update(input) { Some(out_val) => { *out = WickraZigZagOutput { swing: out_val.swing, direction: out_val.direction, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_zig_zag_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zig_zag_reset(handle: *mut ZigZag) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_zig_zag_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_zig_zag_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_zig_zag_free(handle: *mut ZigZag) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Vec-output indicators (profiles; caller buffer + length return) ===== /// Scalar fields of `TpoProfileOutput` (its `Vec` payload is delivered separately). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTpoProfileOutputScalars { pub price_low: f64, pub price_high: f64, } /// Scalar fields of `VolumeProfileOutput` (its `Vec` payload is delivered separately). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraVolumeProfileOutputScalars { pub price_low: f64, pub price_high: f64, } /// Create a `DayOfWeekProfile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_day_of_week_profile_free`. #[no_mangle] pub extern "C" fn wickra_day_of_week_profile_new(utc_offset_minutes: i32) -> *mut DayOfWeekProfile { Box::into_raw(Box::new(DayOfWeekProfile::new(utc_offset_minutes))) } /// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise /// returns the length of the `bins` payload and writes up to `cap` of its values /// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written /// to `*scalars` when non-`NULL`. /// /// # Safety /// `handle` (from `wickra_day_of_week_profile_new`, not freed), `scalars` and `values` must be valid /// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_day_of_week_profile_update( handle: *mut DayOfWeekProfile, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, values: *mut f64, cap: usize, ) -> isize { let Some(ind) = handle.as_mut() else { return -1; }; let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return -1; }; match ind.update(input) { Some(out_val) => { if !values.is_null() { let slots = slice::from_raw_parts_mut(values, cap); for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { *slot = v; } } isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) } None => -1, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_day_of_week_profile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_day_of_week_profile_reset(handle: *mut DayOfWeekProfile) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_day_of_week_profile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_day_of_week_profile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_day_of_week_profile_free(handle: *mut DayOfWeekProfile) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `IntradayVolatilityProfile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_intraday_volatility_profile_free`. #[no_mangle] pub extern "C" fn wickra_intraday_volatility_profile_new( buckets: usize, utc_offset_minutes: i32, ) -> *mut IntradayVolatilityProfile { match IntradayVolatilityProfile::new(buckets, utc_offset_minutes) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise /// returns the length of the `bins` payload and writes up to `cap` of its values /// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written /// to `*scalars` when non-`NULL`. /// /// # Safety /// `handle` (from `wickra_intraday_volatility_profile_new`, not freed), `scalars` and `values` must be valid /// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_volatility_profile_update( handle: *mut IntradayVolatilityProfile, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, values: *mut f64, cap: usize, ) -> isize { let Some(ind) = handle.as_mut() else { return -1; }; let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return -1; }; match ind.update(input) { Some(out_val) => { if !values.is_null() { let slots = slice::from_raw_parts_mut(values, cap); for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { *slot = v; } } isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) } None => -1, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_intraday_volatility_profile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_volatility_profile_reset( handle: *mut IntradayVolatilityProfile, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_intraday_volatility_profile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_intraday_volatility_profile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_intraday_volatility_profile_free( handle: *mut IntradayVolatilityProfile, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TimeOfDayReturnProfile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_time_of_day_return_profile_free`. #[no_mangle] pub extern "C" fn wickra_time_of_day_return_profile_new( buckets: usize, utc_offset_minutes: i32, ) -> *mut TimeOfDayReturnProfile { match TimeOfDayReturnProfile::new(buckets, utc_offset_minutes) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise /// returns the length of the `bins` payload and writes up to `cap` of its values /// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written /// to `*scalars` when non-`NULL`. /// /// # Safety /// `handle` (from `wickra_time_of_day_return_profile_new`, not freed), `scalars` and `values` must be valid /// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_time_of_day_return_profile_update( handle: *mut TimeOfDayReturnProfile, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, values: *mut f64, cap: usize, ) -> isize { let Some(ind) = handle.as_mut() else { return -1; }; let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return -1; }; match ind.update(input) { Some(out_val) => { if !values.is_null() { let slots = slice::from_raw_parts_mut(values, cap); for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { *slot = v; } } isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) } None => -1, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_time_of_day_return_profile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_time_of_day_return_profile_reset( handle: *mut TimeOfDayReturnProfile, ) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_time_of_day_return_profile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_time_of_day_return_profile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_time_of_day_return_profile_free( handle: *mut TimeOfDayReturnProfile, ) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TpoProfile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_tpo_profile_free`. #[no_mangle] pub extern "C" fn wickra_tpo_profile_new(period: usize, bin_count: usize) -> *mut TpoProfile { match TpoProfile::new(period, bin_count) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise /// returns the length of the `counts` payload and writes up to `cap` of its values /// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written /// to `*scalars` when non-`NULL`. /// /// # Safety /// `handle` (from `wickra_tpo_profile_new`, not freed), `scalars` and `values` must be valid /// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_tpo_profile_update( handle: *mut TpoProfile, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, scalars: *mut WickraTpoProfileOutputScalars, values: *mut f64, cap: usize, ) -> isize { let Some(ind) = handle.as_mut() else { return -1; }; let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return -1; }; match ind.update(input) { Some(out_val) => { if !scalars.is_null() { *scalars = WickraTpoProfileOutputScalars { price_low: out_val.price_low, price_high: out_val.price_high, }; } if !values.is_null() { let slots = slice::from_raw_parts_mut(values, cap); for (slot, &v) in slots.iter_mut().zip(&out_val.counts) { *slot = v; } } isize::try_from(out_val.counts.len()).unwrap_or(isize::MAX) } None => -1, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tpo_profile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tpo_profile_reset(handle: *mut TpoProfile) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tpo_profile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tpo_profile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tpo_profile_free(handle: *mut TpoProfile) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumeByTimeProfile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_by_time_profile_free`. #[no_mangle] pub extern "C" fn wickra_volume_by_time_profile_new( buckets: usize, utc_offset_minutes: i32, ) -> *mut VolumeByTimeProfile { match VolumeByTimeProfile::new(buckets, utc_offset_minutes) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise /// returns the length of the `bins` payload and writes up to `cap` of its values /// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written /// to `*scalars` when non-`NULL`. /// /// # Safety /// `handle` (from `wickra_volume_by_time_profile_new`, not freed), `scalars` and `values` must be valid /// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_volume_by_time_profile_update( handle: *mut VolumeByTimeProfile, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, values: *mut f64, cap: usize, ) -> isize { let Some(ind) = handle.as_mut() else { return -1; }; let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return -1; }; match ind.update(input) { Some(out_val) => { if !values.is_null() { let slots = slice::from_raw_parts_mut(values, cap); for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { *slot = v; } } isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) } None => -1, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_by_time_profile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_by_time_profile_reset(handle: *mut VolumeByTimeProfile) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_by_time_profile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_by_time_profile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_by_time_profile_free(handle: *mut VolumeByTimeProfile) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumeProfile` indicator. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_profile_free`. #[no_mangle] pub extern "C" fn wickra_volume_profile_new(period: usize, bin_count: usize) -> *mut VolumeProfile { match VolumeProfile::new(period, bin_count) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one input. On warmup / `NULL` handle / invalid input returns `-1`. Otherwise /// returns the length of the `bins` payload and writes up to `cap` of its values /// into `values` (enlarge `cap` if the return exceeds it); the scalar fields are written /// to `*scalars` when non-`NULL`. /// /// # Safety /// `handle` (from `wickra_volume_profile_new`, not freed), `scalars` and `values` must be valid /// or `NULL`; when non-`NULL`, `values` must cover `cap` `double`s. #[no_mangle] pub unsafe extern "C" fn wickra_volume_profile_update( handle: *mut VolumeProfile, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, scalars: *mut WickraVolumeProfileOutputScalars, values: *mut f64, cap: usize, ) -> isize { let Some(ind) = handle.as_mut() else { return -1; }; let Ok(input) = Candle::new(open, high, low, close, volume, timestamp) else { return -1; }; match ind.update(input) { Some(out_val) => { if !scalars.is_null() { *scalars = WickraVolumeProfileOutputScalars { price_low: out_val.price_low, price_high: out_val.price_high, }; } if !values.is_null() { let slots = slice::from_raw_parts_mut(values, cap); for (slot, &v) in slots.iter_mut().zip(&out_val.bins) { *slot = v; } } isize::try_from(out_val.bins.len()).unwrap_or(isize::MAX) } None => -1, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_profile_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_profile_reset(handle: *mut VolumeProfile) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_profile_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_profile_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_profile_free(handle: *mut VolumeProfile) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Alt-chart bar builders (BarBuilder -> bars via caller buffer + count) ===== /// C-ABI view of one `DollarBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraDollarBar { pub open: f64, pub high: f64, pub low: f64, pub close: f64, pub volume: f64, pub dollar: f64, } /// C-ABI view of one `ImbalanceBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraImbalanceBar { pub open: f64, pub high: f64, pub low: f64, pub close: f64, pub imbalance: f64, pub direction: i8, } /// C-ABI view of one `KagiBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraKagiBar { pub start: f64, pub end: f64, pub direction: i8, } /// C-ABI view of one `PnfColumn` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraPnfColumn { pub direction: i8, pub high: f64, pub low: f64, } /// C-ABI view of one `RangeBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraRangeBar { pub open: f64, pub close: f64, pub direction: i8, } /// C-ABI view of one `RenkoBrick` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraRenkoBrick { pub open: f64, pub close: f64, pub direction: i8, } /// C-ABI view of one `RunBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraRunBar { pub open: f64, pub high: f64, pub low: f64, pub close: f64, pub length: usize, pub direction: i8, } /// C-ABI view of one `LineBreakBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraLineBreakBar { pub open: f64, pub close: f64, pub direction: i8, } /// C-ABI view of one `TickBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraTickBar { pub open: f64, pub high: f64, pub low: f64, pub close: f64, pub volume: f64, } /// C-ABI view of one `VolumeBar` (alt-chart bar element). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraVolumeBar { pub open: f64, pub high: f64, pub low: f64, pub close: f64, pub volume: f64, } /// Create a `DollarBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_dollar_bars_free`. #[no_mangle] pub extern "C" fn wickra_dollar_bars_new(dollar_per_bar: f64) -> *mut DollarBars { match DollarBars::new(dollar_per_bar) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_dollar_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraDollarBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_dollar_bars_update( handle: *mut DollarBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraDollarBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraDollarBar { open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, dollar: bar.dollar, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_dollar_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dollar_bars_reset(handle: *mut DollarBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_dollar_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_dollar_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_dollar_bars_free(handle: *mut DollarBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ImbalanceBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_imbalance_bars_free`. #[no_mangle] pub extern "C" fn wickra_imbalance_bars_new(threshold: f64) -> *mut ImbalanceBars { match ImbalanceBars::new(threshold) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_imbalance_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraImbalanceBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_imbalance_bars_update( handle: *mut ImbalanceBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraImbalanceBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraImbalanceBar { open: bar.open, high: bar.high, low: bar.low, close: bar.close, imbalance: bar.imbalance, direction: bar.direction, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_imbalance_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_imbalance_bars_reset(handle: *mut ImbalanceBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_imbalance_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_imbalance_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_imbalance_bars_free(handle: *mut ImbalanceBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `KagiBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_kagi_bars_free`. #[no_mangle] pub extern "C" fn wickra_kagi_bars_new(reversal: f64) -> *mut KagiBars { match KagiBars::new(reversal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_kagi_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraKagiBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_kagi_bars_update( handle: *mut KagiBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraKagiBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraKagiBar { start: bar.start, end: bar.end, direction: bar.direction, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_kagi_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kagi_bars_reset(handle: *mut KagiBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_kagi_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_kagi_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_kagi_bars_free(handle: *mut KagiBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `PointAndFigureBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_point_and_figure_bars_free`. #[no_mangle] pub extern "C" fn wickra_point_and_figure_bars_new( box_size: f64, reversal: usize, ) -> *mut PointAndFigureBars { match PointAndFigureBars::new(box_size, reversal) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_point_and_figure_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraPnfColumn` elements. #[no_mangle] pub unsafe extern "C" fn wickra_point_and_figure_bars_update( handle: *mut PointAndFigureBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraPnfColumn, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraPnfColumn { direction: bar.direction, high: bar.high, low: bar.low, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_point_and_figure_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_point_and_figure_bars_reset(handle: *mut PointAndFigureBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_point_and_figure_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_point_and_figure_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_point_and_figure_bars_free(handle: *mut PointAndFigureBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RangeBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_range_bars_free`. #[no_mangle] pub extern "C" fn wickra_range_bars_new(range: f64) -> *mut RangeBars { match RangeBars::new(range) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_range_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraRangeBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_range_bars_update( handle: *mut RangeBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraRangeBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraRangeBar { open: bar.open, close: bar.close, direction: bar.direction, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_range_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_range_bars_reset(handle: *mut RangeBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_range_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_range_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_range_bars_free(handle: *mut RangeBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RenkoBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_renko_bars_free`. #[no_mangle] pub extern "C" fn wickra_renko_bars_new(box_size: f64) -> *mut RenkoBars { match RenkoBars::new(box_size) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_renko_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraRenkoBrick` elements. #[no_mangle] pub unsafe extern "C" fn wickra_renko_bars_update( handle: *mut RenkoBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraRenkoBrick, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraRenkoBrick { open: bar.open, close: bar.close, direction: bar.direction, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_renko_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_renko_bars_reset(handle: *mut RenkoBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_renko_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_renko_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_renko_bars_free(handle: *mut RenkoBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `RunBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_run_bars_free`. #[no_mangle] pub extern "C" fn wickra_run_bars_new(run_length: usize) -> *mut RunBars { match RunBars::new(run_length) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_run_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraRunBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_run_bars_update( handle: *mut RunBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraRunBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraRunBar { open: bar.open, high: bar.high, low: bar.low, close: bar.close, length: bar.length, direction: bar.direction, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_run_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_run_bars_reset(handle: *mut RunBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_run_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_run_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_run_bars_free(handle: *mut RunBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `ThreeLineBreakBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_three_line_break_bars_free`. #[no_mangle] pub extern "C" fn wickra_three_line_break_bars_new(lines: usize) -> *mut ThreeLineBreakBars { match ThreeLineBreakBars::new(lines) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_three_line_break_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraLineBreakBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_break_bars_update( handle: *mut ThreeLineBreakBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraLineBreakBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraLineBreakBar { open: bar.open, close: bar.close, direction: bar.direction, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_three_line_break_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_break_bars_reset(handle: *mut ThreeLineBreakBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_three_line_break_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_three_line_break_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_three_line_break_bars_free(handle: *mut ThreeLineBreakBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `TickBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_tick_bars_free`. #[no_mangle] pub extern "C" fn wickra_tick_bars_new(ticks: usize) -> *mut TickBars { match TickBars::new(ticks) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_tick_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraTickBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_tick_bars_update( handle: *mut TickBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraTickBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraTickBar { open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_tick_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tick_bars_reset(handle: *mut TickBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_tick_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_tick_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_tick_bars_free(handle: *mut TickBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// Create a `VolumeBars` bar builder. /// /// Returns `NULL` on invalid parameters; release with `wickra_volume_bars_free`. #[no_mangle] pub extern "C" fn wickra_volume_bars_new(volume_per_bar: f64) -> *mut VolumeBars { match VolumeBars::new(volume_per_bar) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one OHLCV candle; writes up to `cap` completed bars into `out` and returns the /// total number completed on this candle (0, 1, or several). If the return exceeds `cap`, /// the surplus is not written — enlarge `cap` (a single candle rarely completes many bars; /// `cap >= 64` is safe). Returns 0 on a `NULL` handle or an invalid candle. /// /// # Safety /// `handle` (from `wickra_volume_bars_new`, not freed) and `out` must be valid or `NULL`; when /// non-`NULL`, `out` must cover `cap` `WickraVolumeBar` elements. #[no_mangle] pub unsafe extern "C" fn wickra_volume_bars_update( handle: *mut VolumeBars, open: f64, high: f64, low: f64, close: f64, volume: f64, timestamp: i64, out: *mut WickraVolumeBar, cap: usize, ) -> usize { let Some(builder) = handle.as_mut() else { return 0; }; let Ok(candle) = Candle::new(open, high, low, close, volume, timestamp) else { return 0; }; let bars = builder.update(candle); if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, bar) in slots.iter_mut().zip(&bars) { *slot = WickraVolumeBar { open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, }; } } bars.len() } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_volume_bars_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_bars_reset(handle: *mut VolumeBars) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_volume_bars_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_volume_bars_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_volume_bars_free(handle: *mut VolumeBars) { if !handle.is_null() { drop(Box::from_raw(handle)); } } // ===== Bespoke wirings (MaType-enum ctor, Vec output) ===== /// Moving-average selector for `wickra_macd_ext_new`: 0=SMA, 1=EMA, 2=WMA, 3=DEMA, /// 4=TEMA, 5=TRIMA. Any other value is rejected. fn ma_type_from_u8(value: u8) -> Option { match value { 0 => Some(MaType::Sma), 1 => Some(MaType::Ema), 2 => Some(MaType::Wma), 3 => Some(MaType::Dema), 4 => Some(MaType::Tema), 5 => Some(MaType::Trima), _ => None, } } /// Create a `MacdExt`. `fast_type` / `slow_type` / `signal_type` pick the moving /// average per `ma_type_from_u8`. Returns `NULL` on invalid parameters or an unknown /// MA-type code; release with `wickra_macd_ext_free`. #[no_mangle] pub extern "C" fn wickra_macd_ext_new( fast: usize, fast_type: u8, slow: usize, slow_type: u8, signal: usize, signal_type: u8, ) -> *mut MacdExt { let (Some(ft), Some(st), Some(gt)) = ( ma_type_from_u8(fast_type), ma_type_from_u8(slow_type), ma_type_from_u8(signal_type), ) else { return ptr::null_mut(); }; match MacdExt::new(fast, ft, slow, st, signal, gt) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one value; on `true` the MACD/signal/histogram triple is written to `*out`. /// Returns `false` during warmup, on a `NULL` handle / `out`. /// /// # Safety /// `handle` (from `wickra_macd_ext_new`, not freed) and `out` must be valid or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_ext_update( handle: *mut MacdExt, value: f64, out: *mut WickraMacdOutput, ) -> bool { let Some(ind) = handle.as_mut() else { return false; }; if out.is_null() { return false; } match ind.update(value) { Some(out_val) => { *out = WickraMacdOutput { macd: out_val.macd, signal: out_val.signal, histogram: out_val.histogram, }; true } None => false, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_macd_ext_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_ext_reset(handle: *mut MacdExt) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_macd_ext_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_macd_ext_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_macd_ext_free(handle: *mut MacdExt) { if !handle.is_null() { drop(Box::from_raw(handle)); } } /// C-ABI view of one `FootprintLevel` (price bucket with bid/ask volume). #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct WickraFootprintLevel { pub price: f64, pub bid_vol: f64, pub ask_vol: f64, } /// Create a `Footprint` order-flow aggregator. Returns `NULL` on invalid parameters; /// release with `wickra_footprint_free`. #[no_mangle] pub extern "C" fn wickra_footprint_new(tick_size: f64) -> *mut Footprint { match Footprint::new(tick_size) { Ok(ind) => Box::into_raw(Box::new(ind)), Err(_) => ptr::null_mut(), } } /// Feed one trade. Returns `-1` during warmup / on a `NULL` handle / invalid trade, /// otherwise the number of price levels in the completed footprint; up to `cap` levels /// are written to `out` (enlarge `cap` if the return exceeds it). /// /// # Safety /// `handle` (from `wickra_footprint_new`, not freed) and `out` must be valid or `NULL`; /// when non-`NULL`, `out` must cover `cap` `WickraFootprintLevel` elements. #[no_mangle] pub unsafe extern "C" fn wickra_footprint_update( handle: *mut Footprint, price: f64, size: f64, is_buy: bool, timestamp: i64, out: *mut WickraFootprintLevel, cap: usize, ) -> isize { let Some(ind) = handle.as_mut() else { return -1; }; let side = if is_buy { Side::Buy } else { Side::Sell }; let Ok(trade) = Trade::new(price, size, side, timestamp) else { return -1; }; match ind.update(trade) { Some(out_val) => { if !out.is_null() { let slots = slice::from_raw_parts_mut(out, cap); for (slot, level) in slots.iter_mut().zip(&out_val.levels) { *slot = WickraFootprintLevel { price: level.price, bid_vol: level.bid_vol, ask_vol: level.ask_vol, }; } } isize::try_from(out_val.levels.len()).unwrap_or(isize::MAX) } None => -1, } } /// Reset all internal state. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must be valid (from `wickra_footprint_new`, not freed), or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_footprint_reset(handle: *mut Footprint) { if let Some(ind) = handle.as_mut() { ind.reset(); } } /// Destroy a handle created by `wickra_footprint_new`. No-op if `handle` is `NULL`. /// /// # Safety /// `handle` must have been returned by `wickra_footprint_new` and not previously freed, or `NULL`. #[no_mangle] pub unsafe extern "C" fn wickra_footprint_free(handle: *mut Footprint) { if !handle.is_null() { drop(Box::from_raw(handle)); } } #[cfg(test)] mod tests { use super::*; #[test] fn new_rejects_zero_period() { assert!(wickra_sma_new(0).is_null()); } #[test] fn scalar_streaming_batch_and_lifecycle() { let handle = wickra_sma_new(3); assert!(!handle.is_null()); unsafe { assert!(wickra_sma_update(handle, 1.0).is_nan()); assert!(wickra_sma_update(handle, 2.0).is_nan()); assert!((wickra_sma_update(handle, 3.0) - 2.0).abs() < 1e-9); wickra_sma_reset(handle); let input = [1.0_f64, 2.0, 3.0, 4.0, 5.0]; let mut out = [0.0_f64; 5]; wickra_sma_batch(handle, input.as_ptr(), out.as_mut_ptr(), 5); assert!(out[0].is_nan()); assert!((out[2] - 2.0).abs() < 1e-9); assert!((out[4] - 4.0).abs() < 1e-9); wickra_sma_free(handle); } } #[test] fn null_handle_is_a_defined_noop() { unsafe { assert!(wickra_sma_update(ptr::null_mut(), 1.0).is_nan()); wickra_sma_reset(ptr::null_mut()); wickra_sma_free(ptr::null_mut()); } } }