Deepen Moving Averages family with seven additions (#177)
Deepens the **Moving Averages** family with seven widely-used variants (396 → 403 indicators), the first batch of Part B (family deepening). All are scalar `f64 → f64`: | Indicator | Binding | Notes | |-----------|---------|-------| | `SineWeightedMa` | `SWMA` | symmetric half-cycle sine-weighted window | | `GeometricMa` | `GMA` | rolling geometric mean (log-space average) | | `Ehma` | `EHMA` | exponential Hull MA (Hull construction over EMAs) | | `MedianMa` | `MedianMA` | rolling median, robust to single outliers | | `AdaptiveLaguerreFilter` | `AdaptiveLaguerre` | Ehlers' adaptive Laguerre filter (median-of-normalised-error γ) | | `GeneralizedDema` | `GD` | Tillson's volume-factor double EMA; `v=1` is DEMA, `v=0` is EMA | | `HoltWinters` | `HoltWinters` | Holt's linear double exponential smoothing (level + trend) | LSMA was dropped from the planned set: it already ships as `LinearRegression` (TA-Lib `LINEARREG`, the rolling least-squares endpoint). The five single-period filters use the generated scalar macro bindings; `GeneralizedDema` (period, v) and `HoltWinters` (alpha, beta) use hand-written node/python bindings with the typed wasm macro (precedent `T3` / `Alma`). Full coverage: core modules with per-branch unit tests (100% intent), mod/lib catalogue, FAMILIES group + assert, README + docs counters, CHANGELOG, all three bindings (regenerated `index.d.ts` / `index.js`), fuzz drivers, and the python/node test registries. Local verification: `cargo test -p wickra-core` (lib 3255 + doc 361), `cargo clippy --workspace --all-targets --all-features -D warnings` clean, node `npm run build && npm test` (478), python `pytest` (791).
This commit is contained in:
@@ -197,6 +197,15 @@ node_scalar_indicator!(
|
||||
node_scalar_indicator!(TrendLabelNode, "TrendLabel", wc::TrendLabel);
|
||||
node_scalar_indicator!(WinRateNode, "WinRate", wc::WinRate);
|
||||
node_scalar_indicator!(ExpectancyNode, "Expectancy", wc::Expectancy);
|
||||
node_scalar_indicator!(SineWeightedMaNode, "SWMA", wc::SineWeightedMa);
|
||||
node_scalar_indicator!(GeometricMaNode, "GMA", wc::GeometricMa);
|
||||
node_scalar_indicator!(EhmaNode, "EHMA", wc::Ehma);
|
||||
node_scalar_indicator!(MedianMaNode, "MedianMA", wc::MedianMa);
|
||||
node_scalar_indicator!(
|
||||
AdaptiveLaguerreFilterNode,
|
||||
"AdaptiveLaguerre",
|
||||
wc::AdaptiveLaguerreFilter
|
||||
);
|
||||
#[napi(js_name = "JumpIndicator")]
|
||||
pub struct JumpIndicatorNode {
|
||||
inner: wc::JumpIndicator,
|
||||
@@ -3794,6 +3803,80 @@ impl T3Node {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== GD ==============================
|
||||
|
||||
#[napi(js_name = "GD")]
|
||||
pub struct GeneralizedDemaNode {
|
||||
inner: wc::GeneralizedDema,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl GeneralizedDemaNode {
|
||||
#[napi(constructor)]
|
||||
pub fn new(period: u32, v: f64) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::GeneralizedDema::new(period as usize, v).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
#[napi]
|
||||
pub fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
#[napi]
|
||||
pub fn batch(&mut self, prices: Vec<f64>) -> Vec<f64> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== HoltWinters ==============================
|
||||
|
||||
#[napi(js_name = "HoltWinters")]
|
||||
pub struct HoltWintersNode {
|
||||
inner: wc::HoltWinters,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl HoltWintersNode {
|
||||
#[napi(constructor)]
|
||||
pub fn new(alpha: f64, beta: f64) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::HoltWinters::new(alpha, beta).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
#[napi]
|
||||
pub fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
#[napi]
|
||||
pub fn batch(&mut self, prices: Vec<f64>) -> Vec<f64> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== TSI ==============================
|
||||
|
||||
#[napi(js_name = "TSI")]
|
||||
|
||||
Reference in New Issue
Block a user