shape-runtime 0.3.2

Bytecode compiler, builtins, and runtime infrastructure for Shape
Documentation
/// @module std::finance::indicators::volatility
/// Volatility Indicators - Optimized with Intrinsics

from std::core::utils::rolling use { rolling_mean, rolling_std }
from std::finance::indicators::moving_averages use { ema }

/// Compute Bollinger Bands and normalized bandwidth.
///
/// @see std::core::utils::rolling::rolling_mean
/// @see std::core::utils::rolling::rolling_std
pub @warmup(period) fn bollinger_bands(series, period = 20, std_dev = 2.0) {
    // Middle band (SMA) using vector rolling_mean
    let middle = rolling_mean(series, period);

    // Standard deviation using vector rolling_std
    let std = rolling_std(series, period);

    // Upper and lower bands
    let upper = middle + (std_dev * std);
    let lower = middle - (std_dev * std);

    {
        upper: upper,
        middle: middle,
        lower: lower,
        bandwidth: (upper - lower) / middle
    }
}

/// Compute the average true range from high, low, and close series.
///
/// @see std::finance::indicators::moving_averages::ema
pub @warmup(period + 1) fn atr(high, low, close, period = 14) {
    // Calculate true range
    let prev_close = __intrinsic_shift(close, 1);

    let tr1 = high - low;
    let tr2 = abs(high - prev_close);
    let tr3 = abs(low - prev_close);

    // True range is max of the three
    let tr = max(tr1, max(tr2, tr3));

    // ATR is EMA of true range
    // Using standard EMA. Wilder's usually used for ATR.
    // period * 2 - 1 approximates Wilder's.
    let wilder_period = 2 * period - 1;
    ema(tr, wilder_period)
}

/// Compute Keltner Channels around an EMA center line.
///
/// @see std::finance::indicators::volatility::atr
pub @warmup(period + 1) fn keltner_channels(high, low, close, period = 20, atr_mult = 2.0) {
    // Middle line (EMA of close)
    let middle = ema(close, period);

    // ATR for channel width
    let atr_value = atr(high, low, close, period);

    // Upper and lower channels
    let upper = middle + (atr_mult * atr_value);
    let lower = middle - (atr_mult * atr_value);

    {
        upper: upper,
        middle: middle,
        lower: lower
    }
}

/// Compute historical volatility from percentage returns.
pub @warmup(period + 1) fn historical_volatility(series, period = 20, annualize = true) {
    // Calculate returns using pct_change intrinsic
    let returns = __intrinsic_pct_change(series);

    // Rolling std dev of returns
    let vol = rolling_std(returns, period);

    // Annualize if requested (assuming daily data, 252 trading days)
    if annualize {
        vol * sqrt(252)
    } else {
        vol
    }
}

/// Compute Donchian Channels over `period`.
pub @warmup(period) fn donchian_channels(high, low, period = 20) {
    // Upper channel (highest high) - using intrinsic sliding max (O(n))
    let upper = __intrinsic_rolling_max(high, period);

    // Lower channel (lowest low) - using intrinsic sliding min (O(n))
    let lower = __intrinsic_rolling_min(low, period);

    // Middle (average of upper and lower)
    let middle = (upper + lower) / 2;

    {
        upper: upper,
        middle: middle,
        lower: lower
    }
}