Add B7 Trailing Stops family (6 indicators) (#193)

Adds the **Trailing Stops** family deepening (B7), six new indicators (434 -> 440):

- **KaseDevStop** — Cynthia Kase's volatility stop on the standard deviation of the two-bar true range.
- **ElderSafeZone** — Alexander Elder's stop offset by a multiple of average market noise.
- **AtrRatchet** — Kaufman ATR ratchet that tightens its multiple by a per-bar increment.
- **Nrtr** — Nick Rypock Trailing Reverse (percentage band).
- **TimeBasedStop** — exits after a fixed number of bars (scalar fraction of elapsed life).
- **ModifiedMaStop** — moving-average based trailing stop.

("Wilder Volatility System" is intentionally skipped — it overlaps the existing VoltyStop/Psar/SarExt.)

Each takes Candle input; the five band/structure stops emit a {value, direction} struct, TimeBasedStop a scalar. Wired across core, Python/Node/WASM bindings, fuzz target and tests. Verified locally: 3560 core lib + 398 doc tests, clippy clean, 515 node tests, 852 pytest, counter 440.
This commit is contained in:
kingchenc
2026-06-07 01:32:15 +02:00
committed by GitHub
parent 83e34c6f71
commit 2991ba411d
20 changed files with 3131 additions and 53 deletions
@@ -356,6 +356,7 @@ const candleScalar = {
Qstick: { make: () => new wickra.Qstick(10), step: (ind, i) => ind.update(open[i], close[i]), batch: (ind) => ind.batch(open, close) },
VolatilityRatio: { make: () => new wickra.VolatilityRatio(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
ProjectionOscillator: { make: () => new wickra.ProjectionOscillator(14), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
TimeBasedStop: { make: () => new wickra.TimeBasedStop(5), step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
};
for (const [name, d] of Object.entries(candleScalar)) {
@@ -447,6 +448,11 @@ const multi = {
BomarBands: { make: () => new wickra.BomarBands(4, 0.85), fields: ['upper', 'middle', 'lower'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
MedianChannel: { make: () => new wickra.MedianChannel(5, 2.0), fields: ['upper', 'middle', 'lower'], step: (ind, i) => ind.update(close[i]), batch: (ind) => ind.batch(close) },
ProjectionBands: { make: () => new wickra.ProjectionBands(3), fields: ['upper', 'middle', 'lower'], step: (ind, i) => ind.update(high[i], low[i]), batch: (ind) => ind.batch(high, low) },
KaseDevStop: { make: () => new wickra.KaseDevStop(3, 1.0), fields: ['value', 'direction'], step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
ElderSafeZone: { make: () => new wickra.ElderSafeZone(14, 2.0), fields: ['value', 'direction'], step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
AtrRatchet: { make: () => new wickra.AtrRatchet(14, 4.0, 0.1), fields: ['value', 'direction'], step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
Nrtr: { make: () => new wickra.Nrtr(2.0), fields: ['value', 'direction'], step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
ModifiedMaStop: { make: () => new wickra.ModifiedMaStop(14), fields: ['value', 'direction'], step: (ind, i) => ind.update(high[i], low[i], close[i]), batch: (ind) => ind.batch(high, low, close) },
};
for (const [name, d] of Object.entries(multi)) {