use super::{
candle_average, candle_gap_down, candle_gap_up, candle_output, color, each_bar, each_bar_avg_n,
lowershadow, range, realbody, realbody_gap_down, realbody_gap_up, uppershadow, BODY_DOJI,
BODY_LONG, BODY_SHORT, EQUAL, FAR, NEAR, SHADOW_LONG, SHADOW_SHORT, SHADOW_VERY_SHORT,
};
pub fn cdl_morningstar(o: &[f64], h: &[f64], l: &[f64], c: &[f64], penetration: f64) -> Vec<f64> {
let lb = BODY_SHORT.avg_period.max(BODY_LONG.avg_period) + 2;
each_bar_avg_n::<2, 3>([BODY_LONG, BODY_SHORT], lb, o, h, l, c, |i, hist| {
if realbody(o, c, i - 2) > hist[2][0] && color(o, c, i - 2) < 0.0
&& realbody(o, c, i - 1) <= hist[1][1] && realbody_gap_down(o, c, i - 1, i - 2)
&& realbody(o, c, i) > hist[0][1] && color(o, c, i) > 0.0
&& c[i] > c[i - 2] + realbody(o, c, i - 2) * penetration
{
100.0
} else {
0.0
}
})
}
pub fn cdl_eveningstar(o: &[f64], h: &[f64], l: &[f64], c: &[f64], penetration: f64) -> Vec<f64> {
let lb = BODY_SHORT.avg_period.max(BODY_LONG.avg_period) + 2;
each_bar_avg_n::<2, 3>([BODY_LONG, BODY_SHORT], lb, o, h, l, c, |i, hist| {
if realbody(o, c, i - 2) > hist[2][0] && color(o, c, i - 2) > 0.0
&& realbody(o, c, i - 1) <= hist[1][1] && realbody_gap_up(o, c, i - 1, i - 2)
&& realbody(o, c, i) > hist[0][1] && color(o, c, i) < 0.0
&& c[i] < c[i - 2] - realbody(o, c, i - 2) * penetration
{
-100.0
} else {
0.0
}
})
}
pub fn cdl_3inside(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = BODY_SHORT.avg_period.max(BODY_LONG.avg_period) + 2;
each_bar_avg_n::<2, 3>([BODY_LONG, BODY_SHORT], lb, o, h, l, c, |i, hist| {
let engulfed = o[i - 1].max(c[i - 1]) < o[i - 2].max(c[i - 2])
&& o[i - 1].min(c[i - 1]) > o[i - 2].min(c[i - 2]);
let confirmed = (color(o, c, i - 2) > 0.0 && color(o, c, i) < 0.0 && c[i] < o[i - 2])
|| (color(o, c, i - 2) < 0.0 && color(o, c, i) > 0.0 && c[i] > o[i - 2]);
if realbody(o, c, i - 2) > hist[2][0] && realbody(o, c, i - 1) <= hist[1][1] && engulfed
&& confirmed
{
-color(o, c, i - 2) * 100.0
} else {
0.0
}
})
}
pub fn cdl_3outside(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let _ = (h, l);
each_bar(c.len(), 3, |i| {
let bull = color(o, c, i - 1) > 0.0
&& color(o, c, i - 2) < 0.0
&& c[i - 1] > o[i - 2]
&& o[i - 1] < c[i - 2]
&& c[i] > c[i - 1];
let bear = color(o, c, i - 1) < 0.0
&& color(o, c, i - 2) > 0.0
&& o[i - 1] > c[i - 2]
&& c[i - 1] < o[i - 2]
&& c[i] < c[i - 1];
if bull || bear {
color(o, c, i - 1) * 100.0
} else {
0.0
}
})
}
pub fn cdl_3whitesoldiers(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = SHADOW_VERY_SHORT
.avg_period
.max(BODY_SHORT.avg_period)
.max(NEAR.avg_period)
.max(FAR.avg_period)
+ 2;
each_bar_avg_n::<4, 3>(
[SHADOW_VERY_SHORT, NEAR, FAR, BODY_SHORT],
lb,
o,
h,
l,
c,
|i, hist| {
let short_upper = |bar: usize, lag: usize| uppershadow(o, h, c, bar) < hist[lag][0];
if color(o, c, i - 2) > 0.0
&& short_upper(i - 2, 2)
&& color(o, c, i - 1) > 0.0
&& short_upper(i - 1, 1)
&& color(o, c, i) > 0.0
&& short_upper(i, 0)
&& c[i] > c[i - 1]
&& c[i - 1] > c[i - 2]
&& o[i - 1] > o[i - 2]
&& o[i - 1] <= c[i - 2] + hist[2][1] && o[i] > o[i - 1]
&& o[i] <= c[i - 1] + hist[1][1] && realbody(o, c, i - 1) > realbody(o, c, i - 2) - hist[2][2] && realbody(o, c, i) > realbody(o, c, i - 1) - hist[1][2] && realbody(o, c, i) > hist[0][3]
{
100.0
} else {
0.0
}
},
)
}
pub fn cdl_3blackcrows(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = SHADOW_VERY_SHORT.avg_period + 3;
each_bar(c.len(), lb, |i| {
let short_lower =
|k: usize| lowershadow(o, l, c, k) < candle_average(SHADOW_VERY_SHORT, o, h, l, c, k);
if color(o, c, i - 3) > 0.0
&& color(o, c, i - 2) < 0.0
&& short_lower(i - 2)
&& color(o, c, i - 1) < 0.0
&& short_lower(i - 1)
&& color(o, c, i) < 0.0
&& short_lower(i)
&& o[i - 1] < o[i - 2]
&& o[i - 1] > c[i - 2]
&& o[i] < o[i - 1]
&& o[i] > c[i - 1]
&& h[i - 3] > c[i - 2]
&& c[i - 2] > c[i - 1]
&& c[i - 1] > c[i]
{
-100.0
} else {
0.0
}
})
}
pub fn cdl_morningdojistar(
o: &[f64],
h: &[f64],
l: &[f64],
c: &[f64],
penetration: f64,
) -> Vec<f64> {
let lb = BODY_DOJI
.avg_period
.max(BODY_LONG.avg_period)
.max(BODY_SHORT.avg_period)
+ 2;
each_bar_avg_n::<3, 3>(
[BODY_LONG, BODY_DOJI, BODY_SHORT],
lb,
o,
h,
l,
c,
|i, hist| {
if realbody(o, c, i - 2) > hist[2][0] && color(o, c, i - 2) < 0.0
&& realbody(o, c, i - 1) <= hist[1][1] && realbody_gap_down(o, c, i - 1, i - 2)
&& realbody(o, c, i) > hist[0][2] && color(o, c, i) > 0.0
&& c[i] > c[i - 2] + realbody(o, c, i - 2) * penetration
{
100.0
} else {
0.0
}
},
)
}
pub fn cdl_eveningdojistar(
o: &[f64],
h: &[f64],
l: &[f64],
c: &[f64],
penetration: f64,
) -> Vec<f64> {
let lb = BODY_DOJI
.avg_period
.max(BODY_LONG.avg_period)
.max(BODY_SHORT.avg_period)
+ 2;
each_bar_avg_n::<3, 3>(
[BODY_LONG, BODY_DOJI, BODY_SHORT],
lb,
o,
h,
l,
c,
|i, hist| {
if realbody(o, c, i - 2) > hist[2][0] && color(o, c, i - 2) > 0.0
&& realbody(o, c, i - 1) <= hist[1][1] && realbody_gap_up(o, c, i - 1, i - 2)
&& realbody(o, c, i) > hist[0][2] && color(o, c, i) < 0.0
&& c[i] < c[i - 2] - realbody(o, c, i - 2) * penetration
{
-100.0
} else {
0.0
}
},
)
}
pub fn cdl_abandonedbaby(o: &[f64], h: &[f64], l: &[f64], c: &[f64], penetration: f64) -> Vec<f64> {
let lb = BODY_DOJI
.avg_period
.max(BODY_LONG.avg_period)
.max(BODY_SHORT.avg_period)
+ 2;
each_bar_avg_n::<3, 3>(
[BODY_LONG, BODY_DOJI, BODY_SHORT],
lb,
o,
h,
l,
c,
|i, hist| {
let long_doji_body = realbody(o, c, i - 2) > hist[2][0] && realbody(o, c, i - 1) <= hist[1][1] && realbody(o, c, i) > hist[0][2]; let bottom = color(o, c, i - 2) > 0.0
&& color(o, c, i) < 0.0
&& c[i] < c[i - 2] - realbody(o, c, i - 2) * penetration
&& candle_gap_up(h, l, i - 1, i - 2)
&& candle_gap_down(h, l, i, i - 1);
let top = color(o, c, i - 2) < 0.0
&& color(o, c, i) > 0.0
&& c[i] > c[i - 2] + realbody(o, c, i - 2) * penetration
&& candle_gap_down(h, l, i - 1, i - 2)
&& candle_gap_up(h, l, i, i - 1);
if long_doji_body && (bottom || top) {
color(o, c, i) * 100.0
} else {
0.0
}
},
)
}
pub fn cdl_2crows(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = BODY_LONG.avg_period + 2;
let n = c.len();
let Some(mut out) = candle_output(n, lb) else {
return vec![f64::NAN; n];
};
let mut total = 0.0;
let mut trailing = lb - 2 - BODY_LONG.avg_period;
for j in trailing..(lb - 2) {
total += range(BODY_LONG, o, h, l, c, j);
}
let body_long_scale = BODY_LONG.factor / BODY_LONG.avg_period as f64;
for i in lb..n {
let first_white = c[i - 2] >= o[i - 2];
let second_black = c[i - 1] < o[i - 1];
let third_black = c[i] < o[i];
out.set(i, if first_white
&& second_black
&& third_black
&& realbody(o, c, i - 2) > total * body_long_scale
&& realbody_gap_up(o, c, i - 1, i - 2)
&& o[i] < o[i - 1]
&& o[i] > c[i - 1]
&& c[i] > o[i - 2]
&& c[i] < c[i - 2]
{
-100.0
} else {
0.0
});
total += range(BODY_LONG, o, h, l, c, i - 2) - range(BODY_LONG, o, h, l, c, trailing);
trailing += 1;
}
out.finish()
}
pub fn cdl_upsidegap2crows(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = BODY_SHORT.avg_period.max(BODY_LONG.avg_period) + 2;
each_bar_avg_n::<2, 3>([BODY_LONG, BODY_SHORT], lb, o, h, l, c, |i, hist| {
if color(o, c, i - 2) > 0.0
&& realbody(o, c, i - 2) > hist[2][0] && color(o, c, i - 1) < 0.0
&& realbody(o, c, i - 1) <= hist[1][1] && realbody_gap_up(o, c, i - 1, i - 2)
&& color(o, c, i) < 0.0
&& o[i] > o[i - 1]
&& c[i] < c[i - 1]
&& c[i] > c[i - 2]
{
-100.0
} else {
0.0
}
})
}
pub fn cdl_advanceblock(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = SHADOW_LONG
.avg_period
.max(SHADOW_SHORT.avg_period)
.max(BODY_LONG.avg_period)
.max(NEAR.avg_period)
.max(FAR.avg_period)
+ 2;
each_bar_avg_n::<5, 3>(
[FAR, NEAR, SHADOW_SHORT, SHADOW_LONG, BODY_LONG],
lb,
o,
h,
l,
c,
|i, hist| {
let (rb, rb1, rb2) = (
realbody(o, c, i),
realbody(o, c, i - 1),
realbody(o, c, i - 2),
);
let weakening = (rb1 < rb2 - hist[2][0] && rb < rb1 + hist[1][1]) || (rb < rb1 - hist[1][0]) || (rb < rb1
&& rb1 < rb2
&& (uppershadow(o, h, c, i) > hist[0][2] || uppershadow(o, h, c, i - 1) > hist[1][2])) || (rb < rb1 && uppershadow(o, h, c, i) > hist[0][3]); if color(o, c, i - 2) > 0.0
&& color(o, c, i - 1) > 0.0
&& color(o, c, i) > 0.0
&& c[i] > c[i - 1]
&& c[i - 1] > c[i - 2]
&& o[i - 1] > o[i - 2]
&& o[i - 1] <= c[i - 2] + hist[2][1] && o[i] > o[i - 1]
&& o[i] <= c[i - 1] + hist[1][1] && rb2 > hist[2][4] && uppershadow(o, h, c, i - 2) < hist[2][2] && weakening
{
-100.0
} else {
0.0
}
},
)
}
pub fn cdl_stalledpattern(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = BODY_LONG
.avg_period
.max(BODY_SHORT.avg_period)
.max(SHADOW_VERY_SHORT.avg_period)
.max(NEAR.avg_period)
+ 2;
let Some(mut out) = candle_output(c.len(), lb) else {
return vec![f64::NAN; c.len()];
};
let mut i = lb;
while i < c.len() {
out.set(i, if color(o, c, i - 2) > 0.0
&& color(o, c, i - 1) > 0.0
&& color(o, c, i) > 0.0
&& c[i] > c[i - 1]
&& c[i - 1] > c[i - 2]
&& realbody(o, c, i - 2) > candle_average(BODY_LONG, o, h, l, c, i - 2)
&& realbody(o, c, i - 1) > candle_average(BODY_LONG, o, h, l, c, i - 1)
&& uppershadow(o, h, c, i - 1) < candle_average(SHADOW_VERY_SHORT, o, h, l, c, i - 1)
&& o[i - 1] > o[i - 2]
&& o[i - 1] <= c[i - 2] + candle_average(NEAR, o, h, l, c, i - 2)
&& realbody(o, c, i) < candle_average(BODY_SHORT, o, h, l, c, i)
&& o[i] >= c[i - 1] - realbody(o, c, i) - candle_average(NEAR, o, h, l, c, i - 1)
{
-100.0
} else {
0.0
});
i += 1;
}
out.finish()
}
pub fn cdl_identical3crows(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = SHADOW_VERY_SHORT.avg_period.max(EQUAL.avg_period) + 2;
each_bar_avg_n::<2, 3>([SHADOW_VERY_SHORT, EQUAL], lb, o, h, l, c, |i, hist| {
let short_lower = |bar: usize, lag: usize| lowershadow(o, l, c, bar) < hist[lag][0];
let eq2 = hist[2][1]; let eq1 = hist[1][1]; if color(o, c, i - 2) < 0.0
&& short_lower(i - 2, 2)
&& color(o, c, i - 1) < 0.0
&& short_lower(i - 1, 1)
&& color(o, c, i) < 0.0
&& short_lower(i, 0)
&& c[i - 2] > c[i - 1]
&& c[i - 1] > c[i]
&& o[i - 1] <= c[i - 2] + eq2
&& o[i - 1] >= c[i - 2] - eq2
&& o[i] <= c[i - 1] + eq1
&& o[i] >= c[i - 1] - eq1
{
-100.0
} else {
0.0
}
})
}
pub fn cdl_sticksandwich(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = EQUAL.avg_period + 2;
each_bar_avg_n::<1, 3>([EQUAL], lb, o, h, l, c, |i, hist| {
let eq = hist[2][0]; if color(o, c, i - 2) < 0.0
&& color(o, c, i - 1) > 0.0
&& color(o, c, i) < 0.0
&& l[i - 1] > c[i - 2]
&& c[i] <= c[i - 2] + eq
&& c[i] >= c[i - 2] - eq
{
100.0
} else {
0.0
}
})
}
pub fn cdl_tristar(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = BODY_DOJI.avg_period + 2;
let n = c.len();
let Some(mut out) = candle_output(n, lb) else {
return vec![f64::NAN; n];
};
let mut total = 0.0;
let mut trailing = lb - 2 - BODY_DOJI.avg_period;
for j in trailing..(lb - 2) {
total += h[j] - l[j];
}
let body_doji_scale = BODY_DOJI.factor / BODY_DOJI.avg_period as f64;
for i in lb..n {
let doji = total * body_doji_scale;
let prev_lo = o[i - 2].min(c[i - 2]);
let prev_hi = o[i - 2].max(c[i - 2]);
let mid_lo = o[i - 1].min(c[i - 1]);
let mid_hi = o[i - 1].max(c[i - 1]);
let cur_lo = o[i].min(c[i]);
let cur_hi = o[i].max(c[i]);
out.set(i, if prev_hi - prev_lo <= doji && mid_hi - mid_lo <= doji && cur_hi - cur_lo <= doji
{
if mid_lo > prev_hi && cur_hi < mid_hi {
-100.0
} else if mid_hi < prev_lo && cur_lo > mid_lo {
100.0
} else {
0.0
}
} else {
0.0
});
total += (h[i - 2] - l[i - 2]) - (h[trailing] - l[trailing]);
trailing += 1;
}
out.finish()
}
pub fn cdl_unique3river(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = BODY_SHORT.avg_period.max(BODY_LONG.avg_period) + 2;
each_bar_avg_n::<2, 3>([BODY_LONG, BODY_SHORT], lb, o, h, l, c, |i, hist| {
if realbody(o, c, i - 2) > hist[2][0] && color(o, c, i - 2) < 0.0
&& color(o, c, i - 1) < 0.0
&& c[i - 1] > c[i - 2]
&& o[i - 1] <= o[i - 2]
&& l[i - 1] < l[i - 2]
&& realbody(o, c, i) < hist[0][1] && color(o, c, i) > 0.0
&& o[i] > l[i - 1]
{
100.0
} else {
0.0
}
})
}
pub fn cdl_gapsidesidewhite(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = NEAR.avg_period.max(EQUAL.avg_period) + 2;
each_bar_avg_n::<2, 3>([NEAR, EQUAL], lb, o, h, l, c, |i, hist| {
let up = realbody_gap_up(o, c, i - 1, i - 2) && realbody_gap_up(o, c, i, i - 2);
let down = realbody_gap_down(o, c, i - 1, i - 2) && realbody_gap_down(o, c, i, i - 2);
let near = hist[1][0]; let eq = hist[1][1]; if (up || down)
&& color(o, c, i - 1) > 0.0
&& color(o, c, i) > 0.0
&& realbody(o, c, i) >= realbody(o, c, i - 1) - near
&& realbody(o, c, i) <= realbody(o, c, i - 1) + near
&& o[i] >= o[i - 1] - eq
&& o[i] <= o[i - 1] + eq
{
if up {
100.0
} else {
-100.0
}
} else {
0.0
}
})
}
pub fn cdl_tasukigap(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = NEAR.avg_period + 2;
let n = c.len();
let Some(mut out) = candle_output(n, lb) else {
return vec![f64::NAN; n];
};
let mut near_total = 0.0;
for j in (lb - 1 - NEAR.avg_period)..(lb - 1) {
near_total += range(NEAR, o, h, l, c, j);
}
let near_scale = NEAR.factor / NEAR.avg_period as f64;
let mut trailing = lb - 1 - NEAR.avg_period;
#[allow(clippy::explicit_counter_loop)] for i in lb..n {
let c1 = if c[i - 1] >= o[i - 1] { 1.0 } else { -1.0 };
let c0 = if c[i] >= o[i] { 1.0 } else { -1.0 };
let up = c1 > 0.0
&& c0 < 0.0
&& realbody_gap_up(o, c, i - 1, i - 2)
&& o[i] < c[i - 1]
&& o[i] > o[i - 1]
&& c[i] < o[i - 1]
&& c[i] > c[i - 2].max(o[i - 2]);
let down = c1 < 0.0
&& c0 > 0.0
&& realbody_gap_down(o, c, i - 1, i - 2)
&& o[i] < o[i - 1]
&& o[i] > c[i - 1]
&& c[i] > o[i - 1]
&& c[i] < c[i - 2].min(o[i - 2]);
out.set(i, if up || down {
let near = near_total * near_scale;
if (realbody(o, c, i - 1) - realbody(o, c, i)).abs() < near {
c1 * 100.0
} else {
0.0
}
} else {
0.0
});
near_total += range(NEAR, o, h, l, c, i - 1) - range(NEAR, o, h, l, c, trailing);
trailing += 1;
}
out.finish()
}
pub fn cdl_3starsinsouth(o: &[f64], h: &[f64], l: &[f64], c: &[f64]) -> Vec<f64> {
let lb = SHADOW_VERY_SHORT
.avg_period
.max(SHADOW_LONG.avg_period)
.max(BODY_LONG.avg_period)
.max(BODY_SHORT.avg_period)
+ 2;
each_bar(c.len(), lb, |i| {
if color(o, c, i - 2) < 0.0
&& color(o, c, i - 1) < 0.0
&& color(o, c, i) < 0.0
&& realbody(o, c, i - 2) > candle_average(BODY_LONG, o, h, l, c, i - 2)
&& lowershadow(o, l, c, i - 2) > candle_average(SHADOW_LONG, o, h, l, c, i - 2)
&& realbody(o, c, i - 1) < realbody(o, c, i - 2)
&& o[i - 1] > c[i - 2]
&& o[i - 1] <= h[i - 2]
&& l[i - 1] < c[i - 2]
&& l[i - 1] >= l[i - 2]
&& lowershadow(o, l, c, i - 1) > candle_average(SHADOW_VERY_SHORT, o, h, l, c, i - 1)
&& realbody(o, c, i) < candle_average(BODY_SHORT, o, h, l, c, i)
&& lowershadow(o, l, c, i) < candle_average(SHADOW_VERY_SHORT, o, h, l, c, i)
&& uppershadow(o, h, c, i) < candle_average(SHADOW_VERY_SHORT, o, h, l, c, i)
&& l[i] > l[i - 1]
&& h[i] < h[i - 1]
{
100.0
} else {
0.0
}
})
}