use crate::{
KandError,
TAFloat,
TAInt,
helper::{lower_shadow_length, real_body_length},
types::Signal,
};
pub const fn lookback() -> Result<usize, KandError> {
Ok(0)
}
pub fn cdl_gravestone_doji(
input_open: &[TAFloat],
input_high: &[TAFloat],
input_low: &[TAFloat],
input_close: &[TAFloat],
param_body_percent: TAFloat,
output_signals: &mut [TAInt],
) -> Result<(), KandError> {
let len = input_open.len();
#[cfg(feature = "check")]
{
if len != input_high.len()
|| len != input_low.len()
|| len != input_close.len()
|| len != output_signals.len()
{
return Err(KandError::LengthMismatch);
}
}
#[cfg(feature = "deep-check")]
{
for i in 0..len {
if input_open[i].is_nan()
|| input_high[i].is_nan()
|| input_low[i].is_nan()
|| input_close[i].is_nan()
{
return Err(KandError::NaNDetected);
}
}
}
for i in 0..len {
output_signals[i] = cdl_gravestone_doji_inc(
input_open[i],
input_high[i],
input_low[i],
input_close[i],
param_body_percent,
)?;
}
Ok(())
}
pub fn cdl_gravestone_doji_inc(
input_open: TAFloat,
input_high: TAFloat,
input_low: TAFloat,
input_close: TAFloat,
param_body_percent: TAFloat,
) -> Result<TAInt, KandError> {
#[cfg(feature = "deep-check")]
{
if input_open.is_nan() || input_high.is_nan() || input_low.is_nan() || input_close.is_nan()
{
return Err(KandError::NaNDetected);
}
}
let body = real_body_length(input_open, input_close);
let range = input_high - input_low;
let dn_shadow = lower_shadow_length(input_low, input_open, input_close);
let is_doji_body = range > 0.0 && body <= range * param_body_percent / 100.0;
let has_minimal_lower_shadow = dn_shadow <= body;
let output_signal = if is_doji_body && has_minimal_lower_shadow {
Signal::Bearish.into()
} else {
Signal::Neutral.into()
};
Ok(output_signal)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cdl_gravestone_doji() {
let input_open = vec![
102_730.6, 102_233.5, 102_003.4, 102_330.6, 102_211.8, 102_994.9, 102_817.5, 102_407.9,
102_525.3, 103_002.3, 102_826.2, 102_499.1, 102_161.0, 102_033.9, 102_191.6, 102_358.0,
102_368.7, 102_354.8, 101_928.3, 101_923.5, 101_226.3,
];
let input_high = vec![
102_870.3, 102_421.8, 102_374.7, 102_543.5, 103_065.9, 103_059.4, 102_899.3, 102_713.6,
103_089.9, 103_083.5, 102_914.6, 102_510.8, 102_204.8, 102_366.8, 102_358.1, 102_624.0,
102_495.0, 102_354.9, 102_115.4, 101_933.7, 101_449.1,
];
let input_low = vec![
102_205.0, 101_850.0, 101_984.1, 101_921.2, 102_170.3, 102_700.0, 102_301.0, 102_308.3,
102_336.7, 102_733.2, 102_435.0, 102_123.9, 101_778.0, 101_929.2, 101_994.0, 102_357.9,
102_241.4, 101_921.8, 101_852.7, 101_195.2, 101_056.1,
];
let input_close = vec![
102_233.4, 102_003.3, 102_330.6, 102_211.8, 102_994.8, 102_817.5, 102_407.8, 102_525.2,
103_002.3, 102_826.3, 102_499.2, 102_161.1, 102_033.8, 102_191.6, 102_358.0, 102_368.7,
102_354.8, 101_928.4, 101_923.6, 101_226.3, 101_260.0,
];
let param_body_percent = 5.0;
let mut output_signals = vec![0i64; input_open.len()];
cdl_gravestone_doji(
&input_open,
&input_high,
&input_low,
&input_close,
param_body_percent,
&mut output_signals,
)
.unwrap();
println!("output_signals: {output_signals:?}");
assert_eq!(output_signals[15], Signal::Bearish.into());
for i in 0..18 {
let output_signal: i64 = cdl_gravestone_doji_inc(
input_open[i],
input_high[i],
input_low[i],
input_close[i],
param_body_percent,
)
.unwrap();
assert_eq!(output_signal, output_signals[i]);
}
}
}