quant-indicators 0.7.0

Pure indicator math library for trading — MA, RSI, Bollinger, MACD, ATR, HRP
Documentation
use super::*;
use crate::test_helpers::helpers::{make_candle, ts};
use rust_decimal_macros::dec;

#[test]
fn hull_responds_to_trend() {
    // Uptrend
    let candles: Vec<Candle> = (0..20)
        .map(|i| make_candle(Decimal::from(100 + i), ts(i)))
        .collect();

    let hull = HullMa::new(9).expect("valid HullMA period");
    let series = hull.compute(&candles).expect("sufficient data for HullMA");
    let values = series.decimal_values();

    // Hull should trend upward
    for i in 1..values.len() {
        assert!(
            values[i] >= values[i - 1] - dec!(0.1),
            "Hull should be rising in uptrend"
        );
    }
}

#[test]
fn hull_reacts_to_price_jump() {
    // Price jumps from 100 to 200 mid-series
    let candles: Vec<Candle> = (0..30)
        .map(|i| {
            if i < 15 {
                make_candle(dec!(100), ts(i))
            } else {
                make_candle(dec!(200), ts(i))
            }
        })
        .collect();

    let hull = HullMa::new(9).expect("valid HullMA period");
    let series = hull.compute(&candles).expect("sufficient data for HullMA");
    let values = series.decimal_values();

    // Hull values should transition from ~100 to ~200
    // Early values (before jump) should be near 100
    let early = values.first().expect("non-empty series");
    assert!(
        (*early - dec!(100)).abs() < dec!(10),
        "Early Hull {} should be near 100",
        early
    );

    // Late values (after jump settles) should be near 200
    let late = values.last().expect("non-empty series");
    assert!(
        (*late - dec!(200)).abs() < dec!(20),
        "Late Hull {} should be near 200",
        late
    );
}

#[test]
fn hull_insufficient_data() {
    let candles: Vec<Candle> = (0..5).map(|i| make_candle(dec!(100), ts(i))).collect();

    let hull = HullMa::new(9).expect("valid HullMA period");
    let result = hull.compute(&candles);

    assert!(matches!(
        result,
        Err(IndicatorError::InsufficientData { .. })
    ));
}

#[test]
fn hull_period_too_small() {
    let result = HullMa::new(1);
    assert!(matches!(
        result,
        Err(IndicatorError::InvalidParameter { .. })
    ));
}

#[test]
fn hull_name() {
    let hull = HullMa::new(20).expect("valid HullMA period");
    assert_eq!(hull.name(), "HullMA(20)");
}