feat(family-09): add 7 trailing stops (HiLo, Volty, Yo-Yo, Donchian, Pct, Step, Renko) (#46)
* feat(family-09): add 7 trailing stops (HiLo, Volty, Yo-Yo, Donchian, Pct, Step, Renko)
Rounds out the Trailing Stops family from 5 to 12 indicators:
- HiLoActivator (Crabel): SMA-of-high/SMA-of-low trail with a one-bar
lag; emits the opposite-side SMA as the trailing stop.
- VoltyStop (Cynthia Kase): ATR trail anchored on the extreme close
since the trade was opened — tighter than AtrTrailingStop on
pullbacks.
- YoyoExit: long-only ATR trail with an explicit re-entry trigger at
trail + multiplier*ATR; exposes an in_trade flag.
- DonchianStop (Turtle): lowest low / highest high over the window;
multi-output {stop_long, stop_short}.
- PercentageTrailingStop: fixed-percent trail that scales across
instruments without per-asset tuning.
- StepTrailingStop: snaps to a step_size-aligned grid; mirrors
discretionary stop-by-hand workflow.
- RenkoTrailingStop: block-anchored trail; only moves on full-block
advances, ignores intra-block noise.
All seven are wired into wickra-core, the Python / Node / WASM
bindings, the indicator_update + indicator_update_candle fuzz targets,
the wickra bench harness, and the Python + Node test suites. README
counter bumps from 71 to 78; CHANGELOG entry under [Unreleased].
* fix(family-09): satisfy pedantic clippy lints
- hilo_activator: rewrite match-Some/None as if-let-else (single_match_else),
add backticks around the HiLo identifier in module/struct doc (doc_markdown).
- percentage / step / renko trailing stop tests: use f64::from(i32) instead
of `as f64` (cast_lossless).
- bench `benches()` is now >100 lines after Family 09 was wired in; allow
too_many_lines (matches the python pymodule fn).
This commit is contained in:
@@ -18,9 +18,10 @@ use wickra_core::{
|
||||
Alma, Apo, BatchExt, BollingerBands, Cfo, Cmo, ConnorsRsi, Coppock, Dema, DoubleBollinger, Dpo,
|
||||
ElderImpulse, Ema, Frama, HistoricalVolatility, Hma, Indicator, Jma, Kama, Kst, LaguerreRsi,
|
||||
LinRegAngle, LinRegChannel, LinRegSlope, LinearRegression, MaEnvelope, MacdIndicator,
|
||||
McGinleyDynamic, Mom, Pmo, Ppo, Roc, Rsi, RviVolatility, Sma, Smma, StandardErrorBands, Stc,
|
||||
StdDev, StochRsi, T3, Tema, Tii, Trima, Trix, Tsi, UlcerIndex, VerticalHorizontalFilter, Vidya,
|
||||
Wma, ZScore, ZeroLagMacd, Zlema,
|
||||
McGinleyDynamic, Mom, PercentageTrailingStop, Pmo, Ppo, RenkoTrailingStop, Roc, Rsi,
|
||||
RviVolatility, Sma, Smma, StandardErrorBands, Stc, StdDev, StepTrailingStop, StochRsi, T3, Tema,
|
||||
Tii, Trima, Trix, Tsi, UlcerIndex, VerticalHorizontalFilter, Vidya, Wma, ZScore, ZeroLagMacd,
|
||||
Zlema,
|
||||
};
|
||||
|
||||
/// Drive a single streaming + batch run through one scalar indicator. Marked
|
||||
@@ -106,6 +107,11 @@ fuzz_target!(|data: Vec<f64>| {
|
||||
let _ = ZeroLagMacd::classic().batch(&data);
|
||||
}
|
||||
|
||||
// --- Trailing Stops (scalar) ---
|
||||
drive(|| PercentageTrailingStop::new(5.0).unwrap(), &data);
|
||||
drive(|| StepTrailingStop::new(1.0).unwrap(), &data);
|
||||
drive(|| RenkoTrailingStop::new(1.0).unwrap(), &data);
|
||||
|
||||
// MACD and Bollinger Bands have non-`f64` outputs, so they cannot use the
|
||||
// generic `drive` helper above. Streaming + batch are still both exercised.
|
||||
{
|
||||
|
||||
@@ -27,13 +27,13 @@ use wickra_core::{
|
||||
AnchoredVwap, Aroon, AroonOscillator, Atr, AtrBands, AtrTrailingStop, AwesomeOscillator,
|
||||
AwesomeOscillatorHistogram, BalanceOfPower, BatchExt, Candle, Cci, ChaikinMoneyFlow,
|
||||
ChaikinOscillator, ChaikinVolatility, ChandeKrollStop, ChandelierExit, ChoppinessIndex,
|
||||
DemandIndex, Donchian, EaseOfMovement, Evwma, ForceIndex, FractalChaosBands,
|
||||
GarmanKlassVolatility, HurstChannel, Indicator, Inertia, Keltner, Kvo, MarketFacilitationIndex,
|
||||
MassIndex, MedianPrice, Mfi, Natr, Nvi, Obv, ParkinsonVolatility, Pgo, Psar, Pvi,
|
||||
RogersSatchellVolatility, RollingVwap, Rvi, Rwi, Smi, StarcBands, Stochastic, SuperTrend,
|
||||
TrueRange, Tsv, TtmSqueeze, TypicalPrice, UltimateOscillator, VolumeOscillator,
|
||||
VolumePriceTrend, Vortex, Vwap, VwapStdDevBands, Vwma, Vzo, WaveTrend, WeightedClose, WilliamsR,
|
||||
YangZhangVolatility,
|
||||
DemandIndex, Donchian, DonchianStop, EaseOfMovement, Evwma, ForceIndex, FractalChaosBands,
|
||||
GarmanKlassVolatility, HiLoActivator, HurstChannel, Indicator, Inertia, Keltner, Kvo,
|
||||
MarketFacilitationIndex, MassIndex, MedianPrice, Mfi, Natr, Nvi, Obv, ParkinsonVolatility, Pgo,
|
||||
Psar, Pvi, RogersSatchellVolatility, RollingVwap, Rvi, Rwi, Smi, StarcBands, Stochastic,
|
||||
SuperTrend, TrueRange, Tsv, TtmSqueeze, TypicalPrice, UltimateOscillator, VoltyStop,
|
||||
VolumeOscillator, VolumePriceTrend, Vortex, Vwap, VwapStdDevBands, Vwma, Vzo, WaveTrend,
|
||||
WeightedClose, WilliamsR, YangZhangVolatility, YoyoExit,
|
||||
};
|
||||
|
||||
/// Convert a flat `f64` stream into a `Vec<Candle>` by chunking it into
|
||||
@@ -93,6 +93,9 @@ fuzz_target!(|data: Vec<f64>| {
|
||||
drive(|| ChandelierExit::new(22, 3.0).unwrap(), &candles);
|
||||
drive(|| ChandeKrollStop::new(10, 1.0, 9).unwrap(), &candles);
|
||||
drive(|| AtrTrailingStop::new(14, 3.0).unwrap(), &candles);
|
||||
drive(|| HiLoActivator::new(3).unwrap(), &candles);
|
||||
drive(|| VoltyStop::new(14, 2.0).unwrap(), &candles);
|
||||
drive(|| YoyoExit::new(14, 2.0).unwrap(), &candles);
|
||||
|
||||
// --- Trend & Directional ---
|
||||
drive(|| Adx::new(14).unwrap(), &candles);
|
||||
@@ -160,6 +163,15 @@ fuzz_target!(|data: Vec<f64>| {
|
||||
let _ = Stochastic::new(14, 3).unwrap().batch(&candles);
|
||||
}
|
||||
|
||||
// --- Donchian Stop (multi-output) ---
|
||||
{
|
||||
let mut s = DonchianStop::new(10).unwrap();
|
||||
for c in &candles {
|
||||
let _ = s.update(*c);
|
||||
}
|
||||
let _ = DonchianStop::new(10).unwrap().batch(&candles);
|
||||
}
|
||||
|
||||
// --- Family 05: candle-input band/channel indicators (multi-output) ---
|
||||
{
|
||||
let mut ab = AccelerationBands::new(20, 0.001).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user