1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use ustr::Ustr;
9
10#[non_exhaustive]
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub enum MarketEvent {
22 Candle(CandleData),
24
25 Quote(QuoteData),
27
28 Economic(EconomicData),
30
31 FinancialMetric(FinancialMetric),
33
34 CorporateAction(CorporateAction),
36
37 News(NewsEvent),
39
40 SymbolResolved(SymbolInfoEvent),
42}
43
44impl MarketEvent {
45 pub fn timestamp(&self) -> i64 {
47 match self {
48 MarketEvent::Candle(c) => c.timestamp,
49 MarketEvent::Quote(q) => q.timestamp,
50 MarketEvent::Economic(e) => e.timestamp,
51 MarketEvent::FinancialMetric(m) => m.timestamp,
52 MarketEvent::CorporateAction(a) => a.timestamp,
53 MarketEvent::News(n) => n.timestamp,
54 MarketEvent::SymbolResolved(_) => 0,
55 }
56 }
57
58 pub fn symbol(&self) -> Option<&str> {
60 match self {
61 MarketEvent::Candle(c) => Some(c.symbol.as_str()),
62 MarketEvent::Quote(q) => Some(q.symbol.as_str()),
63 MarketEvent::Economic(_) => None,
64 MarketEvent::FinancialMetric(m) => Some(m.symbol.as_str()),
65 MarketEvent::CorporateAction(a) => Some(a.symbol.as_str()),
66 MarketEvent::News(n) => n.symbol.as_deref(),
67 MarketEvent::SymbolResolved(s) => Some(s.symbol.as_str()),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
78pub enum DataKind {
79 Candle,
81 Quote,
83 Economic,
85 FinancialMetric,
87 CorporateAction,
89 News,
91 SymbolResolved,
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub struct CandleData {
102 pub timestamp: i64,
104 pub symbol: Ustr,
106 pub interval: Ustr,
108 pub open: f64,
109 pub high: f64,
110 pub low: f64,
111 pub close: f64,
112 pub volume: f64,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub datasource: Option<Ustr>,
117}
118
119impl CandleData {
120 #[allow(clippy::too_many_arguments)]
122 pub fn new(
123 timestamp: i64,
124 symbol: impl Into<Ustr>,
125 interval: impl Into<Ustr>,
126 open: f64,
127 high: f64,
128 low: f64,
129 close: f64,
130 volume: f64,
131 ) -> Self {
132 Self {
133 timestamp,
134 symbol: symbol.into(),
135 interval: interval.into(),
136 open,
137 high,
138 low,
139 close,
140 volume,
141 datasource: None,
142 }
143 }
144
145 #[allow(clippy::too_many_arguments)]
147 pub fn with_datasource(
148 timestamp: i64,
149 symbol: impl Into<Ustr>,
150 interval: impl Into<Ustr>,
151 open: f64,
152 high: f64,
153 low: f64,
154 close: f64,
155 volume: f64,
156 datasource: impl Into<Ustr>,
157 ) -> Self {
158 Self {
159 timestamp,
160 symbol: symbol.into(),
161 interval: interval.into(),
162 open,
163 high,
164 low,
165 close,
166 volume,
167 datasource: Some(datasource.into()),
168 }
169 }
170
171 pub fn datetime(&self) -> Option<DateTime<Utc>> {
173 DateTime::from_timestamp(self.timestamp, 0)
174 }
175
176 pub fn is_bullish(&self) -> bool {
178 self.close > self.open
179 }
180
181 pub fn is_bearish(&self) -> bool {
183 self.close < self.open
184 }
185
186 pub fn typical_price(&self) -> f64 {
188 (self.high + self.low + self.close) / 3.0
189 }
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub struct QuoteData {
195 pub timestamp: i64,
197 pub symbol: Ustr,
198 pub bid: Option<f64>,
199 pub ask: Option<f64>,
200 pub bid_size: Option<f64>,
201 pub ask_size: Option<f64>,
202 pub last_price: Option<f64>,
203 pub volume: Option<f64>,
204 pub change: Option<f64>,
205 pub change_percent: Option<f64>,
206 pub open: Option<f64>,
207 pub high: Option<f64>,
208 pub low: Option<f64>,
209 pub prev_close: Option<f64>,
210}
211
212impl QuoteData {
213 pub fn mid_price(&self) -> Option<f64> {
215 match (self.bid, self.ask) {
216 (Some(b), Some(a)) => Some((b + a) * 0.5),
217 _ => None,
218 }
219 }
220
221 pub fn spread(&self) -> Option<f64> {
223 match (self.bid, self.ask) {
224 (Some(b), Some(a)) => Some(a - b),
225 _ => None,
226 }
227 }
228}
229
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
232pub struct EconomicData {
233 pub timestamp: i64,
235 pub indicator_id: Ustr,
237 pub indicator_name: Ustr,
239 pub country: Ustr,
241 pub value: f64,
243 pub unit: Ustr,
245 pub frequency: Ustr,
247}
248
249#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
251pub struct FinancialMetric {
252 pub timestamp: i64,
254 pub symbol: Ustr,
255 pub metric_name: Ustr,
256 pub value: f64,
257 pub unit: Option<Ustr>,
259 pub period: Option<Ustr>,
261}
262
263#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
265pub struct CorporateAction {
266 pub timestamp: i64,
268 pub symbol: Ustr,
269 pub action_type: CorporateActionType,
270 pub description: Ustr,
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
276pub enum CorporateActionType {
277 Dividend,
278 StockSplit,
279 ReverseSplit,
280 Merger,
281 Acquisition,
282 SpinOff,
283 RightsOffering,
284 Delisting,
285 Ipo,
286 Other,
287}
288
289impl std::fmt::Display for CorporateActionType {
290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291 match self {
292 CorporateActionType::Dividend => write!(f, "dividend"),
293 CorporateActionType::StockSplit => write!(f, "stock_split"),
294 CorporateActionType::ReverseSplit => write!(f, "reverse_split"),
295 CorporateActionType::Merger => write!(f, "merger"),
296 CorporateActionType::Acquisition => write!(f, "acquisition"),
297 CorporateActionType::SpinOff => write!(f, "spin_off"),
298 CorporateActionType::RightsOffering => write!(f, "rights_offering"),
299 CorporateActionType::Delisting => write!(f, "delisting"),
300 CorporateActionType::Ipo => write!(f, "ipo"),
301 CorporateActionType::Other => write!(f, "other"),
302 }
303 }
304}
305
306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
308pub struct NewsEvent {
309 pub timestamp: i64,
311 pub story_id: Ustr,
313 pub title: Ustr,
315 pub body: Option<Ustr>,
317 pub provider: Ustr,
319 pub url: Option<Ustr>,
321 pub symbol: Option<Ustr>,
323 pub tags: Vec<Ustr>,
324}
325
326#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
328pub struct SymbolInfoEvent {
329 pub symbol: Ustr,
331 pub name: Ustr,
333 pub exchange: Ustr,
335 pub description: Ustr,
337 pub currency: Ustr,
339 pub market_type: Ustr,
341 pub sector: Option<Ustr>,
343 pub industry: Option<Ustr>,
345}
346
347pub trait CandleLike {
356 fn timestamp(&self) -> i64;
357 fn open(&self) -> f64;
358 fn high(&self) -> f64;
359 fn low(&self) -> f64;
360 fn close(&self) -> f64;
361 fn volume(&self) -> f64;
362}
363
364impl CandleLike for CandleData {
365 fn timestamp(&self) -> i64 {
366 self.timestamp
367 }
368 fn open(&self) -> f64 {
369 self.open
370 }
371 fn high(&self) -> f64 {
372 self.high
373 }
374 fn low(&self) -> f64 {
375 self.low
376 }
377 fn close(&self) -> f64 {
378 self.close
379 }
380 fn volume(&self) -> f64 {
381 self.volume
382 }
383}