Files
wickra/bindings/node/index.js
T
kingchenc 6287bd48c1 feat: Family 06 Trend-Strength - 5 new directional/random-walk indicators (#44)
* feat(adxr): add Wilder Average Directional Movement Index Rating

ADXR is the trend-strength smoother Wilder published alongside ADX in
*New Concepts in Technical Trading Systems* (1978):

    ADXR_t = (ADX_t + ADX_{t - (period - 1)}) / 2

The lookback length is the same period that feeds the underlying ADX.
Because the older ADX is period - 1 bars stale, ADXR responds more
slowly than ADX and is the canonical metric for comparing
trend-strength across instruments.

Implementation reuses the existing wickra_core::Adx engine plus a
period-length ring of past ADX values; warmup is 3 * period - 1
(41 for period = 14). Bindings: Python PyAdxr (PyArray1 batch),
Node AdxrNode (number scalar), WASM WasmAdxr. Fuzz target covers
the candle-input path. Python + Node streaming-vs-batch tests
parametrised, plus a pure-uptrend reference value (ADXR == 100
when ADX saturates at 100). Criterion bench added under crates/
wickra/benches/indicators.rs.

README family table and indicator counter updated (71 -> 72).

* feat(rwi): add Mike Poulos Random Walk Index

RWI compares actual price displacement to what a random walk would
produce over the same horizon: for each lookback i in [2, period],

    RWI_High_t(i) = (high_t - low_{t-i+1}) / (ATR_i(t) * sqrt(i))
    RWI_Low_t(i)  = (high_{t-i+1} - low_t) / (ATR_i(t) * sqrt(i))

Per-bar output is the maximum across lookbacks for each direction;
a reading > 1 means the trend beats random-walk noise, > 2 is the
typical strong-trend threshold. Multi-output (high, low). period
must be >= 2 (the shortest meaningful lookback); period < 2 returns
InvalidPeriod. Warmup = period (e.g. 14 for the standard default).

Bindings: Python PyRwi (PyArray2 shape (n, 2)), Node RwiNode +
RwiValue struct, WASM WasmRwi (Object/Reflect for update,
Float64Array interleaved for batch). Fuzz target adds the candle
input case. Python parametric streaming-vs-batch test and pure
uptrend reference test (RWI_High dominates RWI_Low and exceeds 1).
Node parametric streaming-vs-interleaved-batch test. Criterion
bench under crates/wickra/benches/indicators.rs.

README family table and indicator counter updated (72 -> 73).

* feat(tii): add M.H. Pee Trend Intensity Index

TII is a [0, 100] oscillator that asks 'what fraction of the recent
SMA deviations are positive?'. The construction is

    dev_t  = close_t - SMA(close, sma_period)_t
    SD_pos = sum of positive dev_t over the last dev_period bars
    SD_neg = sum of |negative dev_t| over the last dev_period bars
    TII    = 100 * SD_pos / (SD_pos + SD_neg)

Saturates at 100 on a pure uptrend (every close above the lagging
SMA), at 0 on a pure downtrend, and returns the neutral mid-point 50
on a perfectly flat window. The output is clamped to [0, 100] as
the rolling-sum subtraction loop can accumulate a few ULP of error
on long histories. Canonical Pee parameters (sma_period=60,
dev_period=30) wired as Python defaults; warmup is
sma_period + dev_period - 1 (89 for the defaults).

Bindings: Python PyTii (PyArray1 batch), Node TiiNode (scalar
update + batch), WASM WasmTii via the two-arg wasm_scalar_indicator!
macro. Fuzz target adds the scalar path. Python parametric
streaming-vs-batch test plus pure-uptrend (TII == 100) and
flat-market (TII == 50) reference tests. Node parametric
streaming-vs-batch test. Criterion bench under crates/wickra/
benches/indicators.rs.

README family table and indicator counter updated (73 -> 74).

* feat(kst): add Pring Know Sure Thing oscillator

KST is Martin Pring's long-horizon momentum gauge: four smoothed
rate-of-change components combined with fixed weights (1, 2, 3, 4),
plus an SMA signal line.

    RCMA_i = SMA(ROC(close, roc_i), sma_i)        for i in 1..=4
    KST    = 1*RCMA_1 + 2*RCMA_2 + 3*RCMA_3 + 4*RCMA_4
    Signal = SMA(KST, signal_period)

Kst::classic() exposes Pring's recommended parameter set
(roc = (10, 15, 20, 30), sma = (10, 10, 10, 15), signal = 9);
warmup = max(roc_i + sma_i) + signal_period - 1 (53 for the classic
parameters). All four parallel branches are fed unconditionally so
they warm in lock-step.

Bindings: Python PyKst (PyArray2 shape (n, 2)) with a KST.classic()
staticmethod, Node KstNode + KstValue with a KST.classic() factory,
WASM WasmKst with both new(...) and classic() constructors plus
Object/Reflect for update and Float64Array for batch. Fuzz target
adds the scalar multi-output path. Python tests gain a new
MULTI_SCALAR section parametric over scalar-input/multi-output
indicators, plus a classic-on-constant-series reference test. Node
tests gain a KST entry in the multi-output section. Criterion
benchmark added under crates/wickra/benches/indicators.rs.

README family table and indicator counter updated (74 -> 75).

* feat(wave-trend): add LazyBear Wave Trend Oscillator

Two-line mean-reverting momentum gauge built from the typical price
and three cascaded EMAs:

    ap   = (high + low + close) / 3
    esa  = EMA(ap, channel_period)
    d    = EMA(|ap - esa|, channel_period)
    ci   = (ap - esa) / (0.015 * d)
    wt1  = EMA(ci, average_period)
    wt2  = SMA(wt1, signal_period)

WaveTrend::classic() exposes LazyBear's defaults
(channel = 10, average = 21, signal = 4); warmup is
2 * channel_period + average_period + signal_period - 3 (42 for the
classic defaults). On a perfectly flat market the SMA-seeded EMA
introduces a single-ULP drift between ap and esa, which on a tiny d
would make the ratio explode to -1/0.015 = -66.67; a price-scaled
flat-tolerance guard (d <= 16 * EPSILON * max(|esa|, 1)) collapses
the channel index to 0 in that regime so both lines remain at zero.

Bindings: Python PyWaveTrend (PyArray2 shape (n, 2)) with a
WaveTrend.classic() staticmethod, Node WaveTrendNode + WaveTrendValue
with a WaveTrend.classic() factory, WASM WasmWaveTrend with both
new(...) and classic() constructors. Fuzz target adds the candle
multi-output path (sorted alphabetically). Python parametric
streaming-vs-batch test plus a flat-market reference test. Node
parametric streaming-vs-interleaved-batch test. Criterion bench
under crates/wickra/benches/indicators.rs.

README family table and indicator counter updated (75 -> 76).

* fix(family-06): re-add KST::classic() factory + drop dup fuzz block

Family-06 PR's tests call ta.KST.classic() / wickra.KST.classic() — main's
KST binding shipped without the static factory. Add classic() in Python
(staticmethod) and Node (napi factory); WASM already had it. Also drop the
duplicate Kst::classic().unwrap() block in fuzz/indicator_update.rs that
the merge left behind (main's API no longer returns Result).

* test(rwi): drop dead count==0 guard

The loop `for i in 2..=period` makes `count = tr_end - tr_start = i - 1`
which is always >= 1, so the `if count == 0 { continue; }` branch was
unreachable defensive code that codecov flagged on the family-06 PR.
2026-05-25 19:00:13 +02:00

428 lines
14 KiB
JavaScript

/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')
const { platform, arch } = process
let nativeBinding = null
let localFileExisted = false
let loadError = null
function isMusl() {
// For Node 10
if (!process.report || typeof process.report.getReport !== 'function') {
try {
const lddPath = require('child_process').execSync('which ldd').toString().trim()
return readFileSync(lddPath, 'utf8').includes('musl')
} catch (e) {
return true
}
} else {
const { glibcVersionRuntime } = process.report.getReport().header
return !glibcVersionRuntime
}
}
switch (platform) {
case 'android':
switch (arch) {
case 'arm64':
localFileExisted = existsSync(join(__dirname, 'wickra.android-arm64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./wickra.android-arm64.node')
} else {
nativeBinding = require('wickra-android-arm64')
}
} catch (e) {
loadError = e
}
break
case 'arm':
localFileExisted = existsSync(join(__dirname, 'wickra.android-arm-eabi.node'))
try {
if (localFileExisted) {
nativeBinding = require('./wickra.android-arm-eabi.node')
} else {
nativeBinding = require('wickra-android-arm-eabi')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Android ${arch}`)
}
break
case 'win32':
switch (arch) {
case 'x64':
localFileExisted = existsSync(
join(__dirname, 'wickra.win32-x64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.win32-x64-msvc.node')
} else {
nativeBinding = require('wickra-win32-x64-msvc')
}
} catch (e) {
loadError = e
}
break
case 'ia32':
localFileExisted = existsSync(
join(__dirname, 'wickra.win32-ia32-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.win32-ia32-msvc.node')
} else {
nativeBinding = require('wickra-win32-ia32-msvc')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'wickra.win32-arm64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.win32-arm64-msvc.node')
} else {
nativeBinding = require('wickra-win32-arm64-msvc')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Windows: ${arch}`)
}
break
case 'darwin':
localFileExisted = existsSync(join(__dirname, 'wickra.darwin-universal.node'))
try {
if (localFileExisted) {
nativeBinding = require('./wickra.darwin-universal.node')
} else {
nativeBinding = require('wickra-darwin-universal')
}
break
} catch {}
switch (arch) {
case 'x64':
localFileExisted = existsSync(join(__dirname, 'wickra.darwin-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./wickra.darwin-x64.node')
} else {
nativeBinding = require('wickra-darwin-x64')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'wickra.darwin-arm64.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.darwin-arm64.node')
} else {
nativeBinding = require('wickra-darwin-arm64')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on macOS: ${arch}`)
}
break
case 'freebsd':
if (arch !== 'x64') {
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
}
localFileExisted = existsSync(join(__dirname, 'wickra.freebsd-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./wickra.freebsd-x64.node')
} else {
nativeBinding = require('wickra-freebsd-x64')
}
} catch (e) {
loadError = e
}
break
case 'linux':
switch (arch) {
case 'x64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-x64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-x64-musl.node')
} else {
nativeBinding = require('wickra-linux-x64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-x64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-x64-gnu.node')
} else {
nativeBinding = require('wickra-linux-x64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-arm64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-arm64-musl.node')
} else {
nativeBinding = require('wickra-linux-arm64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-arm64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-arm64-gnu.node')
} else {
nativeBinding = require('wickra-linux-arm64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-arm-musleabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-arm-musleabihf.node')
} else {
nativeBinding = require('wickra-linux-arm-musleabihf')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-arm-gnueabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-arm-gnueabihf.node')
} else {
nativeBinding = require('wickra-linux-arm-gnueabihf')
}
} catch (e) {
loadError = e
}
}
break
case 'riscv64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-riscv64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-riscv64-musl.node')
} else {
nativeBinding = require('wickra-linux-riscv64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-riscv64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-riscv64-gnu.node')
} else {
nativeBinding = require('wickra-linux-riscv64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 's390x':
localFileExisted = existsSync(
join(__dirname, 'wickra.linux-s390x-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./wickra.linux-s390x-gnu.node')
} else {
nativeBinding = require('wickra-linux-s390x-gnu')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Linux: ${arch}`)
}
break
default:
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
}
if (!nativeBinding) {
if (loadError) {
throw loadError
}
throw new Error(`Failed to load native binding`)
}
const { version, SMA, EMA, WMA, RSI, DEMA, TEMA, HMA, ROC, TRIX, SMMA, TRIMA, ZLEMA, MOM, CMO, DPO, StdDev, UlcerIndex, VerticalHorizontalFilter, ZScore, MACD, BollingerBands, ATR, Stochastic, OBV, ADX, ADXR, CCI, WilliamsR, MFI, PSAR, Keltner, Donchian, VWAP, RollingVWAP, AwesomeOscillator, Aroon, KAMA, RVI, PGO, KST, SMI, LaguerreRSI, ConnorsRSI, Inertia, ALMA, McGinleyDynamic, FRAMA, VIDYA, JMA, Alligator, EVWMA, APO, AwesomeOscillatorHistogram, CFO, ZeroLagMACD, ElderImpulse, STC, T3, TSI, PMO, TII, ADL, VolumePriceTrend, ChaikinMoneyFlow, ChaikinOscillator, ForceIndex, EaseOfMovement, SuperTrend, ChandelierExit, ChandeKrollStop, AtrTrailingStop, TypicalPrice, MedianPrice, WeightedClose, LinearRegression, LinRegSlope, AcceleratorOscillator, BalanceOfPower, ChoppinessIndex, TrueRange, ChaikinVolatility, LinRegAngle, BollingerBandwidth, PercentB, NATR, HistoricalVolatility, AroonOscillator, Vortex, RWI, WaveTrend, MassIndex, StochRSI, UltimateOscillator, PPO, Coppock, VWMA, RVIVolatility, ParkinsonVolatility, GarmanKlassVolatility, RogersSatchellVolatility, YangZhangVolatility, MaEnvelope, AccelerationBands, StarcBands, AtrBands, HurstChannel, LinRegChannel, StandardErrorBands, DoubleBollinger, TtmSqueeze, FractalChaosBands, VwapStdDevBands } = nativeBinding
module.exports.version = version
module.exports.SMA = SMA
module.exports.EMA = EMA
module.exports.WMA = WMA
module.exports.RSI = RSI
module.exports.DEMA = DEMA
module.exports.TEMA = TEMA
module.exports.HMA = HMA
module.exports.ROC = ROC
module.exports.TRIX = TRIX
module.exports.SMMA = SMMA
module.exports.TRIMA = TRIMA
module.exports.ZLEMA = ZLEMA
module.exports.MOM = MOM
module.exports.CMO = CMO
module.exports.DPO = DPO
module.exports.StdDev = StdDev
module.exports.UlcerIndex = UlcerIndex
module.exports.VerticalHorizontalFilter = VerticalHorizontalFilter
module.exports.ZScore = ZScore
module.exports.MACD = MACD
module.exports.BollingerBands = BollingerBands
module.exports.ATR = ATR
module.exports.Stochastic = Stochastic
module.exports.OBV = OBV
module.exports.ADX = ADX
module.exports.ADXR = ADXR
module.exports.CCI = CCI
module.exports.WilliamsR = WilliamsR
module.exports.MFI = MFI
module.exports.PSAR = PSAR
module.exports.Keltner = Keltner
module.exports.Donchian = Donchian
module.exports.VWAP = VWAP
module.exports.RollingVWAP = RollingVWAP
module.exports.AwesomeOscillator = AwesomeOscillator
module.exports.Aroon = Aroon
module.exports.KAMA = KAMA
module.exports.RVI = RVI
module.exports.PGO = PGO
module.exports.KST = KST
module.exports.SMI = SMI
module.exports.LaguerreRSI = LaguerreRSI
module.exports.ConnorsRSI = ConnorsRSI
module.exports.Inertia = Inertia
module.exports.ALMA = ALMA
module.exports.McGinleyDynamic = McGinleyDynamic
module.exports.FRAMA = FRAMA
module.exports.VIDYA = VIDYA
module.exports.JMA = JMA
module.exports.Alligator = Alligator
module.exports.EVWMA = EVWMA
module.exports.APO = APO
module.exports.AwesomeOscillatorHistogram = AwesomeOscillatorHistogram
module.exports.CFO = CFO
module.exports.ZeroLagMACD = ZeroLagMACD
module.exports.ElderImpulse = ElderImpulse
module.exports.STC = STC
module.exports.T3 = T3
module.exports.TSI = TSI
module.exports.PMO = PMO
module.exports.TII = TII
module.exports.ADL = ADL
module.exports.VolumePriceTrend = VolumePriceTrend
module.exports.ChaikinMoneyFlow = ChaikinMoneyFlow
module.exports.ChaikinOscillator = ChaikinOscillator
module.exports.ForceIndex = ForceIndex
module.exports.EaseOfMovement = EaseOfMovement
module.exports.SuperTrend = SuperTrend
module.exports.ChandelierExit = ChandelierExit
module.exports.ChandeKrollStop = ChandeKrollStop
module.exports.AtrTrailingStop = AtrTrailingStop
module.exports.TypicalPrice = TypicalPrice
module.exports.MedianPrice = MedianPrice
module.exports.WeightedClose = WeightedClose
module.exports.LinearRegression = LinearRegression
module.exports.LinRegSlope = LinRegSlope
module.exports.AcceleratorOscillator = AcceleratorOscillator
module.exports.BalanceOfPower = BalanceOfPower
module.exports.ChoppinessIndex = ChoppinessIndex
module.exports.TrueRange = TrueRange
module.exports.ChaikinVolatility = ChaikinVolatility
module.exports.LinRegAngle = LinRegAngle
module.exports.BollingerBandwidth = BollingerBandwidth
module.exports.PercentB = PercentB
module.exports.NATR = NATR
module.exports.HistoricalVolatility = HistoricalVolatility
module.exports.AroonOscillator = AroonOscillator
module.exports.Vortex = Vortex
module.exports.RWI = RWI
module.exports.WaveTrend = WaveTrend
module.exports.MassIndex = MassIndex
module.exports.StochRSI = StochRSI
module.exports.UltimateOscillator = UltimateOscillator
module.exports.PPO = PPO
module.exports.Coppock = Coppock
module.exports.VWMA = VWMA
module.exports.RVIVolatility = RVIVolatility
module.exports.ParkinsonVolatility = ParkinsonVolatility
module.exports.GarmanKlassVolatility = GarmanKlassVolatility
module.exports.RogersSatchellVolatility = RogersSatchellVolatility
module.exports.YangZhangVolatility = YangZhangVolatility
module.exports.MaEnvelope = MaEnvelope
module.exports.AccelerationBands = AccelerationBands
module.exports.StarcBands = StarcBands
module.exports.AtrBands = AtrBands
module.exports.HurstChannel = HurstChannel
module.exports.LinRegChannel = LinRegChannel
module.exports.StandardErrorBands = StandardErrorBands
module.exports.DoubleBollinger = DoubleBollinger
module.exports.TtmSqueeze = TtmSqueeze
module.exports.FractalChaosBands = FractalChaosBands
module.exports.VwapStdDevBands = VwapStdDevBands