shape-runtime 0.2.0

Bytecode compiler, builtins, and runtime infrastructure for Shape
Documentation
/// @module std::finance::indicators::trend
/// Trend Indicators
/// Directional movement and trend strength indicators

from std::finance::indicators::moving_averages use { ema }
from std::finance::indicators::volatility use { atr }
from std::core::utils::rolling use { linear_recurrence, rolling_mean }
from std::core::utils::vector use { select }

// Wilder's Smoothing (Running Moving Average)
fn rma(series, period) {
    let alpha = 1.0 / period;
    let decay = 1.0 - alpha;
    let init = series[0];
    linear_recurrence(series * alpha, decay, init)
}

/// Compute ADX together with `+DI` and `-DI`.
///
/// @see std::finance::indicators::volatility::atr
pub @warmup(period * 3) fn adx(high, low, close, period = 14) {
    let up_move = high - __intrinsic_shift(high, 1);
    let down_move = __intrinsic_shift(low, 1) - low;

    // +DM: if up_move > down_move and up_move > 0, then up_move, else 0
    let up_gt_down = up_move > down_move;
    let up_gt_zero = up_move > 0.0;
    let plus_cond = up_gt_down and up_gt_zero;
    
    let plus_dm = select(plus_cond, up_move, 0.0);

    // -DM: if down_move > up_move and down_move > 0, then down_move, else 0
    let down_gt_up = down_move > up_move;
    let down_gt_zero = down_move > 0.0;
    let minus_cond = down_gt_up and down_gt_zero;
    
    let minus_dm = select(minus_cond, down_move, 0.0);

    // True Range
    let tr = atr(high, low, close, 1);

    // Smooth everything using Wilder's (RMA)
    let tr_smooth = rma(tr, period);
    let plus_dm_smooth = rma(plus_dm, period);
    let minus_dm_smooth = rma(minus_dm, period);

    // Directional Indicators
    let plus_di = 100 * plus_dm_smooth / tr_smooth;
    let minus_di = 100 * minus_dm_smooth / tr_smooth;

    // Directional Index (DX)
    let sum_di = plus_di + minus_di;
    // Avoid division by zero
    let dx = select(sum_di > 0.0, 100 * abs(plus_di - minus_di) / sum_di, 0.0);

    // ADX is smoothed DX
    let adx_val = rma(dx, period);

    {
        adx: adx_val,
        plus_di: plus_di,
        minus_di: minus_di
    }
}

/// Compute the SuperTrend value and trend direction series.
pub @warmup(period) fn super_trend(high, low, close, period = 10, multiplier = 3.0) {
    let atr_val = atr(high, low, close, period);
    let hl2 = (high + low) / 2;
    
    let basic_upper = hl2 + (multiplier * atr_val);
    let basic_lower = hl2 - (multiplier * atr_val);

    // State loop for SuperTrend
    let len = close.length();
    let upper = [];
    let lower = [];
    let trend = []; // 1 for up, -1 for down
    
    // Init arrays
    for i in 0..len {
        upper.push(basic_upper[i]);
        lower.push(basic_lower[i]);
        trend.push(1);
    }
    
    // Iterate to apply logic
    // Start from 1 as we look back
    for i in 1..len {
        // Upper Band Logic
        // If current basic upper < previous final upper OR previous close > previous final upper
        // Then keep basic upper, else previous final upper
        let prev_upper = upper[i-1];
        let prev_close = close[i-1];
        
        if (basic_upper[i] < prev_upper) or (prev_close > prev_upper) {
            upper[i] = basic_upper[i];
        } else {
            upper[i] = prev_upper;
        }
        
        // Lower Band Logic
        let prev_lower = lower[i-1];
        if (basic_lower[i] > prev_lower) or (prev_close < prev_lower) {
            lower[i] = basic_lower[i];
        } else {
            lower[i] = prev_lower;
        }
        
        // Trend Logic
        let prev_trend = trend[i-1];
        if prev_trend == 1 {
            if close[i] <= lower[i] {
                trend[i] = -1;
            } else {
                trend[i] = 1;
            }
        } else {
            if close[i] >= upper[i] {
                trend[i] = 1;
            } else {
                trend[i] = -1;
            }
        }
    }
    
    let result_line = [];
    for i in 0..len {
        if trend[i] == 1 {
            result_line.push(lower[i]);
        } else {
            result_line.push(upper[i]);
        }
    }
    
    {
        trend: trend,
        value: result_line
    }
}

/// Compute the Parabolic SAR series.
pub @warmup(1) fn parabolic_sar(high, low, start = 0.02, inc = 0.02, max_val = 0.2) {
    let len = high.length();
    let sar = [];
    let is_long = true;
    let af = start;
    let ep = high[0]; // Extreme point
    
    // Init SAR
    sar.push(low[0]);
    
    for i in 1..len {
        let prev_sar = sar[i-1];
        let cur_sar = prev_sar + af * (ep - prev_sar);
        
        let cur_high = high[i];
        let cur_low = low[i];
        let prev_high = high[i-1];
        let prev_low = low[i-1];
        
        if is_long {
            // Check switch
            if cur_low < cur_sar {
                is_long = false;
                cur_sar = ep;
                ep = cur_low;
                af = start;
            } else {
                // Adjust SAR (cannot be above prev low or current low)
                if cur_sar > prev_low { cur_sar = prev_low; }
                if cur_sar > cur_low { cur_sar = cur_low; }
                
                // Update EP and AF
                if cur_high > ep {
                    ep = cur_high;
                    af = af + inc;
                    if af > max_val { af = max_val; }
                }
            }
        } else {
            // Check switch
            if cur_high > cur_sar {
                is_long = true;
                cur_sar = ep;
                ep = cur_high;
                af = start;
            } else {
                // Adjust SAR (cannot be below prev high or current high)
                if cur_sar < prev_high { cur_sar = prev_high; }
                if cur_sar < cur_high { cur_sar = cur_high; }
                
                // Update EP and AF
                if cur_low < ep {
                    ep = cur_low;
                    af = af + inc;
                    if af > max_val { af = max_val; }
                }
            }
        }
        
        sar.push(cur_sar);
    }
    
    sar
}