hyper_agent_core/
signal.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4pub enum Side {
5 Buy,
6 Sell,
7}
8
9impl std::fmt::Display for Side {
10 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11 match self {
12 Side::Buy => write!(f, "buy"),
13 Side::Sell => write!(f, "sell"),
14 }
15 }
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub enum SignalSource {
20 Playbook { strategy_id: String, regime: String },
21 Manual,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub enum SignalAction {
26 Open {
27 side: Side,
28 size: f64,
29 price: Option<f64>,
30 },
31 Close {
32 side: Side,
33 size: f64,
34 },
35 CloseAll {
36 reason: String,
37 },
38 SetStopLoss {
39 trigger_price: f64,
40 },
41 SetTakeProfit {
42 trigger_price: f64,
43 },
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct TradeSignal {
48 pub id: String,
49 pub timestamp: u64,
50 pub source: SignalSource,
51 pub symbol: String,
52 pub action: SignalAction,
53 pub reason: String,
54}
55
56impl TradeSignal {
57 pub fn manual(symbol: String, action: SignalAction, reason: String) -> Self {
58 Self {
59 id: uuid::Uuid::new_v4().to_string(),
60 timestamp: std::time::SystemTime::now()
61 .duration_since(std::time::UNIX_EPOCH)
62 .unwrap_or_default()
63 .as_secs(),
64 source: SignalSource::Manual,
65 symbol,
66 action,
67 reason,
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn side_display() {
78 assert_eq!(Side::Buy.to_string(), "buy");
79 assert_eq!(Side::Sell.to_string(), "sell");
80 }
81
82 #[test]
83 fn side_serde_roundtrip() {
84 let json = serde_json::to_string(&Side::Buy).unwrap();
85 let back: Side = serde_json::from_str(&json).unwrap();
86 assert_eq!(back, Side::Buy);
87 }
88
89 #[test]
90 fn trade_signal_manual_constructor() {
91 let sig = TradeSignal::manual(
92 "BTC-PERP".into(),
93 SignalAction::Open {
94 side: Side::Buy,
95 size: 0.01,
96 price: Some(65000.0),
97 },
98 "manual order".into(),
99 );
100 assert_eq!(sig.symbol, "BTC-PERP");
101 assert!(matches!(sig.source, SignalSource::Manual));
102 assert!(!sig.id.is_empty());
103 assert!(sig.timestamp > 0);
104 }
105
106 #[test]
107 fn trade_signal_serde_roundtrip() {
108 let sig = TradeSignal::manual(
109 "ETH-PERP".into(),
110 SignalAction::SetStopLoss {
111 trigger_price: 3000.0,
112 },
113 "SL set".into(),
114 );
115 let json = serde_json::to_string(&sig).unwrap();
116 let back: TradeSignal = serde_json::from_str(&json).unwrap();
117 assert_eq!(back.symbol, "ETH-PERP");
118 }
119}