use crate::kernels;
pub fn mom(data: &[f64], period: usize) -> Vec<f64> {
let n = data.len();
if period >= n {
return vec![f64::NAN; n];
}
let mut out = vec![f64::NAN; period];
out.extend((period..n).map(|i| data[i] - data[i - period]));
out
}
fn roc_ratio(data: &[f64], period: usize, f: impl Fn(f64, f64) -> f64) -> Vec<f64> {
let n = data.len();
if period >= n {
return vec![f64::NAN; n];
}
let mut out = vec![f64::NAN; period];
out.extend(
data[period..]
.iter()
.zip(&data[..(n - period)])
.map(
|(¤t, &prior)| {
if prior == 0.0 {
0.0
} else {
f(current, prior)
}
},
),
);
out
}
pub fn roc(data: &[f64], period: usize) -> Vec<f64> {
let n = data.len();
if period >= n {
return vec![f64::NAN; n];
}
let mut out = crate::buf::OutBuf::warmup(n, period);
let data_ptr = data.as_ptr();
let out_ptr = out.ptr();
let mut cur_ptr = unsafe { data_ptr.add(period) };
let mut prior_ptr = data_ptr;
let mut out_write = unsafe { out_ptr.add(period) };
let mut remaining = n - period;
while remaining > 0 {
let prior = unsafe { *prior_ptr };
let value = if prior == 0.0 {
0.0
} else {
(unsafe { *cur_ptr } / prior - 1.0) * 100.0
};
unsafe {
*out_write = value;
cur_ptr = cur_ptr.add(1);
prior_ptr = prior_ptr.add(1);
out_write = out_write.add(1);
}
remaining -= 1;
}
out.finish()
}
pub fn rocp(data: &[f64], period: usize) -> Vec<f64> {
roc_ratio(data, period, |cur, prior| cur / prior - 1.0)
}
pub fn rocr(data: &[f64], period: usize) -> Vec<f64> {
roc_ratio(data, period, |cur, prior| cur / prior)
}
pub fn rocr100(data: &[f64], period: usize) -> Vec<f64> {
roc_ratio(data, period, |cur, prior| cur / prior * 100.0)
}
pub fn willr(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let n = close.len();
if period == 0 || period > n {
return vec![f64::NAN; n];
}
let lookback = period - 1;
let mut out = crate::buf::OutBuf::warmup(n, lookback);
let mut today = lookback;
let mut trailing = 0usize;
let mut highest_idx = usize::MAX;
let mut lowest_idx = usize::MAX;
let mut highest = 0.0;
let mut lowest = 0.0;
while today < n {
let low_today = unsafe { *low.get_unchecked(today) };
if lowest_idx == usize::MAX || lowest_idx < trailing {
lowest_idx = trailing;
lowest = unsafe { *low.get_unchecked(trailing) };
let mut idx = trailing + 1;
while idx <= today {
let value = unsafe { *low.get_unchecked(idx) };
if value < lowest {
lowest_idx = idx;
lowest = value;
}
idx += 1;
}
} else if low_today <= lowest {
lowest_idx = today;
lowest = low_today;
}
let high_today = unsafe { *high.get_unchecked(today) };
if highest_idx == usize::MAX || highest_idx < trailing {
highest_idx = trailing;
highest = unsafe { *high.get_unchecked(trailing) };
let mut idx = trailing + 1;
while idx <= today {
let value = unsafe { *high.get_unchecked(idx) };
if value > highest {
highest_idx = idx;
highest = value;
}
idx += 1;
}
} else if high_today >= highest {
highest_idx = today;
highest = high_today;
}
let range = highest - lowest;
if range != 0.0 {
out.set(today, (highest - unsafe { *close.get_unchecked(today) }) * (-100.0 / range));
} else {
out.set(today, 0.0);
}
trailing += 1;
today += 1;
}
out.finish()
}
pub fn bop(open: &[f64], high: &[f64], low: &[f64], close: &[f64]) -> Vec<f64> {
(0..close.len())
.map(|i| {
let range = high[i] - low[i];
if range < 1e-14 {
0.0
} else {
(close[i] - open[i]) / range
}
})
.collect()
}
pub fn imi(open: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let n = close.len();
if period == 0 || period > n {
return vec![f64::NAN; n];
}
let lookback = period - 1;
let updown = |i: usize| -> (f64, f64) {
let (c, o) = (close[i], open[i]);
if c > o {
(c - o, 0.0)
} else {
(0.0, o - c)
}
};
let (mut up_sum, mut down_sum) = (0.0, 0.0);
for i in 0..lookback {
let (u, d) = updown(i);
up_sum += u;
down_sum += d;
}
let mut out = crate::buf::OutBuf::warmup(n, lookback);
for today in lookback..n {
let (u, d) = updown(today);
up_sum += u;
down_sum += d;
out.set(today, 100.0 * (up_sum / (up_sum + down_sum)));
let (lu, ld) = updown(today - lookback);
up_sum -= lu;
down_sum -= ld;
}
out.finish()
}
pub fn cci(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<f64> {
let n = close.len();
if period == 0 || period > n {
return vec![f64::NAN; n];
}
let p = period as f64;
let tp: Vec<f64> = (0..n)
.map(|i| (high[i] + low[i] + close[i]) / 3.0)
.collect();
let mut sum: f64 = tp[..period - 1].iter().sum();
let mut out = crate::buf::OutBuf::warmup(n, period - 1);
for i in (period - 1)..n {
sum += tp[i];
let avg = sum / p;
let window = &tp[i + 1 - period..=i];
let sum_dev: f64 = window.iter().map(|x| (x - avg).abs()).sum();
let num = tp[i] - avg;
out.set(i, if num != 0.0 && sum_dev != 0.0 {
num / (0.015 * (sum_dev / p))
} else {
0.0
});
sum -= tp[i + 1 - period];
}
out.finish()
}
pub fn trix(close: &[f64], period: usize) -> Vec<f64> {
let e3 = kernels::ema_cascade::<3>(close, period);
roc(&e3, 1)
}
pub fn trix_final_state(close: &[f64], period: usize) -> Option<Vec<f64>> {
let e = kernels::ema_cascade_final::<3>(close, period)?;
Some(e.to_vec())
}
pub fn trix_resume(
close: &[f64],
period: usize,
from: usize,
state: &[f64],
) -> (Vec<f64>, Vec<f64>) {
let k = 2.0 / (period as f64 + 1.0);
let n = close.len();
let mut e = [state[0], state[1], state[2]];
let mut out = Vec::with_capacity(n.saturating_sub(from));
for &x in &close[from..n] {
let prev = e[2]; kernels::ema_cascade_step(&mut e, x, k);
out.push((e[2] / prev - 1.0) * 100.0);
}
(out, e.to_vec())
}
pub fn aroon_up(high: &[f64], _low: &[f64], period: usize) -> Vec<f64> {
let n = high.len();
if period == 0 || period >= n {
return vec![f64::NAN; n];
}
let mut out = Vec::with_capacity(n);
out.resize(period, f64::NAN);
let factor = 100.0 / period as f64;
let pf = period as f64;
let mut today = period;
let mut trailing = 0usize;
let mut highest_idx = usize::MAX;
let mut highest = 0.0;
while today < n {
let value = high[today];
if highest_idx == usize::MAX || highest_idx < trailing {
highest_idx = trailing;
highest = high[trailing];
for (off, &candidate) in high[(trailing + 1)..=today].iter().enumerate() {
if candidate >= highest {
highest_idx = trailing + 1 + off;
highest = candidate;
}
}
} else if value >= highest {
highest_idx = today;
highest = value;
}
out.push(factor * (pf - (today - highest_idx) as f64));
trailing += 1;
today += 1;
}
out
}
pub fn aroon_down(_high: &[f64], low: &[f64], period: usize) -> Vec<f64> {
let n = low.len();
if period == 0 || period >= n {
return vec![f64::NAN; n];
}
let mut out = Vec::with_capacity(n);
out.resize(period, f64::NAN);
let factor = 100.0 / period as f64;
let pf = period as f64;
let mut today = period;
let mut trailing = 0usize;
let mut lowest_idx = usize::MAX;
let mut lowest = 0.0;
while today < n {
let value = low[today];
if lowest_idx == usize::MAX || lowest_idx < trailing {
lowest_idx = trailing;
lowest = low[trailing];
for (off, &candidate) in low[(trailing + 1)..=today].iter().enumerate() {
if candidate <= lowest {
lowest_idx = trailing + 1 + off;
lowest = candidate;
}
}
} else if value <= lowest {
lowest_idx = today;
lowest = value;
}
out.push(factor * (pf - (today - lowest_idx) as f64));
trailing += 1;
today += 1;
}
out
}
pub fn aroonosc(high: &[f64], low: &[f64], period: usize) -> Vec<f64> {
let n = high.len();
if period == 0 || period >= n {
return vec![f64::NAN; n];
}
let mut out = crate::buf::OutBuf::warmup(n, period);
let factor = 100.0 / period as f64;
let high_ptr = high.as_ptr();
let low_ptr = low.as_ptr();
let out_ptr = out.ptr();
let mut highest_idx = 0usize;
let mut lowest_idx = 0usize;
let mut highest = unsafe { *high_ptr };
let mut lowest = unsafe { *low_ptr };
let mut idx = 1usize;
while idx <= period {
let low_value = unsafe { *low_ptr.add(idx) };
if low_value <= lowest {
lowest_idx = idx;
lowest = low_value;
}
let high_value = unsafe { *high_ptr.add(idx) };
if high_value >= highest {
highest_idx = idx;
highest = high_value;
}
idx += 1;
}
unsafe {
*out_ptr.add(period) = factor * (highest_idx as isize - lowest_idx as isize) as f64;
}
let mut today = period + 1;
let mut trailing = 1usize;
let mut out_write = unsafe { out_ptr.add(today) };
while today < n {
let low_today = unsafe { *low_ptr.add(today) };
if lowest_idx < trailing {
lowest_idx = trailing;
lowest = unsafe { *low_ptr.add(trailing) };
let mut idx = trailing + 1;
while idx <= today {
let value = unsafe { *low_ptr.add(idx) };
if value <= lowest {
lowest_idx = idx;
lowest = value;
}
idx += 1;
}
} else if low_today <= lowest {
lowest_idx = today;
lowest = low_today;
}
let high_today = unsafe { *high_ptr.add(today) };
if highest_idx < trailing {
highest_idx = trailing;
highest = unsafe { *high_ptr.add(trailing) };
let mut idx = trailing + 1;
while idx <= today {
let value = unsafe { *high_ptr.add(idx) };
if value >= highest {
highest_idx = idx;
highest = value;
}
idx += 1;
}
} else if high_today >= highest {
highest_idx = today;
highest = high_today;
}
unsafe {
*out_write = factor * (highest_idx as isize - lowest_idx as isize) as f64;
out_write = out_write.add(1);
}
trailing += 1;
today += 1;
}
out.finish()
}
pub fn mfi(high: &[f64], low: &[f64], close: &[f64], volume: &[f64], period: usize) -> Vec<f64> {
let n = close.len();
if period == 0 || period + 1 > n {
return vec![f64::NAN; n];
}
let mut out = crate::buf::OutBuf::warmup(n, period);
let mut ring_pos = vec![0.0; period];
let mut ring_neg = vec![0.0; period];
let mut prev_tp = (unsafe { *high.get_unchecked(0) }
+ unsafe { *low.get_unchecked(0) }
+ unsafe { *close.get_unchecked(0) })
/ 3.0;
let mut pos_sum = 0.0;
let mut neg_sum = 0.0;
let mut i = 1usize;
while i <= period {
let tp = (unsafe { *high.get_unchecked(i) }
+ unsafe { *low.get_unchecked(i) }
+ unsafe { *close.get_unchecked(i) })
/ 3.0;
let flow = tp * unsafe { *volume.get_unchecked(i) };
let (p, ng) = if tp > prev_tp {
(flow, 0.0)
} else if tp < prev_tp {
(0.0, flow)
} else {
(0.0, 0.0)
};
ring_pos[i - 1] = p;
ring_neg[i - 1] = ng;
pos_sum += p;
neg_sum += ng;
prev_tp = tp;
i += 1;
}
let total = pos_sum + neg_sum;
out.set(period, if total < 1.0 {
0.0
} else {
100.0 * pos_sum / total
});
let mut slot = 0usize; i = period + 1;
while i < n {
pos_sum -= ring_pos[slot]; neg_sum -= ring_neg[slot];
let tp = (unsafe { *high.get_unchecked(i) }
+ unsafe { *low.get_unchecked(i) }
+ unsafe { *close.get_unchecked(i) })
/ 3.0;
let flow = tp * unsafe { *volume.get_unchecked(i) };
let (p, ng) = if tp > prev_tp {
(flow, 0.0)
} else if tp < prev_tp {
(0.0, flow)
} else {
(0.0, 0.0)
};
ring_pos[slot] = p; ring_neg[slot] = ng;
pos_sum += p;
neg_sum += ng;
let total = pos_sum + neg_sum;
out.set(i, if total < 1.0 {
0.0
} else {
100.0 * pos_sum / total
});
prev_tp = tp;
slot += 1;
if slot == period {
slot = 0;
}
i += 1;
}
out.finish()
}
pub fn ultosc(
high: &[f64],
low: &[f64],
close: &[f64],
p1: usize,
p2: usize,
p3: usize,
) -> Vec<f64> {
let n = close.len();
let max_p = p1.max(p2).max(p3);
if p1 == 0 || p2 == 0 || p3 == 0 || max_p >= n {
return vec![f64::NAN; n];
}
let term = |i: usize| -> (f64, f64) {
let prev_close = close[i - 1];
let true_low = low[i].min(prev_close);
let bp = close[i] - true_low;
let mut tr = high[i] - low[i];
tr = tr.max((prev_close - high[i]).abs());
tr = tr.max((prev_close - low[i]).abs());
(bp, tr)
};
let start = max_p; let mut bp_ring = vec![0.0; max_p];
let mut tr_ring = vec![0.0; max_p];
let (mut a1, mut b1, mut a2, mut b2, mut a3, mut b3) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
for i in (start - max_p + 1)..start {
let (a, b) = term(i);
bp_ring[i % max_p] = a;
tr_ring[i % max_p] = b;
if i >= start - p1 + 1 {
a1 += a;
b1 += b;
}
if i >= start - p2 + 1 {
a2 += a;
b2 += b;
}
a3 += a;
b3 += b;
}
let (mut t1, mut t2, mut t3) = (start - p1 + 1, start - p2 + 1, start - p3 + 1);
let mut out = crate::buf::OutBuf::warmup(n, start);
for today in start..n {
let (a, b) = term(today);
let slot = today % max_p;
bp_ring[slot] = a;
tr_ring[slot] = b;
a1 += a;
a2 += a;
a3 += a;
b1 += b;
b2 += b;
b3 += b;
let mut output = 0.0;
if b1.abs() >= 1e-14 {
output += 4.0 * (a1 / b1);
}
if b2.abs() >= 1e-14 {
output += 2.0 * (a2 / b2);
}
if b3.abs() >= 1e-14 {
output += a3 / b3;
}
let at = bp_ring[t1 % max_p];
let bt = tr_ring[t1 % max_p];
a1 -= at;
b1 -= bt;
t1 += 1;
let at = bp_ring[t2 % max_p];
let bt = tr_ring[t2 % max_p];
a2 -= at;
b2 -= bt;
t2 += 1;
let at = bp_ring[t3 % max_p];
let bt = tr_ring[t3 % max_p];
a3 -= at;
b3 -= bt;
t3 += 1;
out.set(today, 100.0 * (output / 7.0));
}
out.finish()
}