pub use crate::model::Bar;
pub use crate::model::Timeframe;
use std::error::Error;
use std::fmt;
#[derive(Debug, Clone)]
pub enum DataSourceError {
ConnError(String),
FetchError(String),
SymbolNotFound(String),
InvalidTimeRange(String),
IoError(String),
ParseError(String),
RateLimited,
NotSupported(String),
}
impl fmt::Display for DataSourceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DataSourceError::ConnError(msg) => write!(f, "Conn error: {msg}"),
DataSourceError::FetchError(msg) => write!(f, "Fetch error: {msg}"),
DataSourceError::SymbolNotFound(msg) => write!(f, "Symbol not found: {msg}"),
DataSourceError::InvalidTimeRange(msg) => write!(f, "Invalid time range: {msg}"),
DataSourceError::IoError(msg) => write!(f, "IO error: {msg}"),
DataSourceError::ParseError(msg) => write!(f, "Parse error: {msg}"),
DataSourceError::RateLimited => write!(f, "Rate limited"),
DataSourceError::NotSupported(msg) => write!(f, "Not supported: {msg}"),
}
}
}
impl Error for DataSourceError {}
impl From<std::io::Error> for DataSourceError {
fn from(err: std::io::Error) -> Self {
DataSourceError::IoError(err.to_string())
}
}
impl DataSourceError {
pub fn is_recoverable(&self) -> bool {
matches!(
self,
DataSourceError::ConnError(_)
| DataSourceError::FetchError(_)
| DataSourceError::IoError(_)
)
}
pub fn is_conn_error(&self) -> bool {
matches!(self, DataSourceError::ConnError(_))
}
pub fn message(&self) -> &str {
match self {
DataSourceError::ConnError(msg)
| DataSourceError::FetchError(msg)
| DataSourceError::SymbolNotFound(msg)
| DataSourceError::InvalidTimeRange(msg)
| DataSourceError::IoError(msg)
| DataSourceError::ParseError(msg)
| DataSourceError::NotSupported(msg) => msg,
DataSourceError::RateLimited => "Rate limited",
}
}
}
#[derive(Debug, Clone)]
pub enum DataUpdate {
NewBars {
symbol: String,
bars: Vec<Bar>,
},
HistoricalBars {
symbol: String,
bars: Vec<Bar>,
},
FullDataset {
symbol: String,
bars: Vec<Bar>,
},
SymbolsChanged(Vec<String>),
ConnStatus(bool),
}
#[derive(Debug, Clone)]
pub struct HistoricalDataRequest {
pub symbol: String,
pub timeframe: Timeframe,
pub end_ts_millis: i64,
pub limit: usize,
}
#[derive(Debug, Clone)]
pub struct SymbolSearchResult {
pub symbol: String,
pub full_name: String,
pub description: String,
pub exchange: String,
pub symbol_type: String,
}
#[derive(Debug, Clone)]
pub struct BarMark {
pub time: i64,
pub color: String,
pub label: String,
pub tooltip: String,
}
#[derive(Debug, Clone)]
pub struct TimescaleMark {
pub time: i64,
pub color: String,
pub label: String,
pub tooltip: String,
}
#[derive(Debug, Clone)]
pub struct PaginatedSearchResult {
pub results: Vec<SymbolSearchResult>,
pub total: usize,
pub offset: usize,
pub has_more: bool,
}
pub trait DataSource: Send {
fn symbols(&self) -> Vec<String>;
fn subscribe(&mut self, symbol: String, timeframe: Timeframe) -> Result<(), DataSourceError>;
fn unsubscribe(&mut self, symbol: String) -> Result<(), DataSourceError>;
fn poll(&mut self) -> Vec<DataUpdate>;
fn fetch_historical(
&mut self,
request: HistoricalDataRequest,
) -> Result<Vec<Bar>, DataSourceError>;
fn supports_historical(&self) -> bool {
false
}
fn is_connected(&self) -> bool {
true
}
fn get_timeframe(&self, symbol: &str) -> Option<Timeframe>;
fn search_symbols(
&self,
_user_input: &str,
_exchange: &str,
_symbol_type: &str,
_max_records: usize,
) -> Result<Vec<SymbolSearchResult>, DataSourceError> {
Ok(Vec::new())
}
fn supports_symbol_search(&self) -> bool {
false
}
fn get_marks(
&self,
_symbol: &str,
_from: i64,
_to: i64,
) -> Result<Vec<BarMark>, DataSourceError> {
Ok(Vec::new())
}
fn supports_marks(&self) -> bool {
false
}
fn get_timescale_marks(
&self,
_symbol: &str,
_from: i64,
_to: i64,
) -> Result<Vec<TimescaleMark>, DataSourceError> {
Ok(Vec::new())
}
fn supports_timescale_marks(&self) -> bool {
false
}
fn get_server_time(&self) -> Result<i64, DataSourceError> {
Ok(chrono::Utc::now().timestamp())
}
fn supports_server_time(&self) -> bool {
false
}
}