// Price transforms and rolling utilities (TA-Lib "Statistic Functions" and
// "Price Transform" groups).
///|
/// Applies a per-bar transform of (open, high, low, close); any NaN
/// component maps to NaN.
fn transform(
candles : @base.Candles,
f : (Double, Double, Double, Double) -> Double,
) -> Array[Double] raise @base.TaError {
@base.validate_candles(candles)
let n = candles.close.length()
let out : Array[Double] = []
for i in 0.. Array[Double] raise @base.TaError {
transform(candles, fn(o : Double, h : Double, l : Double, c : Double) {
(o + h + l + c) / 4.0
})
}
///|
/// Median price: `(high + low) / 2`.
pub fn med_price(candles : @base.Candles) -> Array[Double] raise @base.TaError {
transform(candles, fn(o : Double, h : Double, l : Double, c : Double) {
let _o = o
let _c = c
(h + l) / 2.0
})
}
///|
/// Typical price (used by CCI and MFI): `(high + low + close) / 3`.
pub fn typ_price(candles : @base.Candles) -> Array[Double] raise @base.TaError {
transform(candles, fn(o : Double, h : Double, l : Double, c : Double) {
let _o = o
(h + l + c) / 3.0
})
}
///|
/// Weighted close price (TA-Lib `WCLPRICE`): `(high + low + 2 * close) / 4`.
pub fn wcl_price(candles : @base.Candles) -> Array[Double] raise @base.TaError {
transform(candles, fn(o : Double, h : Double, l : Double, c : Double) {
let _o = o
(h + l + 2.0 * c) / 4.0
})
}
///|
/// Number of leading NaN values produced by `max`/`min`/`sum` for the given
/// period.
pub fn window_lookback(period : Int32) -> Int32 {
period - 1
}
///|
/// Rolling maximum over a window of `period` values. A NaN inside the window
/// yields NaN.
pub fn max(
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.. best {
best = v
}
}
if valid {
out.push(best)
} else {
out.push(@base.nan())
}
}
out
}
///|
/// Rolling minimum over a window of `period` values. A NaN inside the window
/// yields NaN.
pub fn min(
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.. Array[Double] raise @base.TaError {
@base.validate_series(data, period)
let n = data.length()
let out : Array[Double] = []
for i in 0.. Int32 {
period - 1
}
///|
/// Rolling minimum and maximum over a window of `period` values, computed in
/// a single pass.
pub fn minmax(
data : Array[Double],
period : Int32,
) -> MinMaxResult raise @base.TaError {
@base.validate_series(data, period)
let n = data.length()
let min : Array[Double] = []
let max : Array[Double] = []
for i in 0.. best_max {
best_max = v
}
}
}
if valid {
min.push(best_min)
max.push(best_max)
} else {
min.push(@base.nan())
max.push(@base.nan())
}
}
{ min, max, }
}
///|
/// Number of leading NaN values produced by `avgdev` for the given period.
pub fn avgdev_lookback(period : Int32) -> Int32 {
period - 1
}
///|
/// Mean absolute deviation over a rolling window:
/// `sum(|x - mean|) / period`. A flat window reads 0; a NaN inside the
/// window yields NaN.
pub fn avgdev(
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..