1use std::fmt;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use crate::engine::market_state::PlaybookState;
7use crate::model::{MarketRegime, RiskPlan, SupportResistanceZone};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
10#[cfg_attr(
11 feature = "serde",
12 derive(Serialize, Deserialize),
13 serde(rename_all = "lowercase")
14)]
15pub enum SignalDirection {
16 Bullish,
17 #[default]
18 Neutral,
19 Bearish,
20}
21
22impl fmt::Display for SignalDirection {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 SignalDirection::Bullish => write!(f, "BULLISH"),
26 SignalDirection::Neutral => write!(f, "NEUTRAL"),
27 SignalDirection::Bearish => write!(f, "BEARISH"),
28 }
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
34#[cfg_attr(
35 feature = "serde",
36 derive(Serialize, Deserialize),
37 serde(rename_all = "snake_case")
38)]
39pub enum TriggerAction {
40 Buy,
41 Sell,
42 Exit,
43 #[default]
44 Hold,
45}
46
47impl fmt::Display for TriggerAction {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 TriggerAction::Buy => write!(f, "BUY"),
51 TriggerAction::Sell => write!(f, "SELL"),
52 TriggerAction::Exit => write!(f, "EXIT"),
53 TriggerAction::Hold => write!(f, "HOLD"),
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq)]
60#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
61pub struct TargetZone {
62 pub price: f64,
63 pub name: String,
64 pub reward_atr: f64,
65}
66
67#[derive(Debug, Clone, PartialEq)]
69#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
70pub struct InvalidationZone {
71 pub price: f64,
72 pub reason: String,
73 pub risk_atr: f64,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq)]
78#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79pub struct SetupDuration {
80 pub max_bars: u32,
81 pub elapsed_bars: u32,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
85#[cfg_attr(
86 feature = "serde",
87 derive(Serialize, Deserialize),
88 serde(rename_all = "snake_case")
89)]
90pub enum PermissionGrade {
91 ClearToTrade,
92 Caution,
93 #[default]
94 Veto,
95}
96
97impl fmt::Display for PermissionGrade {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 PermissionGrade::ClearToTrade => write!(f, "CLEAR TO TRADE"),
101 PermissionGrade::Caution => write!(f, "CAUTION / PROTECT"),
102 PermissionGrade::Veto => write!(f, "NO TRADE / VETO"),
103 }
104 }
105}
106
107impl From<PlaybookState> for PermissionGrade {
108 fn from(state: PlaybookState) -> Self {
109 match state {
110 PlaybookState::Preferred => PermissionGrade::ClearToTrade,
111 PlaybookState::Neutral => PermissionGrade::Caution,
112 PlaybookState::Suppressed | PlaybookState::NotAllowed => PermissionGrade::Veto,
113 }
114 }
115}
116
117impl From<PermissionGrade> for PlaybookState {
118 fn from(grade: PermissionGrade) -> Self {
119 match grade {
120 PermissionGrade::ClearToTrade => PlaybookState::Preferred,
121 PermissionGrade::Caution => PlaybookState::Neutral,
122 PermissionGrade::Veto => PlaybookState::Suppressed,
123 }
124 }
125}
126
127#[derive(Debug, Clone, PartialEq)]
128#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129pub struct SubScore {
130 pub indicator: String,
131 pub score: f64, pub raw_value: f64,
133 pub reason: Option<String>,
134}
135
136#[derive(Debug, Clone, PartialEq)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138pub struct CompositeSignal {
139 pub direction: SignalDirection,
140 pub trigger: TriggerAction,
141 pub score: f64,
142 pub agreement: f64,
148 pub heat_score: f64, pub permission: PermissionGrade,
150 pub regime: MarketRegime,
151 pub target_zone: Option<TargetZone>,
152 pub invalidation_zone: Option<InvalidationZone>,
153 pub setup_duration: Option<SetupDuration>,
154 pub sr_zones: Vec<SupportResistanceZone>,
155 pub risk_plan: Option<RiskPlan>,
156 pub reasons: Vec<String>,
157 pub explanation: String,
158 pub per_indicator: Vec<SubScore>,
159}