F3: add MOM, CMO, TSI and PMO momentum indicators
Completes the F3 family (Momentum) end to end: - Rust core: mom.rs (raw price-difference momentum), cmo.rs (Chande Momentum Oscillator — unsmoothed gain/loss sum, bounded [-100,100]), tsi.rs (True Strength Index — double-EMA-smoothed momentum ratio), pmo.rs (DecisionPoint Price Momentum Oscillator — doubly-smoothed ROC with the 2/period custom smoothing). Each with a full Indicator impl, runnable doctest and reference-value / saturation / warmup / reset / batch==streaming / non-finite tests. - Python: PyMom / PyCmo / PyTsi / PyPmo PyO3 classes + module registration + .pyi stubs (defaults MOM=10, CMO=14, TSI=(25,13), PMO=(35,20)). - Node: MomNode / CmoNode via the scalar macro, explicit TsiNode and PmoNode; index.d.ts and index.js updated. - WASM: WasmMom / WasmCmo / WasmTsi / WasmPmo via the scalar macro. - Wiki: Indicator-Mom/Cmo/Tsi/Pmo.md plus rows in Indicators-Overview.md and entries in Home.md. cargo fmt + clippy (core/wickra/data/wasm/node) clean; 262 core tests, 25 data tests and 37 doctests green.
This commit is contained in:
@@ -105,6 +105,8 @@ node_scalar_indicator!(TrixNode, "TRIX", wc::Trix);
|
||||
node_scalar_indicator!(SmmaNode, "SMMA", wc::Smma);
|
||||
node_scalar_indicator!(TrimaNode, "TRIMA", wc::Trima);
|
||||
node_scalar_indicator!(ZlemaNode, "ZLEMA", wc::Zlema);
|
||||
node_scalar_indicator!(MomNode, "MOM", wc::Mom);
|
||||
node_scalar_indicator!(CmoNode, "CMO", wc::Cmo);
|
||||
|
||||
// ============================== MACD ==============================
|
||||
|
||||
@@ -1066,6 +1068,80 @@ impl T3Node {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== TSI ==============================
|
||||
|
||||
#[napi(js_name = "TSI")]
|
||||
pub struct TsiNode {
|
||||
inner: wc::Tsi,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl TsiNode {
|
||||
#[napi(constructor)]
|
||||
pub fn new(long: u32, short: u32) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::Tsi::new(long as usize, short as usize).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
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== PMO ==============================
|
||||
|
||||
#[napi(js_name = "PMO")]
|
||||
pub struct PmoNode {
|
||||
inner: wc::Pmo,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl PmoNode {
|
||||
#[napi(constructor)]
|
||||
pub fn new(smoothing1: u32, smoothing2: u32) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::Pmo::new(smoothing1 as usize, smoothing2 as usize).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
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== VWMA ==============================
|
||||
|
||||
#[napi(js_name = "VWMA")]
|
||||
|
||||
Reference in New Issue
Block a user