finance_query/models/quote/
price.rs1use crate::models::format::{Both, Format};
7use finance_query_derive::FormatConvert;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, FormatConvert, Default)]
19#[serde(rename_all = "camelCase", bound = "")]
20#[non_exhaustive]
21pub struct Price<F: Format = Both> {
22 #[serde(default)]
24 pub max_age: Option<i64>,
25
26 #[serde(default)]
28 pub pre_market_change_percent: Option<F::Value<f64>>,
29
30 #[serde(default)]
32 pub pre_market_change: Option<F::Value<f64>>,
33
34 #[serde(default)]
36 pub pre_market_time: Option<i64>,
37
38 #[serde(default)]
40 pub pre_market_price: Option<F::Value<f64>>,
41
42 #[serde(default)]
44 pub pre_market_source: Option<String>,
45
46 #[serde(default)]
48 pub post_market_change_percent: Option<F::Value<f64>>,
49
50 #[serde(default)]
52 pub post_market_change: Option<F::Value<f64>>,
53
54 #[serde(default)]
56 pub post_market_time: Option<i64>,
57
58 #[serde(default)]
60 pub post_market_price: Option<F::Value<f64>>,
61
62 #[serde(default)]
64 pub post_market_source: Option<String>,
65
66 #[serde(default)]
68 pub regular_market_change_percent: Option<F::Value<f64>>,
69
70 #[serde(default)]
72 pub regular_market_change: Option<F::Value<f64>>,
73
74 #[serde(default)]
76 pub regular_market_time: Option<i64>,
77
78 #[serde(default)]
80 pub price_hint: Option<F::Value<i64>>,
81
82 #[serde(default)]
84 pub regular_market_price: Option<F::Value<f64>>,
85
86 #[serde(default)]
88 pub regular_market_day_high: Option<F::Value<f64>>,
89
90 #[serde(default)]
92 pub regular_market_day_low: Option<F::Value<f64>>,
93
94 #[serde(default)]
96 pub regular_market_volume: Option<F::Value<i64>>,
97
98 #[serde(default)]
100 pub average_daily_volume10_day: Option<F::Value<i64>>,
101
102 #[serde(default)]
104 pub average_daily_volume3_month: Option<F::Value<i64>>,
105
106 #[serde(default)]
108 pub regular_market_previous_close: Option<F::Value<f64>>,
109
110 #[serde(default)]
112 pub regular_market_source: Option<String>,
113
114 #[serde(default)]
116 pub regular_market_open: Option<F::Value<f64>>,
117
118 #[serde(default)]
120 pub exchange: Option<String>,
121
122 #[serde(default)]
124 pub exchange_name: Option<String>,
125
126 #[serde(default)]
128 pub exchange_data_delayed_by: Option<i32>,
129
130 #[serde(default)]
132 pub market_state: Option<String>,
133
134 #[serde(default)]
136 pub quote_type: Option<String>,
137
138 #[serde(default)]
140 pub symbol: Option<String>,
141
142 #[serde(default)]
144 pub underlying_symbol: Option<String>,
145
146 #[serde(default)]
148 pub short_name: Option<String>,
149
150 #[serde(default)]
152 pub long_name: Option<String>,
153
154 #[serde(default)]
156 pub currency: Option<String>,
157
158 #[serde(default)]
160 pub quote_source_name: Option<String>,
161
162 #[serde(default)]
164 pub currency_symbol: Option<String>,
165
166 #[serde(default)]
168 pub from_currency: Option<String>,
169
170 #[serde(default)]
172 pub to_currency: Option<String>,
173
174 #[serde(default)]
176 pub last_market: Option<String>,
177
178 #[serde(default)]
180 pub market_cap: Option<F::Value<i64>>,
181}
182
183impl Price<Both> {
184 pub fn current_price(&self) -> Option<f64> {
186 self.regular_market_price.as_ref()?.raw
187 }
188
189 pub fn day_change(&self) -> Option<f64> {
191 self.regular_market_change.as_ref()?.raw
192 }
193
194 pub fn day_change_percent(&self) -> Option<f64> {
196 self.regular_market_change_percent.as_ref()?.raw
197 }
198
199 pub fn day_range(&self) -> Option<(f64, f64)> {
201 let low = self.regular_market_day_low.as_ref()?.raw?;
202 let high = self.regular_market_day_high.as_ref()?.raw?;
203 Some((low, high))
204 }
205
206 pub fn is_market_open(&self) -> bool {
208 self.market_state.as_deref() == Some("REGULAR")
209 }
210
211 pub fn is_pre_market(&self) -> bool {
213 self.market_state.as_deref() == Some("PRE")
214 }
215
216 pub fn is_post_market(&self) -> bool {
218 self.market_state.as_deref() == Some("POST")
219 }
220
221 pub fn live_price(&self) -> Option<f64> {
226 if self.is_post_market() {
227 self.post_market_price
228 .as_ref()
229 .and_then(|p| p.raw)
230 .or_else(|| self.regular_market_price.as_ref()?.raw)
231 } else if self.is_pre_market() {
232 self.pre_market_price
233 .as_ref()
234 .and_then(|p| p.raw)
235 .or_else(|| self.regular_market_price.as_ref()?.raw)
236 } else {
237 self.regular_market_price.as_ref()?.raw
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use serde_json::json;
246
247 #[test]
248 fn test_price_deserialize() {
249 let json = json!({
250 "maxAge": 1,
251 "regularMarketPrice": {"raw": 150.25, "fmt": "150.25"},
252 "regularMarketChange": {"raw": 2.50, "fmt": "2.50"},
253 "regularMarketChangePercent": {"raw": 0.0169, "fmt": "1.69%"},
254 "regularMarketDayHigh": {"raw": 151.00, "fmt": "151.00"},
255 "regularMarketDayLow": {"raw": 148.50, "fmt": "148.50"},
256 "regularMarketVolume": {"raw": 50000000, "fmt": "50M"},
257 "marketCap": {"raw": 2500000000000i64, "fmt": "2.5T"},
258 "symbol": "AAPL",
259 "shortName": "Apple Inc.",
260 "longName": "Apple Inc.",
261 "currency": "USD",
262 "exchange": "NMS",
263 "marketState": "REGULAR"
264 });
265
266 let price: Price = serde_json::from_value(json).unwrap();
267 assert_eq!(
268 price.regular_market_price.as_ref().and_then(|v| v.raw),
269 Some(150.25)
270 );
271 assert_eq!(price.symbol, Some("AAPL".to_string()));
272 assert_eq!(price.current_price(), Some(150.25));
273 assert_eq!(price.day_change(), Some(2.50));
274 assert!(price.is_market_open());
275 }
276
277 #[test]
278 fn test_price_helpers() {
279 use super::super::FormattedValue;
280
281 let price = Price {
282 regular_market_price: Some(FormattedValue::new(100.0)),
283 regular_market_day_low: Some(FormattedValue::new(98.0)),
284 regular_market_day_high: Some(FormattedValue::new(102.0)),
285 market_state: Some("REGULAR".to_string()),
286 post_market_price: Some(FormattedValue::new(101.0)),
287 pre_market_price: Some(FormattedValue::new(99.0)),
288 ..Default::default()
289 };
290
291 assert_eq!(price.day_range(), Some((98.0, 102.0)));
292 assert_eq!(price.live_price(), Some(100.0));
293 assert!(price.is_market_open());
294 }
295
296 #[test]
297 fn test_live_price_post_market() {
298 use super::super::FormattedValue;
299
300 let price = Price {
301 regular_market_price: Some(FormattedValue::new(100.0)),
302 post_market_price: Some(FormattedValue::new(101.0)),
303 market_state: Some("POST".to_string()),
304 ..Default::default()
305 };
306
307 assert_eq!(price.live_price(), Some(101.0));
308 assert!(price.is_post_market());
309 }
310
311 #[test]
312 fn test_into_raw() {
313 use super::super::FormattedValue;
314
315 let price = Price {
316 regular_market_price: Some(FormattedValue::new(100.0)),
317 market_state: Some("REGULAR".to_string()),
318 ..Default::default()
319 };
320
321 let raw = price.into_raw();
322 assert_eq!(raw.regular_market_price, Some(100.0));
323 assert_eq!(raw.market_state.as_deref(), Some("REGULAR"));
324 }
325}