feat: add DeMark deepening (B12, 7 indicators) (#204)

B12 of the family-deepening roadmap — seven Tom DeMark indicators (467 -> 474).

**Candle -> +1/0 qualifier patterns (candlestick macro bindings):**
- **TD Camouflage** — hidden intrabar strength/weakness against the prior close.
- **TD Clop** — two-bar open/close engulfing reversal.
- **TD Clopwin** — the inside-body cousin of TD Clop (compression bar).
- **TD Propulsion** — continuation thrust closing beyond the prior extreme.
- **TD Trap** — inside ("trap") bar followed by a range breakout.

**Hand-bound:**
- **TD D-Wave** — streaming Elliott-style 1-5 / A-C swing-wave counter (candle -> f64, `strength` param).
- **TD Moving Averages** — ST1/ST2 median-price trend ribbon (candle -> struct {st1, st2}).

All seven join the existing **DeMark** family. Patterns follow the house-style
+1/0 candle-pattern convention (neutral 0.0 during warmup). Public binding names
use the family-consistent `TD...` casing.

Wiring complete across core, Python, Node, WASM, fuzz, tests, README + docs
counter (474) and CHANGELOG. Verified: core 3874 + doc 427, clippy clean,
node 549, python 903.
This commit is contained in:
kingchenc
2026-06-08 01:12:46 +02:00
committed by GitHub
parent ed01604a18
commit 8431b1400c
21 changed files with 1899 additions and 15 deletions
+123
View File
@@ -11100,6 +11100,124 @@ impl TdComboNode {
}
}
// ============================== TD D-Wave ==============================
#[napi(js_name = "TDDWave")]
pub struct TdDWaveNode {
inner: wc::TdDWave,
}
#[napi]
impl TdDWaveNode {
#[napi(constructor)]
pub fn new(strength: u32) -> napi::Result<Self> {
Ok(Self {
inner: wc::TdDWave::new(strength as usize).map_err(map_err)?,
})
}
#[napi]
pub fn update(&mut self, high: f64, low: f64, close: f64) -> napi::Result<Option<f64>> {
Ok(self.inner.update(cnd(high, low, close, 0.0)?))
}
#[napi]
pub fn batch(
&mut self,
high: Vec<f64>,
low: Vec<f64>,
close: Vec<f64>,
) -> napi::Result<Vec<f64>> {
if high.len() != low.len() || low.len() != close.len() {
return Err(NapiError::from_reason(
"high, low, close must be equal length".to_string(),
));
}
let mut out = Vec::with_capacity(high.len());
for i in 0..high.len() {
out.push(
self.inner
.update(cnd(high[i], low[i], close[i], 0.0)?)
.unwrap_or(f64::NAN),
);
}
Ok(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
}
}
// ============================== TD Moving Averages ==============================
#[napi(object)]
pub struct TdMovingAverageValue {
pub st1: f64,
pub st2: f64,
}
#[napi(js_name = "TDMovingAverage")]
pub struct TdMovingAverageNode {
inner: wc::TdMovingAverage,
}
#[napi]
impl TdMovingAverageNode {
#[napi(constructor)]
pub fn new(period_st1: u32, period_st2: u32) -> napi::Result<Self> {
Ok(Self {
inner: wc::TdMovingAverage::new(period_st1 as usize, period_st2 as usize)
.map_err(map_err)?,
})
}
#[napi]
pub fn update(&mut self, high: f64, low: f64) -> napi::Result<Option<TdMovingAverageValue>> {
Ok(self
.inner
.update(cnd(high, low, low, 0.0)?)
.map(|o| TdMovingAverageValue {
st1: o.st1,
st2: o.st2,
}))
}
#[napi]
pub fn batch(&mut self, high: Vec<f64>, low: Vec<f64>) -> napi::Result<Vec<f64>> {
if high.len() != low.len() {
return Err(NapiError::from_reason(
"high, low must be equal length".to_string(),
));
}
let n = high.len();
let mut out = vec![f64::NAN; n * 2];
for i in 0..n {
if let Some(o) = self.inner.update(cnd(high[i], low[i], low[i], 0.0)?) {
out[i * 2] = o.st1;
out[i * 2 + 1] = o.st2;
}
}
Ok(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
}
}
// ============================== TD Countdown ==============================
#[napi(js_name = "TDCountdown")]
@@ -12807,6 +12925,11 @@ node_candle_pattern!(CrabNode, wc::Crab, "Crab");
node_candle_pattern!(SharkNode, wc::Shark, "Shark");
node_candle_pattern!(CypherNode, wc::Cypher, "Cypher");
node_candle_pattern!(ThreeDrivesNode, wc::ThreeDrives, "ThreeDrives");
node_candle_pattern!(TdCamouflageNode, wc::TdCamouflage, "TDCamouflage");
node_candle_pattern!(TdClopNode, wc::TdClop, "TDClop");
node_candle_pattern!(TdClopwinNode, wc::TdClopwin, "TDClopwin");
node_candle_pattern!(TdPropulsionNode, wc::TdPropulsion, "TDPropulsion");
node_candle_pattern!(TdTrapNode, wc::TdTrap, "TDTrap");
// ============================== Microstructure: Order Book ==============================
//