market_data/
client.rs

1//! Market-Data client implementation
2
3use crate::{errors::MarketResult, indicators::EnhancedMarketSeries, publishers::Publisher};
4use chrono::NaiveDate;
5use serde::{Deserialize, Serialize};
6use std::fmt;
7use std::str::FromStr;
8
9/// MarketClient holds the Publisher
10pub struct MarketClient<T: Publisher> {
11    pub site: T,
12}
13
14impl<T: Publisher> MarketClient<T> {
15    pub fn new(site: T) -> Self {
16        MarketClient { site }
17    }
18
19    /// Creates the final query URL for the selected Provider
20    pub fn create_endpoint(mut self) -> MarketResult<Self> {
21        self.site.create_endpoint()?;
22        Ok(self)
23    }
24
25    /// Download the data series in the Provider format
26    #[cfg(feature = "use-async")]
27    pub async fn get_data(mut self) -> MarketResult<Self> {
28        self.site.get_data().await?;
29        Ok(self)
30    }
31
32    /// Download the data series in the Provider format
33    #[cfg(feature = "use-sync")]
34    pub fn get_data(mut self) -> MarketResult<Self> {
35        self.site.get_data()?;
36        Ok(self)
37    }
38
39    /// Write the downloaded data to anything that implements std::io::Write , like File, TcpStream, Stdout, etc
40    pub fn to_writer(&self, writer: impl std::io::Write) -> MarketResult<()> {
41        self.site.to_writer(writer)?;
42        Ok(())
43    }
44
45    /// Transform the downloaded Provider series into MarketSeries format
46    pub fn transform_data(&mut self) -> Vec<MarketResult<MarketSeries>> {
47        self.site.transform_data()
48    }
49}
50
51/// Holds the parsed data from Publishers
52#[derive(Debug, Serialize, Deserialize)]
53pub struct MarketSeries {
54    /// holds symbol like: "GOOGL"
55    pub symbol: String,
56    /// inteval from intraday to monthly
57    pub interval: Interval,
58    /// the original series downloaded and parsed from publishers
59    pub data: Vec<Series>,
60}
61
62/// Series part of the MarketSeries
63#[derive(Debug, Serialize, Deserialize)]
64pub struct Series {
65    /// the date of the stock price
66    pub date: NaiveDate,
67    /// the opening price of the stock for the selected interval
68    pub open: f32,
69    /// the closing price of the stock for the selected interval
70    pub close: f32,
71    /// the highest price of the stock for the selected interval
72    pub high: f32,
73    /// the lowest price of the stock for the selected interval
74    pub low: f32,
75    /// the number of shares traded in the selected interval
76    pub volume: f32,
77}
78
79/// The time interval between two data points
80#[derive(Debug, Serialize, Deserialize, Default, Clone)]
81pub enum Interval {
82    /// 1 minute interval
83    Min1,
84    /// 5 minutes interval
85    Min5,
86    /// 15 minutes interval
87    Min15,
88    /// 30 minutes interval
89    Min30,
90    /// 1 hour interval
91    Hour1,
92    /// 2 hours interval
93    Hour2,
94    /// 4 hours interval
95    Hour4,
96    /// daily interval
97    #[default]
98    Daily,
99    /// weekly interval
100    Weekly,
101    /// monthly interval
102    Monthly,
103}
104
105impl MarketSeries {
106    pub fn enhance_data(self) -> EnhancedMarketSeries {
107        EnhancedMarketSeries {
108            symbol: self.symbol,
109            interval: self.interval,
110            series: self.data,
111            asks: Vec::new(),
112            indicators: Default::default(),
113        }
114    }
115}
116
117impl fmt::Display for MarketSeries {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        write!(
120            f,
121            "MarketSeries: Symbol = {}, Interval = {}, Series =\n{}",
122            self.symbol,
123            self.interval,
124            self.data
125                .iter()
126                .map(|series| format!("  {}", series))
127                .collect::<Vec<String>>()
128                .join("\n")
129        )
130    }
131}
132
133impl fmt::Display for Series {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        write!(
136            f,
137            "Date: {}, Open: {}, Close: {}, High: {}, Low: {}, Volume: {}",
138            self.date, self.open, self.close, self.high, self.low, self.volume
139        )
140    }
141}
142
143impl fmt::Display for Interval {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        let interval_str = match self {
146            Interval::Min1 => "1 minute",
147            Interval::Min5 => "5 minutes",
148            Interval::Min15 => "15 minutes",
149            Interval::Min30 => "30 minutes",
150            Interval::Hour1 => "1 hour",
151            Interval::Hour2 => "2 hours",
152            Interval::Hour4 => "4 hours",
153            Interval::Daily => "Daily",
154            Interval::Weekly => "Weekly",
155            Interval::Monthly => "Monthly",
156        };
157
158        write!(f, "{}", interval_str)
159    }
160}
161
162/// AlphaVantage response interval: 1min, 5min, 15min, 30min, 60min,
163/// Twelvedata response interval: 1min, 5min, 15min, 30min, 1h, 2h, 4h, 1day, 1week, 1month
164impl std::str::FromStr for Interval {
165    type Err = &'static str;
166
167    fn from_str(s: &str) -> Result<Self, Self::Err> {
168        match s.to_lowercase().as_str() {
169            "1min" => Ok(Interval::Min1),
170            "5min" => Ok(Interval::Min5),
171            "15min" => Ok(Interval::Min15),
172            "30min" => Ok(Interval::Min30),
173            "60min" => Ok(Interval::Hour1),
174            "1h" => Ok(Interval::Hour1),
175            "2h" => Ok(Interval::Hour2),
176            "4h" => Ok(Interval::Hour4),
177            "1day" => Ok(Interval::Daily),
178            "1week" => Ok(Interval::Weekly),
179            "1month" => Ok(Interval::Monthly),
180            _ => Err("Invalid interval string"),
181        }
182    }
183}
184
185impl From<String> for Interval {
186    fn from(s: String) -> Self {
187        Interval::from_str(&s).unwrap_or_else(|_| Interval::Daily)
188    }
189}