kestrel_chartkit/engine/
acceptance_detector.rs1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::engine::market_context::AcceptanceLevel;
5use crate::model::Bar;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[cfg_attr(
10 feature = "serde",
11 derive(Serialize, Deserialize),
12 serde(rename_all = "snake_case")
13)]
14pub enum RejectionKind {
15 RejectionHigh,
16 RejectionLow,
17 None,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23pub struct AcceptanceDetectorOutput {
24 pub acceptance_level: AcceptanceLevel,
25 pub rejection_kind: RejectionKind,
26 pub consecutive_acceptance_bars: u32,
27 pub level: f64,
28}
29
30pub fn detect_acceptance_rejection(
32 bars: &[Bar],
33 level: f64,
34 min_acceptance_bars: usize,
35) -> AcceptanceDetectorOutput {
36 if bars.is_empty() || level <= 0.0 {
37 return AcceptanceDetectorOutput {
38 acceptance_level: AcceptanceLevel::None,
39 rejection_kind: RejectionKind::None,
40 consecutive_acceptance_bars: 0,
41 level,
42 };
43 }
44
45 let mut consecutive_above = 0u32;
46 let mut consecutive_below = 0u32;
47
48 for b in bars.iter().rev() {
49 if b.close > level {
50 if consecutive_below > 0 {
51 break;
52 }
53 consecutive_above += 1;
54 } else if b.close < level {
55 if consecutive_above > 0 {
56 break;
57 }
58 consecutive_below += 1;
59 } else {
60 break;
61 }
62 }
63
64 let min_b = min_acceptance_bars as u32;
65 let (acc_level, rejection, count) = if consecutive_above >= min_b {
66 (
67 AcceptanceLevel::High,
68 RejectionKind::None,
69 consecutive_above,
70 )
71 } else if consecutive_below >= min_b {
72 (AcceptanceLevel::Low, RejectionKind::None, consecutive_below)
73 } else {
74 let last_bar = bars.last().unwrap();
75 let rejection = if last_bar.high > level && last_bar.close < level {
76 RejectionKind::RejectionHigh
77 } else if last_bar.low < level && last_bar.close > level {
78 RejectionKind::RejectionLow
79 } else {
80 RejectionKind::None
81 };
82 (AcceptanceLevel::None, rejection, 0)
83 };
84
85 AcceptanceDetectorOutput {
86 acceptance_level: acc_level,
87 rejection_kind: rejection,
88 consecutive_acceptance_bars: count,
89 level,
90 }
91}