12681e4b1b
Stacked on #222 (base `feat/c-abi-hub`), so the diff is just the additions on top of the hub foundation — no merge of #222 required. ## What this adds **Examples — full parity with rust/python/node (`examples/c/`)** - `streaming.c` upgraded to the multi-indicator (SMA/EMA/RSI/MACD + signals) demo - `backtest.c`, `multi_timeframe.c` (manual time-bucket resampling), `parallel_assets.c` (serial vs OpenMP fan-out, one handle per asset) - three educational strategies: `strategy_rsi_mean_reversion.c`, `strategy_macd_adx.c`, `strategy_bollinger_squeeze.c` - two network examples shelling out to `curl`: `fetch_btcusdt.c`, `live_binance.c` (REST poll) - two header-only helpers (`wickra_csv.h`, `wickra_strategy.h`) since the C ABI ships no IO layer - CMake builds all 11; the 9 offline ones run under `ctest` on 3 OS; the network two are built-only **Docs & metadata — surface the C ABI everywhere it was missing** - ARCHITECTURE diagram + crate table, SECURITY + THREAT_MODEL (the C ABI as the sole `unsafe` FFI surface), the three binding package READMEs, issue/PR templates, CHANGELOG, and the GitHub About template (live About + org description updated too) **Cleanup** - removed all references to the private generator tooling from public files (`bindings/c/src/lib.rs` header, `CONTRIBUTING.md`, `sync-about.yml`) Verified locally: `cargo build -p wickra-c --release`, `cmake + ctest` (9/9 pass), and `-Wall -Wextra -Wpedantic` clean on gcc 13.
44291 lines
1.3 MiB
Plaintext
44291 lines
1.3 MiB
Plaintext
//! 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_<ind>_new(...)` — construct; returns `NULL` on invalid parameters.
|
|
//! - `wickra_<ind>_update(h, ...)` — feed one point; returns the output, or `NaN`
|
|
//! while warming up / if `h` is `NULL` / if the inputs are invalid.
|
|
//! - `wickra_<ind>_batch(h, ..., out, n)` — write one output per input into the
|
|
//! caller-owned `out` buffer (length `n`), `NaN` at warmup positions.
|
|
//! - `wickra_<ind>_reset(h)` — clear all state.
|
|
//! - `wickra_<ind>_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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>` 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<f64>-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<struct> 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<MaType> {
|
|
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());
|
|
}
|
|
}
|
|
}
|