pub fn sma(data: &[f64], period: usize) -> Vec<Option<f64>> {
if data.is_empty() {
return vec![];
}
if period < 2 {
return vec![None; data.len()];
}
let mut result = Vec::with_capacity(data.len());
if data.len() < period {
return vec![None; data.len()];
}
for _ in 0..period - 1 {
result.push(None);
}
for window in data.windows(period) {
let sum: f64 = window.iter().sum();
let avg = sum / period as f64;
result.push(Some(avg));
}
result
}
pub fn ema(data: &[f64], period: usize) -> Vec<f64> {
if data.is_empty() {
return vec![];
}
if period < 1 {
return vec![0.0; data.len()];
}
let multiplier = 2.0 / (period as f64 + 1.0);
let mut emas = Vec::with_capacity(data.len());
let mut prev_ema = data[0];
emas.push(prev_ema);
for &price in &data[1..] {
let ema = (price - prev_ema) * multiplier + prev_ema;
emas.push(ema);
prev_ema = ema;
}
emas
}
#[derive(Debug, Clone)]
pub struct MacdResult {
pub macd: Vec<Option<f64>>,
pub signal: Vec<Option<f64>>,
pub histogram: Vec<Option<f64>>,
}
pub fn macd(
data: &[f64],
fast_period: usize,
slow_period: usize,
signal_period: usize,
) -> MacdResult {
let ema_fast = ema(data, fast_period);
let ema_slow = ema(data, slow_period);
let macd_line: Vec<Option<f64>> = ema_fast
.iter()
.zip(ema_slow.iter())
.map(|(f, s)| Some(f - s))
.collect();
let macd_values: Vec<f64> = macd_line.iter().filter_map(|&v| v).collect();
let signal_ema = ema(&macd_values, signal_period);
let mut signal_line = vec![None; macd_line.len().saturating_sub(signal_ema.len())];
signal_line.extend(signal_ema.iter().map(|&v| Some(v)));
let histogram: Vec<Option<f64>> = macd_line
.iter()
.zip(signal_line.iter())
.map(|(m, s)| match (m, s) {
(Some(macd_val), Some(signal_val)) => Some(macd_val - signal_val),
_ => None,
})
.collect();
MacdResult {
macd: macd_line,
signal: signal_line,
histogram,
}
}
pub fn rsi(data: &[f64], period: usize) -> Vec<Option<f64>> {
if data.len() < period + 1 {
return vec![None; data.len()];
}
let mut result = vec![None; period];
for window in data.windows(period + 1) {
let mut gains = 0.0;
let mut losses = 0.0;
for i in 1..=period {
let change = window[i] - window[i - 1];
if change > 0.0 {
gains += change;
} else {
losses += change.abs();
}
}
let avg_gain = gains / period as f64;
let avg_loss = losses / period as f64;
let rsi_value = if avg_loss == 0.0 {
100.0 } else {
let rs = avg_gain / avg_loss;
100.0 - (100.0 / (1.0 + rs))
};
result.push(Some(rsi_value));
}
result
}
#[derive(Debug, Clone)]
pub struct BollingerBandsResult {
pub upper: Vec<Option<f64>>,
pub middle: Vec<Option<f64>>,
pub lower: Vec<Option<f64>>,
}
pub fn bollinger_bands(
data: &[f64],
period: usize,
std_dev_multiplier: f64,
) -> BollingerBandsResult {
if data.is_empty() {
return BollingerBandsResult {
upper: vec![],
middle: vec![],
lower: vec![],
};
}
let sma_values = sma(data, period);
let mut upper_band = Vec::new();
let mut lower_band = Vec::new();
for (i, _) in data.iter().enumerate() {
if period < 2 || i + 1 < period {
upper_band.push(None);
lower_band.push(None);
} else {
let start = i + 1 - period;
let window = &data[start..=i];
let mean: f64 = window.iter().sum::<f64>() / period as f64;
let variance = window.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / period as f64;
let std = variance.sqrt();
if i < sma_values.len() {
if let Some(middle_val) = sma_values[i] {
upper_band.push(Some(middle_val + std_dev_multiplier * std));
lower_band.push(Some(middle_val - std_dev_multiplier * std));
} else {
upper_band.push(None);
lower_band.push(None);
}
} else {
upper_band.push(None);
lower_band.push(None);
}
}
}
BollingerBandsResult {
upper: upper_band,
middle: sma_values,
lower: lower_band,
}
}
#[derive(Debug, Clone)]
pub struct KdjResult {
pub k: Vec<Option<f64>>,
pub d: Vec<Option<f64>>,
pub j: Vec<Option<f64>>,
}
pub fn kdj(
high: &[f64],
low: &[f64],
close: &[f64],
k_period: usize,
d_period: usize,
j_multiplier: usize,
) -> KdjResult {
let len = close.len();
if len == 0 {
return KdjResult {
k: vec![],
d: vec![],
j: vec![],
};
}
if len != high.len() || len != low.len() {
return KdjResult {
k: vec![None; len],
d: vec![None; len],
j: vec![None; len],
};
}
let mut rsv_values = Vec::new();
for i in 0..len {
if k_period < 2 || i + 1 < k_period {
rsv_values.push(None);
} else {
let start = i + 1 - k_period;
let window_high = &high[start..=i];
let window_low = &low[start..=i];
let highest_high = window_high.iter().fold(f64::NAN, |a, &b| a.max(b));
let lowest_low = window_low.iter().fold(f64::NAN, |a, &b| a.min(b));
if highest_high - lowest_low > 0.0 {
let rsv = (close[i] - lowest_low) / (highest_high - lowest_low) * 100.0;
rsv_values.push(Some(rsv));
} else {
rsv_values.push(Some(50.0)); }
}
}
let k_values: Vec<f64> = rsv_values
.iter()
.filter_map(|&v| v)
.collect();
let k_ema = if k_values.is_empty() {
vec![0.0; len]
} else {
ema(&k_values, d_period)
};
let mut k_line = vec![None; rsv_values.len().saturating_sub(k_ema.len())];
k_line.extend(k_ema.iter().map(|&v| Some(v)));
let d_values: Vec<f64> = k_line.iter().filter_map(|&v| v).collect();
let d_ema = if d_values.is_empty() {
vec![0.0]
} else {
ema(&d_values, d_period)
};
let mut d_line = vec![None; k_line.len().saturating_sub(d_ema.len())];
d_line.extend(d_ema.iter().map(|&v| Some(v)));
let mut j_line = Vec::new();
for (k_val, d_val) in k_line.iter().zip(d_line.iter()) {
match (k_val, d_val) {
(Some(k), Some(d)) => {
let j = j_multiplier as f64 * k - (j_multiplier as f64 - 1.0) * d;
j_line.push(Some(j));
}
_ => {
j_line.push(None);
}
}
}
KdjResult {
k: k_line,
d: d_line,
j: j_line,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sma() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = sma(&data, 3);
assert_eq!(result.len(), 5);
assert_eq!(result[0], None);
assert_eq!(result[1], None);
assert_eq!(result[2], Some(2.0));
assert_eq!(result[3], Some(3.0));
assert_eq!(result[4], Some(4.0));
}
#[test]
fn test_ema() {
let data = vec![22.27, 22.19, 22.08, 22.17, 22.18];
let result = ema(&data, 5);
assert_eq!(result.len(), 5);
assert!((result[0] - 22.27).abs() < 0.01); for i in 1..result.len() {
assert!(result[i] > 0.0);
}
}
#[test]
fn test_macd() {
let data = vec![
10.0, 10.5, 11.0, 10.8, 11.2, 11.5, 11.3, 11.8,
12.0, 11.9, 12.2, 12.5, 12.3, 12.8, 13.0,
];
let result = macd(&data, 12, 26, 9);
assert_eq!(result.macd.len(), data.len());
assert_eq!(result.signal.len(), data.len());
assert_eq!(result.histogram.len(), data.len());
let has_values = result.macd.iter().any(|&v| v.is_some());
assert!(has_values);
}
#[test]
fn test_rsi() {
let data = vec![
10.0, 10.5, 11.0, 10.8, 11.2, 11.5, 11.3, 11.8,
12.0, 11.9, 12.2, 12.5, 12.3, 12.8, 13.0,
];
let period = 14;
let result = rsi(&data, period);
assert_eq!(result.len(), data.len());
assert_eq!(result[..period].iter().filter(|&&v| v.is_none()).count(), period);
let has_values = result[period..].iter().any(|&v| v.is_some());
assert!(has_values);
for value in result.iter().filter_map(|&v| v) {
assert!(value >= 0.0 && value <= 100.0);
}
}
#[test]
fn test_rsi_extreme_cases() {
let rising: Vec<f64> = (0..20).map(|i| 10.0 + i as f64).collect();
let result = rsi(&rising, 14);
if let Some(&Some(value)) = result.last() {
assert_eq!(value, 100.0); }
let falling: Vec<f64> = (0..20).map(|i| 30.0 - i as f64).collect();
let result = rsi(&falling, 14);
if let Some(&Some(value)) = result.last() {
assert!(value < 50.0); }
}
#[test]
fn test_bollinger_bands() {
let data = vec![
10.0, 10.5, 11.0, 10.8, 11.2, 11.5, 11.3, 11.8,
12.0, 11.9, 12.2, 12.5, 12.3, 12.8, 13.0,
];
let result = bollinger_bands(&data, 5, 2.0);
assert_eq!(result.upper.len(), data.len());
assert_eq!(result.middle.len(), data.len());
assert_eq!(result.lower.len(), data.len());
let has_values = result.upper.iter().any(|&v| v.is_some());
assert!(has_values);
for i in 0..data.len() {
if let (Some(u), Some(m), Some(l)) = (result.upper[i], result.middle[i], result.lower[i]) {
assert!(u >= m);
assert!(m >= l);
}
}
}
#[test]
fn test_kdj() {
let highs: Vec<f64> = (0..20).map(|i| 10.0 + i as f64 * 0.1 + 0.5).collect();
let lows: Vec<f64> = (0..20).map(|i| 10.0 + i as f64 * 0.1 - 0.5).collect();
let closes: Vec<f64> = (0..20).map(|i| 10.0 + i as f64 * 0.1).collect();
let result = kdj(&highs, &lows, &closes, 9, 3, 3);
assert_eq!(result.k.len(), closes.len());
assert_eq!(result.d.len(), closes.len());
assert_eq!(result.j.len(), closes.len());
let has_values = result.k.iter().any(|&v| v.is_some());
assert!(has_values);
for value in result.k.iter().filter_map(|&v| v) {
assert!(value >= 0.0 && value <= 100.0);
}
for value in result.d.iter().filter_map(|&v| v) {
assert!(value >= 0.0 && value <= 100.0);
}
}
#[test]
fn test_empty_data() {
let data: Vec<f64> = vec![];
assert_eq!(sma(&data, 5), vec![None; 0]);
assert_eq!(ema(&data, 5), vec![]);
assert_eq!(rsi(&data, 14), vec![None; 0]);
}
#[test]
fn test_single_value() {
let data = vec![10.0];
let sma_result = sma(&data, 5);
assert_eq!(sma_result.len(), 1);
assert_eq!(sma_result[0], None);
let ema_result = ema(&data, 5);
assert_eq!(ema_result.len(), 1);
assert_eq!(ema_result[0], 10.0);
}
}