use mantis_ta::indicators::{Ichimoku, Indicator};
use mantis_ta::types::Candle;
fn create_ohlc_candles(highs: &[f64], lows: &[f64], closes: &[f64]) -> Vec<Candle> {
highs
.iter()
.enumerate()
.map(|(i, &high)| Candle {
timestamp: i as i64 * 60_000,
open: closes[i],
high,
low: lows[i],
close: closes[i],
volume: 1_000_000.0,
})
.collect()
}
fn trending_candles(n: usize) -> Vec<Candle> {
let closes: Vec<f64> = (0..n).map(|i| 100.0 + i as f64).collect();
let highs: Vec<f64> = closes.iter().map(|c| c + 1.0).collect();
let lows: Vec<f64> = closes.iter().map(|c| c - 1.0).collect();
create_ohlc_candles(&highs, &lows, &closes)
}
#[test]
fn verify_ichimoku_warmup_and_bounds() {
let candles = trending_candles(60);
let ichimoku = Ichimoku::new(9, 26, 52);
let outputs = ichimoku.calculate(&candles);
assert!(
outputs
.iter()
.take(ichimoku.warmup_period() - 1)
.all(|o| o.is_none())
);
assert!(outputs[ichimoku.warmup_period() - 1].is_some());
for out in outputs.iter().flatten() {
assert!(out.tenkan_sen.is_finite());
assert!(out.kijun_sen.is_finite());
assert!(out.senkou_span_a.is_finite());
assert!(out.senkou_span_b.is_finite());
assert!(out.chikou_span.is_finite());
}
}
#[test]
fn verify_ichimoku_tenkan_kijun_formula() {
let n = 60;
let closes: Vec<f64> = vec![100.0; n];
let highs: Vec<f64> = vec![110.0; n];
let lows: Vec<f64> = vec![90.0; n];
let candles = create_ohlc_candles(&highs, &lows, &closes);
let ichimoku = Ichimoku::new(9, 26, 52);
let outputs = ichimoku.calculate(&candles);
let last = outputs.last().unwrap().unwrap();
assert!((last.tenkan_sen - 100.0).abs() < 1e-9);
assert!((last.kijun_sen - 100.0).abs() < 1e-9);
assert!((last.senkou_span_a - 100.0).abs() < 1e-9);
assert!((last.senkou_span_b - 100.0).abs() < 1e-9);
assert!((last.chikou_span - 100.0).abs() < 1e-9);
}
#[test]
fn verify_ichimoku_streaming_matches_batch() {
let candles = trending_candles(70);
let mut streaming = Ichimoku::new(9, 26, 52);
let batch_outputs = Ichimoku::new(9, 26, 52).calculate(&candles);
for (i, candle) in candles.iter().enumerate() {
let streamed = streaming.next(candle);
assert_eq!(streamed, batch_outputs[i], "mismatch at index {i}");
}
}
#[test]
fn verify_ichimoku_reset_functionality() {
let candles = trending_candles(60);
let mut ichimoku = Ichimoku::new(9, 26, 52);
for candle in &candles {
ichimoku.next(candle);
}
assert!(ichimoku.next(&candles[0]).is_some());
ichimoku.reset();
assert_eq!(ichimoku.next(&candles[0]), None);
}