#[path = "common/mod.rs"]
mod common;
use adaq_talib::pattern::*;
use adaq_talib::utils::approx_eq_slice;
fn check_cdl(
name: &str,
f: fn(&[f64], &[f64], &[f64], &[f64]) -> Result<Vec<f64>, adaq_talib::error::TaError>,
) {
let fixture = format!("cdl_{name}.json");
let json = common::load_json(&fixture).expect("load fixture");
let open = common::load_f64_array(&json, "open").expect("open");
let high = common::load_f64_array(&json, "high").expect("high");
let low = common::load_f64_array(&json, "low").expect("low");
let close = common::load_f64_array(&json, "close").expect("close");
let expected = common::load_f64_array(&json, "expected").expect("expected");
let out = f(&open, &high, &low, &close).expect("pattern fn");
assert_eq!(out.len(), expected.len(), "length mismatch for cdl_{name}");
assert!(
approx_eq_slice(&out, &expected),
"cdl_{name} output deviates from golden vector beyond ADR 0005 tolerance"
);
}
#[test]
fn cdl_homingpigeon_matches_golden_vector() {
check_cdl("homingpigeon", cdl_homingpigeon);
}
#[test]
fn cdl_identical3crows_matches_golden_vector() {
check_cdl("identical3crows", cdl_identical3crows);
}
#[test]
fn cdl_inneck_matches_golden_vector() {
check_cdl("inneck", cdl_inneck);
}
#[test]
fn cdl_invertedhammer_matches_golden_vector() {
check_cdl("invertedhammer", cdl_invertedhammer);
}
#[test]
fn cdl_kicking_matches_golden_vector() {
check_cdl("kicking", cdl_kicking);
}
#[test]
fn cdl_kickingbylength_matches_golden_vector() {
check_cdl("kickingbylength", cdl_kickingbylength);
}
#[test]
fn cdl_ladderbottom_matches_golden_vector() {
check_cdl("ladderbottom", cdl_ladderbottom);
}
fn seed(o: &mut Vec<f64>, h: &mut Vec<f64>, l: &mut Vec<f64>, c: &mut Vec<f64>, count: usize) {
for _ in 0..count {
o.push(100.0);
h.push(120.0);
l.push(98.0);
c.push(99.0);
}
}
#[test]
fn cdl_homingpigeon_trigger() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.5);
l.push(89.5);
c.push(90.0);
o.push(99.0);
h.push(99.2);
l.push(98.0);
c.push(98.5);
let out = cdl_homingpigeon(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 100.0, "homingpigeon should trigger");
}
#[test]
fn cdl_homingpigeon_reject() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.5);
l.push(89.5);
c.push(90.0);
o.push(101.0);
h.push(101.2);
l.push(98.0);
c.push(99.0);
let out = cdl_homingpigeon(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 0.0, "homingpigeon should not trigger");
}
#[test]
fn cdl_identical3crows_trigger() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(120.0);
l.push(97.5);
c.push(98.0);
o.push(98.0);
h.push(118.0);
l.push(97.5);
c.push(96.0);
o.push(96.0);
h.push(116.0);
l.push(93.5);
c.push(94.0);
let out = cdl_identical3crows(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], -100.0, "identical3crows should trigger");
}
#[test]
fn cdl_identical3crows_reject() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(120.0);
l.push(97.5);
c.push(98.0);
o.push(98.0);
h.push(118.0);
l.push(97.5);
c.push(96.0);
o.push(90.0);
h.push(116.0);
l.push(93.5);
c.push(94.0);
let out = cdl_identical3crows(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 0.0, "identical3crows should not trigger");
}
#[test]
fn cdl_inneck_trigger() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.5);
l.push(89.5);
c.push(90.0);
o.push(88.0);
h.push(92.0);
l.push(87.0);
c.push(91.0);
let out = cdl_inneck(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], -100.0, "inneck should trigger");
}
#[test]
fn cdl_inneck_reject() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.5);
l.push(89.5);
c.push(90.0);
o.push(91.0);
h.push(92.0);
l.push(87.0);
c.push(91.0);
let out = cdl_inneck(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 0.0, "inneck should not trigger");
}
#[test]
fn cdl_invertedhammer_trigger() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(95.0);
h.push(110.0);
l.push(94.5);
c.push(94.8);
let out = cdl_invertedhammer(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 100.0, "invertedhammer should trigger");
}
#[test]
fn cdl_invertedhammer_reject() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(101.0);
h.push(116.0);
l.push(100.5);
c.push(100.8);
let out = cdl_invertedhammer(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 0.0, "invertedhammer should not trigger");
}
#[test]
fn cdl_kicking_trigger() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.3);
l.push(90.0);
c.push(90.0);
o.push(101.0);
h.push(112.0);
l.push(101.0);
c.push(112.0);
let out = cdl_kicking(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 100.0, "kicking should trigger bullish");
}
#[test]
fn cdl_kicking_reject() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.3);
l.push(90.0);
c.push(90.0);
o.push(95.0);
h.push(105.0);
l.push(95.0);
c.push(105.0);
let out = cdl_kicking(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 0.0, "kicking should not trigger");
}
#[test]
fn cdl_kickingbylength_trigger() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.3);
l.push(95.0);
c.push(95.0);
o.push(101.0);
h.push(112.0);
l.push(101.0);
c.push(112.0);
let out = cdl_kickingbylength(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 100.0, "kickingbylength bullish by longer body");
}
#[test]
fn cdl_kickingbylength_reject() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(100.3);
l.push(95.0);
c.push(95.0);
o.push(98.0);
h.push(113.0);
l.push(98.0);
c.push(113.0);
let out = cdl_kickingbylength(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 0.0, "kickingbylength should not trigger");
}
#[test]
fn cdl_ladderbottom_trigger() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(101.0);
l.push(99.0);
c.push(98.0);
o.push(98.0);
h.push(99.0);
l.push(97.0);
c.push(96.0);
o.push(96.0);
h.push(97.0);
l.push(95.0);
c.push(94.0);
o.push(94.0);
h.push(98.0);
l.push(93.5);
c.push(93.6);
o.push(94.5);
h.push(99.0);
l.push(93.8);
c.push(98.5);
let out = cdl_ladderbottom(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 100.0, "ladderbottom should trigger");
}
#[test]
fn cdl_ladderbottom_reject() {
let mut o = Vec::new();
let mut h = Vec::new();
let mut l = Vec::new();
let mut c = Vec::new();
seed(&mut o, &mut h, &mut l, &mut c, 20);
o.push(100.0);
h.push(101.0);
l.push(99.0);
c.push(98.0);
o.push(98.0);
h.push(99.0);
l.push(97.0);
c.push(96.0);
o.push(96.0);
h.push(97.0);
l.push(95.0);
c.push(94.0);
o.push(94.0);
h.push(98.0);
l.push(93.5);
c.push(93.6);
o.push(94.0);
h.push(97.5);
l.push(93.8);
c.push(97.0);
let out = cdl_ladderbottom(&o, &h, &l, &c).unwrap();
assert_eq!(out[out.len() - 1], 0.0, "ladderbottom should not trigger");
}