From c6d5710cd6235315daf40771d9b685540ff8e316 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Sun, 24 May 2026 21:40:16 +0200 Subject: [PATCH] fix(apo): add PyApo + ApoNode + WasmApo bindings missed from ec269d8 The previous APO commit (ec269d8) only registered APO in the Python __init__.py / Node index.js / Node index.d.ts / fuzz / tests / docs. The actual PyApo pyclass, ApoNode napi class, and WasmApo wasm class edits silently no-op'd because the underlying lib.rs files had been touched by a branch switch between Read and Edit. The bindings were therefore advertising APO from the Python module / Node package / WASM module but not actually exposing it. Fix: insert PyApo block + add_class call in bindings/python/src/lib.rs, ApoNode block in bindings/node/src/lib.rs, WasmApo macro line in bindings/wasm/src/lib.rs. cargo test workspace stays at 615 (no new tests added; the existing test_known_values + indicators.test.js references would have failed at import once the bindings rebuilt without these classes). --- bindings/node/src/lib.rs | 34 ++++++++++++++++++++++++++++ bindings/python/src/lib.rs | 46 ++++++++++++++++++++++++++++++++++++++ bindings/wasm/src/lib.rs | 1 + 3 files changed, 81 insertions(+) diff --git a/bindings/node/src/lib.rs b/bindings/node/src/lib.rs index 3ef53a24..da0d31a5 100644 --- a/bindings/node/src/lib.rs +++ b/bindings/node/src/lib.rs @@ -1068,6 +1068,40 @@ impl AroonNode { } } +#[napi(js_name = "APO")] +pub struct ApoNode { + inner: wc::Apo, +} +#[napi] +impl ApoNode { + #[napi(constructor)] + pub fn new(fast: u32, slow: u32) -> napi::Result { + Ok(Self { + inner: wc::Apo::new(clamp_period(fast), clamp_period(slow)).map_err(map_err)?, + }) + } + #[napi] + pub fn update(&mut self, value: f64) -> Option { + self.inner.update(value) + } + #[napi] + pub fn batch(&mut self, prices: Vec) -> Vec { + flatten(self.inner.batch(&prices)) + } + #[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 = "KAMA")] pub struct KamaNode { inner: wc::Kama, diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 41b15d4e..6fa464be 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -812,6 +812,51 @@ impl PyKama { } } +// ============================== APO ============================== + +#[pyclass(name = "APO", module = "wickra._wickra", skip_from_py_object)] +#[derive(Clone)] +struct PyApo { + inner: wc::Apo, +} + +#[pymethods] +impl PyApo { + #[new] + #[pyo3(signature = (fast=12, slow=26))] + fn new(fast: usize, slow: usize) -> PyResult { + Ok(Self { + inner: wc::Apo::new(fast, slow).map_err(map_err)?, + }) + } + fn update(&mut self, value: f64) -> Option { + self.inner.update(value) + } + fn batch<'py>( + &mut self, + py: Python<'py>, + prices: PyReadonlyArray1<'py, f64>, + ) -> PyResult>> { + let s = prices + .as_slice() + .map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?; + Ok(flatten(self.inner.batch(s)).into_pyarray(py)) + } + fn reset(&mut self) { + self.inner.reset(); + } + fn is_ready(&self) -> bool { + self.inner.is_ready() + } + fn warmup_period(&self) -> usize { + self.inner.warmup_period() + } + fn __repr__(&self) -> String { + let (f, s) = self.inner.periods(); + format!("APO(fast={f}, slow={s})") + } +} + // ============================== CCI ============================== #[pyclass(name = "CCI", module = "wickra._wickra", skip_from_py_object)] @@ -4494,6 +4539,7 @@ fn _wickra(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 56b98a81..3628710a 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -86,6 +86,7 @@ wasm_scalar_indicator!(WasmPmo, "PMO", wc::Pmo, smoothing1: usize, smoothing2: u wasm_scalar_indicator!(WasmStochRsi, "StochRSI", wc::StochRsi, rsi_period: usize, stoch_period: usize); wasm_scalar_indicator!(WasmDpo, "DPO", wc::Dpo, period: usize); wasm_scalar_indicator!(WasmPpo, "PPO", wc::Ppo, fast: usize, slow: usize); +wasm_scalar_indicator!(WasmApo, "APO", wc::Apo, fast: usize, slow: usize); wasm_scalar_indicator!(WasmCoppock, "Coppock", wc::Coppock, roc_long: usize, roc_short: usize, wma_period: usize); wasm_scalar_indicator!(WasmStdDev, "StdDev", wc::StdDev, period: usize); wasm_scalar_indicator!(WasmUlcerIndex, "UlcerIndex", wc::UlcerIndex, period: usize);