use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ustr::Ustr;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MarketEvent {
Candle(CandleData),
Quote(QuoteData),
Economic(EconomicData),
FinancialMetric(FinancialMetric),
CorporateAction(CorporateAction),
News(NewsEvent),
SymbolResolved(SymbolInfoEvent),
}
impl MarketEvent {
pub fn timestamp(&self) -> i64 {
match self {
MarketEvent::Candle(c) => c.timestamp,
MarketEvent::Quote(q) => q.timestamp,
MarketEvent::Economic(e) => e.timestamp,
MarketEvent::FinancialMetric(m) => m.timestamp,
MarketEvent::CorporateAction(a) => a.timestamp,
MarketEvent::News(n) => n.timestamp,
MarketEvent::SymbolResolved(_) => 0,
}
}
pub fn symbol(&self) -> Option<&str> {
match self {
MarketEvent::Candle(c) => Some(c.symbol.as_str()),
MarketEvent::Quote(q) => Some(q.symbol.as_str()),
MarketEvent::Economic(_) => None,
MarketEvent::FinancialMetric(m) => Some(m.symbol.as_str()),
MarketEvent::CorporateAction(a) => Some(a.symbol.as_str()),
MarketEvent::News(n) => n.symbol.as_deref(),
MarketEvent::SymbolResolved(s) => Some(s.symbol.as_str()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DataKind {
Candle,
Quote,
Economic,
FinancialMetric,
CorporateAction,
News,
SymbolResolved,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CandleData {
pub timestamp: i64,
pub symbol: Ustr,
pub interval: Ustr,
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
pub volume: f64,
}
impl CandleData {
#[allow(clippy::too_many_arguments)]
pub fn new(
timestamp: i64,
symbol: impl Into<Ustr>,
interval: impl Into<Ustr>,
open: f64,
high: f64,
low: f64,
close: f64,
volume: f64,
) -> Self {
Self {
timestamp,
symbol: symbol.into(),
interval: interval.into(),
open,
high,
low,
close,
volume,
}
}
pub fn datetime(&self) -> Option<DateTime<Utc>> {
DateTime::from_timestamp(self.timestamp, 0)
}
pub fn is_bullish(&self) -> bool {
self.close > self.open
}
pub fn is_bearish(&self) -> bool {
self.close < self.open
}
pub fn typical_price(&self) -> f64 {
(self.high + self.low + self.close) / 3.0
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct QuoteData {
pub timestamp: i64,
pub symbol: Ustr,
pub bid: Option<f64>,
pub ask: Option<f64>,
pub bid_size: Option<f64>,
pub ask_size: Option<f64>,
pub last_price: Option<f64>,
pub volume: Option<f64>,
pub change: Option<f64>,
pub change_percent: Option<f64>,
pub open: Option<f64>,
pub high: Option<f64>,
pub low: Option<f64>,
pub prev_close: Option<f64>,
}
impl QuoteData {
pub fn mid_price(&self) -> Option<f64> {
match (self.bid, self.ask) {
(Some(b), Some(a)) => Some((b + a) * 0.5),
_ => None,
}
}
pub fn spread(&self) -> Option<f64> {
match (self.bid, self.ask) {
(Some(b), Some(a)) => Some(a - b),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EconomicData {
pub timestamp: i64,
pub indicator_id: Ustr,
pub indicator_name: Ustr,
pub country: Ustr,
pub value: f64,
pub unit: Ustr,
pub frequency: Ustr,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FinancialMetric {
pub timestamp: i64,
pub symbol: Ustr,
pub metric_name: Ustr,
pub value: f64,
pub unit: Option<Ustr>,
pub period: Option<Ustr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CorporateAction {
pub timestamp: i64,
pub symbol: Ustr,
pub action_type: CorporateActionType,
pub description: Ustr,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CorporateActionType {
Dividend,
StockSplit,
ReverseSplit,
Merger,
Acquisition,
SpinOff,
RightsOffering,
Delisting,
Ipo,
Other,
}
impl std::fmt::Display for CorporateActionType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CorporateActionType::Dividend => write!(f, "dividend"),
CorporateActionType::StockSplit => write!(f, "stock_split"),
CorporateActionType::ReverseSplit => write!(f, "reverse_split"),
CorporateActionType::Merger => write!(f, "merger"),
CorporateActionType::Acquisition => write!(f, "acquisition"),
CorporateActionType::SpinOff => write!(f, "spin_off"),
CorporateActionType::RightsOffering => write!(f, "rights_offering"),
CorporateActionType::Delisting => write!(f, "delisting"),
CorporateActionType::Ipo => write!(f, "ipo"),
CorporateActionType::Other => write!(f, "other"),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NewsEvent {
pub timestamp: i64,
pub story_id: Ustr,
pub title: Ustr,
pub body: Option<Ustr>,
pub provider: Ustr,
pub url: Option<Ustr>,
pub symbol: Option<Ustr>,
pub tags: Vec<Ustr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SymbolInfoEvent {
pub symbol: Ustr,
pub name: Ustr,
pub exchange: Ustr,
pub description: Ustr,
pub currency: Ustr,
pub market_type: Ustr,
pub sector: Option<Ustr>,
pub industry: Option<Ustr>,
}
pub trait CandleLike {
fn timestamp(&self) -> i64;
fn open(&self) -> f64;
fn high(&self) -> f64;
fn low(&self) -> f64;
fn close(&self) -> f64;
fn volume(&self) -> f64;
}
impl CandleLike for CandleData {
fn timestamp(&self) -> i64 {
self.timestamp
}
fn open(&self) -> f64 {
self.open
}
fn high(&self) -> f64 {
self.high
}
fn low(&self) -> f64 {
self.low
}
fn close(&self) -> f64 {
self.close
}
fn volume(&self) -> f64 {
self.volume
}
}