diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4118fcb..be7add87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- **TA-Lib parity — Directional Movement components** — the ADX building blocks,
+ previously available only bundled inside `Adx`, as standalone single-output
+ indicators:
+ - `PlusDm` (`PLUS_DM`) — Wilder-smoothed plus directional movement.
+ - `MinusDm` (`MINUS_DM`) — Wilder-smoothed minus directional movement.
+ - `PlusDi` (`PLUS_DI`) — plus directional indicator, `100 · smoothed(+DM) / ATR`.
+ - `MinusDi` (`MINUS_DI`) — minus directional indicator, `100 · smoothed(-DM) / ATR`.
+ - `Dx` (`DX`) — directional movement index, `100 · |+DI − −DI| / (+DI + −DI)`.
+- **TA-Lib parity — price transforms** — window and per-bar price aggregates:
+ - `MidPrice` (`MIDPRICE`) — `(highest high + lowest low) / 2` over a window.
+ - `MidPoint` (`MIDPOINT`) — `(max + min) / 2` of a scalar series over a window.
+ - `AvgPrice` (`AVGPRICE`) — per-bar `(open + high + low + close) / 4`.
+- **TA-Lib parity — rate-of-change variants** — the ratio forms of `Roc`:
+ - `Rocp` (`ROCP`) — `(close − close[period]) / close[period]` (fraction).
+ - `Rocr` (`ROCR`) — `close / close[period]` (ratio).
+ - `Rocr100` (`ROCR100`) — `close / close[period] · 100`.
+- **TA-Lib parity — linear-regression outputs** — the remaining OLS endpoints:
+ - `LinRegIntercept` (`LINEARREG_INTERCEPT`) — the OLS intercept `a`.
+ - `Tsf` (`TSF`) — time series forecast, `a + b·period` (one bar ahead).
+- **TA-Lib parity — `MacdFix` (`MACDFIX`)** — MACD with fast/slow fixed at 12/26
+ and only the signal period configurable; output is the usual `{macd, signal,
+ histogram}` triple.
+- **TA-Lib parity — `SarExt` (`SAREXT`)** — Parabolic SAR with a start value,
+ reversal offset, independent long/short acceleration, and a signed output
+ (positive in long phases, negative in short phases).
+- **TA-Lib parity — `MacdExt` (`MACDEXT`)** — MACD with an independently
+ selectable moving-average type (new `MaType` enum: SMA/EMA/WMA/DEMA/TEMA/TRIMA)
+ for each of the fast, slow and signal lines.
+- **TA-Lib parity — `HtPhasor` (`HT_PHASOR`)** — the in-phase and quadrature
+ components of the Hilbert-transform analytic signal, as a `{inphase,
+ quadrature}` pair.
+- **TA-Lib parity — `HtDcPhase` (`HT_DCPHASE`)** — the phase angle (in degrees)
+ of the Hilbert-transform dominant cycle.
+- **TA-Lib parity — `HtTrendMode` (`HT_TRENDMODE`)** — Ehlers' trend (`1`) vs
+ cycle (`0`) classification from the Hilbert-transform dominant cycle.
+
## [0.4.5] - 2026-06-02
### Added
diff --git a/README.md b/README.md
index 7026647c..370083b4 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
@@ -47,7 +47,7 @@ Full documentation lives at **[docs.wickra.org](https://docs.wickra.org)**:
[Node](https://docs.wickra.org/Quickstart-Node),
[WASM](https://docs.wickra.org/Quickstart-WASM).
- **Indicators** — a per-indicator deep dive (formula, parameters, warmup) for
- every one of the 295 indicators; start at the
+ every one of the 314 indicators; start at the
[indicators overview](https://docs.wickra.org/Indicators-Overview).
- **Reference** — [warmup periods](https://docs.wickra.org/Warmup-Periods),
[streaming vs batch](https://docs.wickra.org/Streaming-vs-Batch),
@@ -135,7 +135,7 @@ python -m benchmarks.compare_libraries
## Indicators
-295 streaming-first indicators across nineteen families. Every one passes the
+314 streaming-first indicators across nineteen families. Every one passes the
`batch == streaming` equivalence test, reference-value tests, and reset
semantics tests. Each has a per-indicator deep dive (formula, parameters,
warmup) at [docs.wickra.org](https://docs.wickra.org/Indicators-Overview).
@@ -239,7 +239,7 @@ A Python live-trading example using the public `websockets` package lives at
```
wickra/
├── crates/
-│ ├── wickra-core/ core engine + all 295 indicators
+│ ├── wickra-core/ core engine + all 314 indicators
│ ├── wickra/ top-level facade crate (publishes on crates.io) + benches/
│ └── wickra-data/ CSV reader, tick aggregator, live exchange feeds
├── bindings/
diff --git a/bindings/node/__tests__/indicators.test.js b/bindings/node/__tests__/indicators.test.js
index b4793866..ae650766 100644
--- a/bindings/node/__tests__/indicators.test.js
+++ b/bindings/node/__tests__/indicators.test.js
@@ -28,6 +28,12 @@ function num(v) {
// --- Scalar indicators: update(value) vs batch(prices) ---
const scalarFactories = {
+ TSF: () => new wickra.TSF(14),
+ LINEARREG_INTERCEPT: () => new wickra.LINEARREG_INTERCEPT(14),
+ ROCR100: () => new wickra.ROCR100(10),
+ ROCR: () => new wickra.ROCR(10),
+ ROCP: () => new wickra.ROCP(10),
+ MIDPOINT: () => new wickra.MIDPOINT(14),
SMA: () => new wickra.SMA(14),
EMA: () => new wickra.EMA(14),
WMA: () => new wickra.WMA(14),
@@ -90,6 +96,8 @@ const scalarFactories = {
EhlersStochastic: () => new wickra.EhlersStochastic(20),
EmpiricalModeDecomposition: () => new wickra.EmpiricalModeDecomposition(20, 0.5),
HilbertDominantCycle: () => new wickra.HilbertDominantCycle(),
+ HT_DCPHASE: () => new wickra.HT_DCPHASE(),
+ HT_TRENDMODE: () => new wickra.HT_TRENDMODE(),
AdaptiveCycle: () => new wickra.AdaptiveCycle(),
SineWave: () => new wickra.SineWave(),
FAMA: () => new wickra.FAMA(0.5, 0.05),
@@ -159,10 +167,17 @@ for (const [name, make] of Object.entries(scalarFactories)) {
// --- Scalar-output candle indicators: update(...) vs batch(...) ---
const candleScalar = {
+ MIDPRICE: { make: () => new wickra.MIDPRICE(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
+ DX: { make: () => new wickra.DX(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
+ MINUS_DI: { make: () => new wickra.MINUS_DI(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
+ PLUS_DI: { make: () => new wickra.PLUS_DI(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
ATR: { make: () => new wickra.ATR(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
+ PLUS_DM: { make: () => new wickra.PLUS_DM(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
+ MINUS_DM: { make: () => new wickra.MINUS_DM(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
CCI: { make: () => new wickra.CCI(20), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
WilliamsR: { make: () => new wickra.WilliamsR(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
PSAR: { make: () => new wickra.PSAR(0.02, 0.02, 0.2), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
+ SAREXT: { make: () => new wickra.SAREXT(0, 0, 0.02, 0.02, 0.2, 0.02, 0.02, 0.2), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
MFI: { make: () => new wickra.MFI(14), step: (ind, i) => ind.update(high[i], low[i], close[i], volume[i]), batch: (ind) => ind.batch(high, low, close, volume) },
VWAP: { make: () => new wickra.VWAP(), step: (ind, i) => ind.update(high[i], low[i], close[i], volume[i]), batch: (ind) => ind.batch(high, low, close, volume) },
RollingVWAP: { make: () => new wickra.RollingVWAP(20), step: (ind, i) => ind.update(high[i], low[i], close[i], volume[i]), batch: (ind) => ind.batch(high, low, close, volume) },
@@ -170,6 +185,7 @@ const candleScalar = {
OBV: { make: () => new wickra.OBV(), step: (ind, i) => ind.update(close[i], volume[i]), batch: (ind) => ind.batch(close, volume) },
VWMA: { make: () => new wickra.VWMA(20), step: (ind, i) => ind.update(close[i], volume[i]), batch: (ind) => ind.batch(close, volume) },
RVI: { make: () => new wickra.RVI(10), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
+ AVGPRICE: { make: () => new wickra.AVGPRICE(), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
Inertia: { make: () => new wickra.Inertia(14, 20), step: (ind, i) => ind.update(open[i], high[i], low[i], close[i]), batch: (ind) => ind.batch(open, high, low, close) },
PGO: { make: () => new wickra.PGO(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
SMI: { make: () => new wickra.SMI(5, 3, 3), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
@@ -302,6 +318,9 @@ const multi = {
Alligator: { make: () => new wickra.Alligator(13, 8, 5), fields: ['jaw', 'teeth', 'lips'], step: (ind, i) => ind.update(high[i], low[i]), batch: (ind) => ind.batch(high, low) },
ZeroLagMACD: { make: () => new wickra.ZeroLagMACD(12, 26, 9), fields: ['macd', 'signal', 'histogram'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
MACD: { make: () => new wickra.MACD(12, 26, 9), fields: ['macd', 'signal', 'histogram'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
+ HT_PHASOR: { make: () => new wickra.HT_PHASOR(), fields: ['inphase', 'quadrature'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
+ MACDFIX: { make: () => new wickra.MACDFIX(9), fields: ['macd', 'signal', 'histogram'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
+ MACDEXT: { make: () => new wickra.MACDEXT(12, 0, 26, 0, 9, 0), fields: ['macd', 'signal', 'histogram'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
KST: { make: () => wickra.KST.classic(), fields: ['kst', 'signal'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
BollingerBands: { make: () => new wickra.BollingerBands(20, 2), fields: ['upper', 'middle', 'lower', 'stddev'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
Stochastic: { make: () => new wickra.Stochastic(14, 3), fields: ['k', 'd'], step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
diff --git a/bindings/node/index.d.ts b/bindings/node/index.d.ts
index 8e18e191..070cf185 100644
--- a/bindings/node/index.d.ts
+++ b/bindings/node/index.d.ts
@@ -45,6 +45,10 @@ export interface BollingerValue {
lower: number
stddev: number
}
+export interface HtPhasorValue {
+ inphase: number
+ quadrature: number
+}
export interface StochValue {
k: number
d: number
@@ -658,6 +662,60 @@ export declare class MedianAbsoluteDeviation {
isReady(): boolean
warmupPeriod(): number
}
+export type MidPointNode = MIDPOINT
+export declare class MIDPOINT {
+ constructor(period: number)
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type RocpNode = ROCP
+export declare class ROCP {
+ constructor(period: number)
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type RocrNode = ROCR
+export declare class ROCR {
+ constructor(period: number)
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type Rocr100Node = ROCR100
+export declare class ROCR100 {
+ constructor(period: number)
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type LinRegInterceptNode = LINEARREG_INTERCEPT
+export declare class LINEARREG_INTERCEPT {
+ constructor(period: number)
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type TsfNode = TSF
+export declare class TSF {
+ constructor(period: number)
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
export type AutocorrelationNode = Autocorrelation
export declare class Autocorrelation {
constructor(period: number, lag: number)
@@ -801,6 +859,36 @@ export declare class MACD {
isReady(): boolean
warmupPeriod(): number
}
+export type MacdFixNode = MACDFIX
+export declare class MACDFIX {
+ constructor(signal: number)
+ update(value: number): MacdValue | null
+ /**
+ * Batch over a price array. Returns a flat array of length `3 * n`,
+ * interleaved per row as `[macd0, signal0, histogram0, macd1, ...]`.
+ */
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type MacdExtNode = MACDEXT
+export declare class MACDEXT {
+ /**
+ * Moving-average types are TA-Lib `MA_Type` codes `0..=5`
+ * (SMA, EMA, WMA, DEMA, TEMA, TRIMA).
+ */
+ constructor(fast: number, fastMatype: number, slow: number, slowMatype: number, signal: number, signalMatype: number)
+ update(value: number): MacdValue | null
+ /**
+ * Batch over a price array. Returns a flat array of length `3 * n`,
+ * interleaved per row as `[macd0, signal0, histogram0, macd1, ...]`.
+ */
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
export type BollingerNode = BollingerBands
export declare class BollingerBands {
constructor(period: number, multiplier: number)
@@ -824,6 +912,91 @@ export declare class ATR {
isReady(): boolean
warmupPeriod(): number
}
+export type PlusDmNode = PLUS_DM
+export declare class PLUS_DM {
+ constructor(period: number)
+ update(high: number, low: number, close: number): number | null
+ batch(high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type MinusDmNode = MINUS_DM
+export declare class MINUS_DM {
+ constructor(period: number)
+ update(high: number, low: number, close: number): number | null
+ batch(high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type PlusDiNode = PLUS_DI
+export declare class PLUS_DI {
+ constructor(period: number)
+ update(high: number, low: number, close: number): number | null
+ batch(high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type MinusDiNode = MINUS_DI
+export declare class MINUS_DI {
+ constructor(period: number)
+ update(high: number, low: number, close: number): number | null
+ batch(high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type DxNode = DX
+export declare class DX {
+ constructor(period: number)
+ update(high: number, low: number, close: number): number | null
+ batch(high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type MidPriceNode = MIDPRICE
+export declare class MIDPRICE {
+ constructor(period: number)
+ update(high: number, low: number, close: number): number | null
+ batch(high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type AvgPriceNode = AVGPRICE
+export declare class AVGPRICE {
+ constructor()
+ update(open: number, high: number, low: number, close: number): number | null
+ batch(open: Array, high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type SarExtNode = SAREXT
+export declare class SAREXT {
+ constructor(startValue: number, offsetOnReverse: number, accelInitLong: number, accelLong: number, accelMaxLong: number, accelInitShort: number, accelShort: number, accelMaxShort: number)
+ update(high: number, low: number, close: number): number | null
+ batch(high: Array, low: Array, close: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type HtPhasorNode = HT_PHASOR
+export declare class HT_PHASOR {
+ constructor()
+ update(value: number): HtPhasorValue | null
+ /**
+ * Batch over a price array. Returns a flat array of length `2 * n`,
+ * interleaved per row as `[inphase0, quadrature0, inphase1, ...]`.
+ */
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
export type StochNode = Stochastic
export declare class Stochastic {
constructor(kPeriod: number, dPeriod: number)
@@ -2010,6 +2183,24 @@ export declare class EmpiricalModeDecomposition {
isReady(): boolean
warmupPeriod(): number
}
+export type HtDcPhaseNode = HT_DCPHASE
+export declare class HT_DCPHASE {
+ constructor()
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
+export type HtTrendModeNode = HT_TRENDMODE
+export declare class HT_TRENDMODE {
+ constructor()
+ update(value: number): number | null
+ batch(prices: Array): Array
+ reset(): void
+ isReady(): boolean
+ warmupPeriod(): number
+}
export type HilbertDominantCycleNode = HilbertDominantCycle
export declare class HilbertDominantCycle {
constructor()
diff --git a/bindings/node/index.js b/bindings/node/index.js
index b8027d23..bd79395c 100644
--- a/bindings/node/index.js
+++ b/bindings/node/index.js
@@ -310,7 +310,7 @@ if (!nativeBinding) {
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, McGinleyDynamic, FRAMA, SuperSmoother, FisherTransform, Decycler, CenterOfGravity, CyberneticCycle, InstantaneousTrendline, EhlersStochastic, RVIVolatility, Variance, CoefficientOfVariation, Skewness, Kurtosis, StandardError, DetrendedStdDev, RSquared, MedianAbsoluteDeviation, Autocorrelation, HurstExponent, PearsonCorrelation, Beta, PairwiseBeta, SpearmanCorrelation, PairSpreadZScore, LeadLagCrossCorrelation, Cointegration, RelativeStrengthAB, MACD, BollingerBands, ATR, Stochastic, OBV, ADX, ADXR, CCI, WilliamsR, MFI, PSAR, Keltner, Donchian, VWAP, RollingVWAP, AwesomeOscillator, Aroon, Inertia, ConnorsRSI, LaguerreRSI, SMI, KST, PGO, RVI, AwesomeOscillatorHistogram, STC, ElderImpulse, ZeroLagMACD, CFO, APO, KAMA, EVWMA, Alligator, JMA, VIDYA, ALMA, T3, TSI, PMO, TII, ADL, VolumePriceTrend, ChaikinMoneyFlow, ChaikinOscillator, ForceIndex, NVI, PVI, VolumeOscillator, KVO, WilliamsAD, AnchoredRSI, AnchoredVWAP, DemandIndex, TSV, VZO, MarketFacilitationIndex, EaseOfMovement, SuperTrend, ChandelierExit, ChandeKrollStop, AtrTrailingStop, HiLoActivator, VoltyStop, YoyoExit, DonchianStop, PercentageTrailingStop, StepTrailingStop, RenkoTrailingStop, TypicalPrice, MedianPrice, WeightedClose, LinearRegression, LinRegSlope, AcceleratorOscillator, BalanceOfPower, ChoppinessIndex, TrueRange, ChaikinVolatility, YangZhangVolatility, RogersSatchellVolatility, GarmanKlassVolatility, ParkinsonVolatility, LinRegAngle, BollingerBandwidth, PercentB, NATR, HistoricalVolatility, AroonOscillator, WaveTrend, RWI, Vortex, MassIndex, StochRSI, UltimateOscillator, PPO, Coppock, VWMA, MaEnvelope, AccelerationBands, StarcBands, AtrBands, HurstChannel, LinRegChannel, StandardErrorBands, DoubleBollinger, TtmSqueeze, FractalChaosBands, VwapStdDevBands, ClassicPivots, FibonacciPivots, Camarilla, WoodiePivots, DemarkPivots, WilliamsFractals, ZigZag, TDSetup, TDSequential, TDDeMarker, TDREI, TDPressure, TDCombo, TDCountdown, TDLines, TDRangeProjection, TDDifferential, TDOpen, TDRiskLevel, InverseFisherTransform, DecyclerOscillator, RoofingFilter, EmpiricalModeDecomposition, HilbertDominantCycle, AdaptiveCycle, SineWave, MAMA, FAMA, Ichimoku, HeikinAshi, ValueArea, VolumeProfile, TpoProfile, InitialBalance, OpeningRange, Doji, Hammer, InvertedHammer, HangingMan, ShootingStar, Engulfing, Harami, MorningEveningStar, ThreeSoldiersOrCrows, PiercingDarkCloud, Marubozu, Tweezer, SpinningTop, ThreeInside, ThreeOutside, TwoCrows, UpsideGapTwoCrows, IdenticalThreeCrows, ThreeLineStrike, ThreeStarsInSouth, AbandonedBaby, AdvanceBlock, BeltHold, Breakaway, Counterattack, DojiStar, DragonflyDoji, GravestoneDoji, LongLeggedDoji, RickshawMan, EveningDojiStar, MorningDojiStar, GapSideBySideWhite, HighWave, Hikkake, HikkakeModified, HomingPigeon, OnNeck, InNeck, Thrusting, SeparatingLines, Kicking, KickingByLength, LadderBottom, MatHold, MatchingLow, LongLine, ShortLine, RisingThreeMethods, FallingThreeMethods, UpsideGapThreeMethods, DownsideGapThreeMethods, StalledPattern, StickSandwich, Takuri, ClosingMarubozu, OpeningMarubozu, TasukiGap, UniqueThreeRiver, ConcealingBabySwallow, OrderBookImbalanceTop1, OrderBookImbalanceFull, Microprice, QuotedSpread, DepthSlope, OrderBookImbalanceTopN, SignedVolume, CumulativeVolumeDelta, TradeImbalance, EffectiveSpread, RealizedSpread, KylesLambda, Footprint, FundingRate, FundingRateMean, FundingRateZScore, FundingBasis, OpenInterestDelta, OIPriceDivergence, OIWeighted, LongShortRatio, TakerBuySellRatio, LiquidationFeatures, TermStructureBasis, CalendarSpread, SharpeRatio, SortinoRatio, CalmarRatio, OmegaRatio, MaxDrawdown, AverageDrawdown, DrawdownDuration, PainIndex, ValueAtRisk, ConditionalValueAtRisk, ProfitFactor, GainLossRatio, RecoveryFactor, KellyCriterion, TreynorRatio, InformationRatio, RenkoBars, KagiBars, PointAndFigureBars, Alpha } = nativeBinding
+const { version, SMA, EMA, WMA, RSI, DEMA, TEMA, HMA, ROC, TRIX, SMMA, TRIMA, ZLEMA, MOM, CMO, DPO, StdDev, UlcerIndex, VerticalHorizontalFilter, ZScore, McGinleyDynamic, FRAMA, SuperSmoother, FisherTransform, Decycler, CenterOfGravity, CyberneticCycle, InstantaneousTrendline, EhlersStochastic, RVIVolatility, Variance, CoefficientOfVariation, Skewness, Kurtosis, StandardError, DetrendedStdDev, RSquared, MedianAbsoluteDeviation, MIDPOINT, ROCP, ROCR, ROCR100, LINEARREG_INTERCEPT, TSF, Autocorrelation, HurstExponent, PearsonCorrelation, Beta, PairwiseBeta, SpearmanCorrelation, PairSpreadZScore, LeadLagCrossCorrelation, Cointegration, RelativeStrengthAB, MACD, MACDFIX, MACDEXT, BollingerBands, ATR, PLUS_DM, MINUS_DM, PLUS_DI, MINUS_DI, DX, MIDPRICE, AVGPRICE, SAREXT, HT_PHASOR, Stochastic, OBV, ADX, ADXR, CCI, WilliamsR, MFI, PSAR, Keltner, Donchian, VWAP, RollingVWAP, AwesomeOscillator, Aroon, Inertia, ConnorsRSI, LaguerreRSI, SMI, KST, PGO, RVI, AwesomeOscillatorHistogram, STC, ElderImpulse, ZeroLagMACD, CFO, APO, KAMA, EVWMA, Alligator, JMA, VIDYA, ALMA, T3, TSI, PMO, TII, ADL, VolumePriceTrend, ChaikinMoneyFlow, ChaikinOscillator, ForceIndex, NVI, PVI, VolumeOscillator, KVO, WilliamsAD, AnchoredRSI, AnchoredVWAP, DemandIndex, TSV, VZO, MarketFacilitationIndex, EaseOfMovement, SuperTrend, ChandelierExit, ChandeKrollStop, AtrTrailingStop, HiLoActivator, VoltyStop, YoyoExit, DonchianStop, PercentageTrailingStop, StepTrailingStop, RenkoTrailingStop, TypicalPrice, MedianPrice, WeightedClose, LinearRegression, LinRegSlope, AcceleratorOscillator, BalanceOfPower, ChoppinessIndex, TrueRange, ChaikinVolatility, YangZhangVolatility, RogersSatchellVolatility, GarmanKlassVolatility, ParkinsonVolatility, LinRegAngle, BollingerBandwidth, PercentB, NATR, HistoricalVolatility, AroonOscillator, WaveTrend, RWI, Vortex, MassIndex, StochRSI, UltimateOscillator, PPO, Coppock, VWMA, MaEnvelope, AccelerationBands, StarcBands, AtrBands, HurstChannel, LinRegChannel, StandardErrorBands, DoubleBollinger, TtmSqueeze, FractalChaosBands, VwapStdDevBands, ClassicPivots, FibonacciPivots, Camarilla, WoodiePivots, DemarkPivots, WilliamsFractals, ZigZag, TDSetup, TDSequential, TDDeMarker, TDREI, TDPressure, TDCombo, TDCountdown, TDLines, TDRangeProjection, TDDifferential, TDOpen, TDRiskLevel, InverseFisherTransform, DecyclerOscillator, RoofingFilter, EmpiricalModeDecomposition, HT_DCPHASE, HT_TRENDMODE, HilbertDominantCycle, AdaptiveCycle, SineWave, MAMA, FAMA, Ichimoku, HeikinAshi, ValueArea, VolumeProfile, TpoProfile, InitialBalance, OpeningRange, Doji, Hammer, InvertedHammer, HangingMan, ShootingStar, Engulfing, Harami, MorningEveningStar, ThreeSoldiersOrCrows, PiercingDarkCloud, Marubozu, Tweezer, SpinningTop, ThreeInside, ThreeOutside, TwoCrows, UpsideGapTwoCrows, IdenticalThreeCrows, ThreeLineStrike, ThreeStarsInSouth, AbandonedBaby, AdvanceBlock, BeltHold, Breakaway, Counterattack, DojiStar, DragonflyDoji, GravestoneDoji, LongLeggedDoji, RickshawMan, EveningDojiStar, MorningDojiStar, GapSideBySideWhite, HighWave, Hikkake, HikkakeModified, HomingPigeon, OnNeck, InNeck, Thrusting, SeparatingLines, Kicking, KickingByLength, LadderBottom, MatHold, MatchingLow, LongLine, ShortLine, RisingThreeMethods, FallingThreeMethods, UpsideGapThreeMethods, DownsideGapThreeMethods, StalledPattern, StickSandwich, Takuri, ClosingMarubozu, OpeningMarubozu, TasukiGap, UniqueThreeRiver, ConcealingBabySwallow, OrderBookImbalanceTop1, OrderBookImbalanceFull, Microprice, QuotedSpread, DepthSlope, OrderBookImbalanceTopN, SignedVolume, CumulativeVolumeDelta, TradeImbalance, EffectiveSpread, RealizedSpread, KylesLambda, Footprint, FundingRate, FundingRateMean, FundingRateZScore, FundingBasis, OpenInterestDelta, OIPriceDivergence, OIWeighted, LongShortRatio, TakerBuySellRatio, LiquidationFeatures, TermStructureBasis, CalendarSpread, SharpeRatio, SortinoRatio, CalmarRatio, OmegaRatio, MaxDrawdown, AverageDrawdown, DrawdownDuration, PainIndex, ValueAtRisk, ConditionalValueAtRisk, ProfitFactor, GainLossRatio, RecoveryFactor, KellyCriterion, TreynorRatio, InformationRatio, RenkoBars, KagiBars, PointAndFigureBars, Alpha } = nativeBinding
module.exports.version = version
module.exports.SMA = SMA
@@ -350,6 +350,12 @@ module.exports.StandardError = StandardError
module.exports.DetrendedStdDev = DetrendedStdDev
module.exports.RSquared = RSquared
module.exports.MedianAbsoluteDeviation = MedianAbsoluteDeviation
+module.exports.MIDPOINT = MIDPOINT
+module.exports.ROCP = ROCP
+module.exports.ROCR = ROCR
+module.exports.ROCR100 = ROCR100
+module.exports.LINEARREG_INTERCEPT = LINEARREG_INTERCEPT
+module.exports.TSF = TSF
module.exports.Autocorrelation = Autocorrelation
module.exports.HurstExponent = HurstExponent
module.exports.PearsonCorrelation = PearsonCorrelation
@@ -361,8 +367,19 @@ module.exports.LeadLagCrossCorrelation = LeadLagCrossCorrelation
module.exports.Cointegration = Cointegration
module.exports.RelativeStrengthAB = RelativeStrengthAB
module.exports.MACD = MACD
+module.exports.MACDFIX = MACDFIX
+module.exports.MACDEXT = MACDEXT
module.exports.BollingerBands = BollingerBands
module.exports.ATR = ATR
+module.exports.PLUS_DM = PLUS_DM
+module.exports.MINUS_DM = MINUS_DM
+module.exports.PLUS_DI = PLUS_DI
+module.exports.MINUS_DI = MINUS_DI
+module.exports.DX = DX
+module.exports.MIDPRICE = MIDPRICE
+module.exports.AVGPRICE = AVGPRICE
+module.exports.SAREXT = SAREXT
+module.exports.HT_PHASOR = HT_PHASOR
module.exports.Stochastic = Stochastic
module.exports.OBV = OBV
module.exports.ADX = ADX
@@ -491,6 +508,8 @@ module.exports.InverseFisherTransform = InverseFisherTransform
module.exports.DecyclerOscillator = DecyclerOscillator
module.exports.RoofingFilter = RoofingFilter
module.exports.EmpiricalModeDecomposition = EmpiricalModeDecomposition
+module.exports.HT_DCPHASE = HT_DCPHASE
+module.exports.HT_TRENDMODE = HT_TRENDMODE
module.exports.HilbertDominantCycle = HilbertDominantCycle
module.exports.AdaptiveCycle = AdaptiveCycle
module.exports.SineWave = SineWave
diff --git a/bindings/node/src/lib.rs b/bindings/node/src/lib.rs
index b5c191b1..b7281bed 100644
--- a/bindings/node/src/lib.rs
+++ b/bindings/node/src/lib.rs
@@ -172,6 +172,16 @@ node_scalar_indicator!(
"MedianAbsoluteDeviation",
wc::MedianAbsoluteDeviation
);
+node_scalar_indicator!(MidPointNode, "MIDPOINT", wc::MidPoint);
+node_scalar_indicator!(RocpNode, "ROCP", wc::Rocp);
+node_scalar_indicator!(RocrNode, "ROCR", wc::Rocr);
+node_scalar_indicator!(Rocr100Node, "ROCR100", wc::Rocr100);
+node_scalar_indicator!(
+ LinRegInterceptNode,
+ "LINEARREG_INTERCEPT",
+ wc::LinRegIntercept
+);
+node_scalar_indicator!(TsfNode, "TSF", wc::Tsf);
// ============================== Autocorrelation (period + lag) ==============================
@@ -632,6 +642,121 @@ impl MacdNode {
}
}
+#[napi(js_name = "MACDFIX")]
+pub struct MacdFixNode {
+ inner: wc::MacdFix,
+}
+
+#[napi]
+impl MacdFixNode {
+ #[napi(constructor)]
+ pub fn new(signal: u32) -> napi::Result {
+ Ok(Self {
+ inner: wc::MacdFix::new(signal as usize).map_err(map_err)?,
+ })
+ }
+ #[napi]
+ pub fn update(&mut self, value: f64) -> Option {
+ self.inner.update(value).map(|o| MacdValue {
+ macd: o.macd,
+ signal: o.signal,
+ histogram: o.histogram,
+ })
+ }
+ /// Batch over a price array. Returns a flat array of length `3 * n`,
+ /// interleaved per row as `[macd0, signal0, histogram0, macd1, ...]`.
+ #[napi]
+ pub fn batch(&mut self, prices: Vec) -> Vec {
+ let mut out = vec![f64::NAN; prices.len() * 3];
+ for (i, p) in prices.iter().enumerate() {
+ if let Some(o) = self.inner.update(*p) {
+ out[i * 3] = o.macd;
+ out[i * 3 + 1] = o.signal;
+ out[i * 3 + 2] = o.histogram;
+ }
+ }
+ out
+ }
+ #[napi]
+ pub fn reset(&mut self) {
+ self.inner.reset();
+ }
+ #[napi(js_name = "isReady")]
+ pub fn is_ready(&self) -> bool {
+ self.inner.is_ready()
+ }
+ #[napi(js_name = "warmupPeriod")]
+ pub fn warmup_period(&self) -> u32 {
+ self.inner.warmup_period() as u32
+ }
+}
+
+#[napi(js_name = "MACDEXT")]
+pub struct MacdExtNode {
+ inner: wc::MacdExt,
+}
+
+#[napi]
+impl MacdExtNode {
+ /// Moving-average types are TA-Lib `MA_Type` codes `0..=5`
+ /// (SMA, EMA, WMA, DEMA, TEMA, TRIMA).
+ #[napi(constructor)]
+ pub fn new(
+ fast: u32,
+ fast_matype: u32,
+ slow: u32,
+ slow_matype: u32,
+ signal: u32,
+ signal_matype: u32,
+ ) -> napi::Result {
+ Ok(Self {
+ inner: wc::MacdExt::new(
+ fast as usize,
+ wc::MaType::from_code(fast_matype).map_err(map_err)?,
+ slow as usize,
+ wc::MaType::from_code(slow_matype).map_err(map_err)?,
+ signal as usize,
+ wc::MaType::from_code(signal_matype).map_err(map_err)?,
+ )
+ .map_err(map_err)?,
+ })
+ }
+ #[napi]
+ pub fn update(&mut self, value: f64) -> Option {
+ self.inner.update(value).map(|o| MacdValue {
+ macd: o.macd,
+ signal: o.signal,
+ histogram: o.histogram,
+ })
+ }
+ /// Batch over a price array. Returns a flat array of length `3 * n`,
+ /// interleaved per row as `[macd0, signal0, histogram0, macd1, ...]`.
+ #[napi]
+ pub fn batch(&mut self, prices: Vec) -> Vec {
+ let mut out = vec![f64::NAN; prices.len() * 3];
+ for (i, p) in prices.iter().enumerate() {
+ if let Some(o) = self.inner.update(*p) {
+ out[i * 3] = o.macd;
+ out[i * 3 + 1] = o.signal;
+ out[i * 3 + 2] = o.histogram;
+ }
+ }
+ out
+ }
+ #[napi]
+ pub fn reset(&mut self) {
+ self.inner.reset();
+ }
+ #[napi(js_name = "isReady")]
+ pub fn is_ready(&self) -> bool {
+ self.inner.is_ready()
+ }
+ #[napi(js_name = "warmupPeriod")]
+ pub fn warmup_period(&self) -> u32 {
+ self.inner.warmup_period() as u32
+ }
+}
+
// ============================== Bollinger ==============================
#[napi(object)]
@@ -753,6 +878,522 @@ impl AtrNode {
}
}
+#[napi(js_name = "PLUS_DM")]
+pub struct PlusDmNode {
+ inner: wc::PlusDm,
+}
+
+#[napi]
+impl PlusDmNode {
+ #[napi(constructor)]
+ pub fn new(period: u32) -> napi::Result {
+ Ok(Self {
+ inner: wc::PlusDm::new(period as usize).map_err(map_err)?,
+ })
+ }
+ #[napi]
+ pub fn update(&mut self, high: f64, low: f64, close: f64) -> napi::Result