// Linear-regression family (TA-Lib "Statistic Functions"): slope, intercept,
// regression endpoint, angle in degrees and one-bar forecast. All five share
// the least-squares core `linreg_at`.
///|
/// Precomputed per-period regression constants: the mean offset `x_bar`, the
/// per-offset deviations `dxa` (x - x_bar for offsets 0..period-1) and the
/// constant denominator `sum(dxa^2)`. All three are bit-identical to the
/// values the per-window computation produced.
priv struct LinregWeights {
x_bar : Double
dxa : FixedArray[Double]
den : Double
}
///|
fn linreg_weights(period : Int32) -> LinregWeights {
let x_bar = (period - 1).to_double() / 2.0
let dxa : FixedArray[Double] = FixedArray::make(period, 0.0)
let mut den = 0.0
for k in 0.. (Double, Double) {
let period = w.dxa.length()
let mut num = 0.0
for j in (i - period + 1)..<(i + 1) {
num += w.dxa[j - i + period - 1] * (data[j] - y_bar)
}
let slope = num / w.den
(slope, y_bar - slope * w.x_bar)
}
///|
/// Least-squares slope over a rolling window. A window shorter than 2 bars
/// cannot define a slope; `period` must be at least 2.
pub fn linearreg_slope(
data : Array[Double],
period : Int32,
) -> Array[Double] raise @base.TaError {
@base.validate_series(data, period)
if period < 2 {
raise @base.InvalidPeriod("period must be at least 2, got \{period}")
}
let w = linreg_weights(period)
let s1 = @base.rolling_sum(data, period)
let pd = period.to_double()
let n = data.length()
let out : Array[Double] = []
for i in 0.. Array[Double] raise @base.TaError {
@base.validate_series(data, period)
if period < 2 {
raise @base.InvalidPeriod("period must be at least 2, got \{period}")
}
let w = linreg_weights(period)
let s1 = @base.rolling_sum(data, period)
let pd = period.to_double()
let n = data.length()
let out : Array[Double] = []
for i in 0.. Int32 {
period - 1
}
///|
/// Linear regression endpoint: the fitted value at the current bar,
/// `intercept + slope * (period - 1)`.
pub fn linearreg(
data : Array[Double],
period : Int32,
) -> Array[Double] raise @base.TaError {
@base.validate_series(data, period)
if period < 2 {
raise @base.InvalidPeriod("period must be at least 2, got \{period}")
}
let w = linreg_weights(period)
let s1 = @base.rolling_sum(data, period)
let pd = period.to_double()
let n = data.length()
let out : Array[Double] = []
for i in 0.. Array[Double] raise @base.TaError {
@base.validate_series(data, period)
if period < 2 {
raise @base.InvalidPeriod("period must be at least 2, got \{period}")
}
let w = linreg_weights(period)
let s1 = @base.rolling_sum(data, period)
let pd = period.to_double()
let n = data.length()
let rad_to_deg = 180.0 / 3.141592653589793
let out : Array[Double] = []
for i in 0.. Array[Double] raise @base.TaError {
@base.validate_series(data, period)
if period < 2 {
raise @base.InvalidPeriod("period must be at least 2, got \{period}")
}
let w = linreg_weights(period)
let s1 = @base.rolling_sum(data, period)
let pd = period.to_double()
let n = data.length()
let out : Array[Double] = []
for i in 0..