#[derive(Debug)]
pub struct KDJ {
pub k: f64,
pub d: f64,
pub j: f64,
}
impl KDJ {
pub fn new(highs: &[f64], lows: &[f64], closes: &[f64], period: usize) -> Vec<Self> {
if highs.len() != lows.len() || lows.len() != closes.len() {
panic!("Highs, lows, and closes must have the same length");
}
let mut kdj_values = Vec::new();
let mut k: f64 = 50.0;
let mut d: f64 = 50.0;
for i in period..highs.len() {
let high = highs[i - period..i]
.iter()
.copied()
.fold(f64::MIN, f64::max);
let low = lows[i - period..i].iter().copied().fold(f64::MAX, f64::min);
let rsv = if high != low {
(closes[i] - low) / (high - low) * 100.0
} else {
50.0 };
k = 2.0 / 3.0 * k + 1.0 / 3.0 * rsv;
d = 2.0 / 3.0 * d + 1.0 / 3.0 * k;
let j = 3.0 * k - 2.0 * d;
kdj_values.push(KDJ { k, d, j });
}
kdj_values
}
}
#[cfg(test)]
mod tests {
use super::KDJ;
#[test]
fn test_kdj_calculation() {
let highs = vec![11.0, 12.0, 13.0, 14.0, 15.0, 16.0];
let lows = vec![10.0, 9.0, 8.0, 7.0, 6.0, 5.0];
let closes = vec![10.5, 11.0, 12.0, 13.0, 14.0, 15.0];
let kdj_values = KDJ::new(&highs, &lows, &closes, 3);
assert_eq!(kdj_values.len(), 3);
let first_kdj = &kdj_values[0];
assert!(first_kdj.k >= 0.0 && first_kdj.k <= 100.0);
assert!(first_kdj.d >= 0.0 && first_kdj.d <= 100.0);
assert!(first_kdj.j >= 0.0 && first_kdj.j <= 100.0);
}
#[test]
#[should_panic]
fn test_panic_on_mismatched_lengths() {
let highs = vec![11.0, 12.0];
let lows = vec![10.0, 9.0];
let closes = vec![10.5];
KDJ::new(&highs, &lows, &closes, 3); }
}