// Rate-of-change family: ROC, MOM, CMO, APO and PPO.
///|
/// Number of leading NaN values produced by `roc` for the given period.
pub fn roc_lookback(period : Int32) -> Int32 {
period
}
///|
/// Rate of change in percent: `100 * (x[i] - x[i - period]) / x[i - period]`.
/// A zero base value yields NaN.
pub fn roc(
data : Array[Double],
period : Int32,
) -> Array[Double] raise @base.TaError {
@base.validate_series(data, period)
let n = data.length()
let out : Array[Double] = []
for i in 0.. Int32 {
period
}
///|
/// Momentum: `x[i] - x[i - period]`. NaN inputs yield NaN.
pub fn mom(
data : Array[Double],
period : Int32,
) -> Array[Double] raise @base.TaError {
@base.validate_series(data, period)
let n = data.length()
let out : Array[Double] = []
for i in 0.. Int32 {
period
}
///|
/// Chande Momentum Oscillator:
/// `100 * (sum_up - sum_down) / (sum_up + sum_down)` over the last `period`
/// price changes. A completely flat window reads 0.
pub fn cmo(
data : Array[Double],
period : Int32,
) -> Array[Double] raise @base.TaError {
@base.validate_series(data, period)
let n = data.length()
// Price changes computed once per bar; each window then only sums.
let diffs : Array[Double] = []
for j in 0.. 0.0 {
up += d
} else {
down -= d
}
}
if !valid {
out.push(@base.nan())
continue
}
let total = up + down
if total == 0.0 {
out.push(0.0)
} else {
out.push(100.0 * (up - down) / total)
}
}
out
}
///|
/// Absolute Price Oscillator: `fast_MA - slow_MA` with a selectable MA flavor.
pub fn apo(
data : Array[Double],
fast_period : Int32,
slow_period : Int32,
ma_type~ : @base.MAType,
) -> Array[Double] raise @base.TaError {
@base.validate_series(data, slow_period)
if fast_period <= 0 || slow_period <= 0 {
raise @base.InvalidPeriod("all periods must be positive")
}
if fast_period >= slow_period {
raise @base.InvalidPeriod("fast_period must be smaller than slow_period")
}
let fast_ma = @overlap.moving_average(data, fast_period, ma_type~)
let slow_ma = @overlap.moving_average(data, slow_period, ma_type~)
let n = data.length()
let out : Array[Double] = []
for i in 0.. Array[Double] raise @base.TaError {
@base.validate_series(data, slow_period)
if fast_period <= 0 || slow_period <= 0 {
raise @base.InvalidPeriod("all periods must be positive")
}
if fast_period >= slow_period {
raise @base.InvalidPeriod("fast_period must be smaller than slow_period")
}
let fast_ma = @overlap.moving_average(data, fast_period, ma_type~)
let slow_ma = @overlap.moving_average(data, slow_period, ma_type~)
let n = data.length()
let out : Array[Double] = []
for i in 0.. Array[Double] raise @base.TaError {
@base.validate_candles(candles)
let n = candles.close.length()
let out : Array[Double] = []
for i in 0..