finance_query/adapters/polygon/
reference.rs1use serde::{Deserialize, Serialize};
4
5use crate::adapters::common::encode_path_segment;
6use crate::error::{FinanceError, Result};
7
8use super::build_client;
9use super::models::PaginatedResponse;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13#[non_exhaustive]
14pub struct TickerRef {
15 pub ticker: Option<String>,
17 pub name: Option<String>,
19 pub market: Option<String>,
21 pub locale: Option<String>,
23 pub primary_exchange: Option<String>,
25 #[serde(rename = "type")]
27 pub asset_type: Option<String>,
28 pub active: Option<bool>,
30 pub currency_name: Option<String>,
32 pub cik: Option<String>,
34 pub composite_figi: Option<String>,
36 pub share_class_figi: Option<String>,
38 pub last_updated_utc: Option<String>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44#[non_exhaustive]
45pub struct TickerDetails {
46 pub ticker: Option<String>,
48 pub name: Option<String>,
50 pub market: Option<String>,
52 pub locale: Option<String>,
54 pub primary_exchange: Option<String>,
56 #[serde(rename = "type")]
58 pub asset_type: Option<String>,
59 pub active: Option<bool>,
61 pub currency_name: Option<String>,
63 pub cik: Option<String>,
65 pub sic_code: Option<String>,
67 pub sic_description: Option<String>,
69 pub description: Option<String>,
71 pub homepage_url: Option<String>,
73 pub total_employees: Option<u64>,
75 pub market_cap: Option<f64>,
77 pub phone_number: Option<String>,
79 pub address: Option<serde_json::Value>,
81 pub branding: Option<serde_json::Value>,
83 pub list_date: Option<String>,
85 pub share_class_shares_outstanding: Option<f64>,
87 pub weighted_shares_outstanding: Option<f64>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93#[non_exhaustive]
94pub struct TickerDetailsResponse {
95 pub request_id: Option<String>,
97 pub status: Option<String>,
99 pub results: Option<TickerDetails>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105#[non_exhaustive]
106pub struct TickerType {
107 pub code: Option<String>,
109 pub description: Option<String>,
111 pub asset_class: Option<String>,
113 pub locale: Option<String>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119#[non_exhaustive]
120pub struct RelatedTicker {
121 pub ticker: Option<String>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127#[non_exhaustive]
128pub struct Exchange {
129 pub id: Option<i64>,
131 #[serde(rename = "type")]
133 pub exchange_type: Option<String>,
134 pub asset_class: Option<String>,
136 pub locale: Option<String>,
138 pub name: Option<String>,
140 pub mic: Option<String>,
142 pub operating_mic: Option<String>,
144 pub participant_id: Option<String>,
146 pub url: Option<String>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152#[non_exhaustive]
153pub struct Condition {
154 pub id: Option<i32>,
156 #[serde(rename = "type")]
158 pub condition_type: Option<String>,
159 pub name: Option<String>,
161 pub description: Option<String>,
163 pub asset_class: Option<String>,
165 pub sip_mapping: Option<serde_json::Value>,
167 pub data_types: Option<Vec<String>>,
169 pub legacy: Option<bool>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175#[non_exhaustive]
176pub struct MarketHoliday {
177 pub name: Option<String>,
179 pub date: Option<String>,
181 pub exchange: Option<String>,
183 pub status: Option<String>,
185 pub open: Option<String>,
187 pub close: Option<String>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193#[non_exhaustive]
194pub struct MarketStatusResponse {
195 #[serde(rename = "afterHours")]
197 pub after_hours: Option<bool>,
198 #[serde(rename = "earlyHours")]
200 pub early_hours: Option<bool>,
201 pub market: Option<String>,
203 #[serde(rename = "serverTime")]
205 pub server_time: Option<String>,
206 pub exchanges: Option<serde_json::Value>,
208 pub currencies: Option<serde_json::Value>,
210}
211
212pub async fn all_tickers(params: &[(&str, &str)]) -> Result<PaginatedResponse<TickerRef>> {
216 let client = build_client()?;
217 client.get("/v3/reference/tickers", params).await
218}
219
220pub async fn ticker_details(ticker: &str) -> Result<TickerDetailsResponse> {
222 let client = build_client()?;
223 let path = format!("/v3/reference/tickers/{}", encode_path_segment(ticker));
224 let json = client.get_raw(&path, &[]).await?;
225 serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
226 field: "ticker_details".to_string(),
227 context: format!("Failed to parse ticker details: {e}"),
228 })
229}
230
231pub async fn ticker_types(params: &[(&str, &str)]) -> Result<PaginatedResponse<TickerType>> {
233 let client = build_client()?;
234 client.get("/v3/reference/tickers/types", params).await
235}
236
237pub async fn related_tickers(ticker: &str) -> Result<PaginatedResponse<RelatedTicker>> {
239 let client = build_client()?;
240 let path = format!("/v1/related-companies/{}", encode_path_segment(ticker));
241 client.get(&path, &[]).await
242}
243
244pub async fn exchanges(params: &[(&str, &str)]) -> Result<PaginatedResponse<Exchange>> {
246 let client = build_client()?;
247 client.get("/v3/reference/exchanges", params).await
248}
249
250pub async fn condition_codes(params: &[(&str, &str)]) -> Result<PaginatedResponse<Condition>> {
252 let client = build_client()?;
253 client.get("/v3/reference/conditions", params).await
254}
255
256pub async fn market_holidays() -> Result<Vec<MarketHoliday>> {
258 let client = build_client()?;
259 let json = client.get_raw("/v1/marketstatus/upcoming", &[]).await?;
260 serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
261 field: "market_holidays".to_string(),
262 context: format!("Failed to parse market holidays: {e}"),
263 })
264}
265
266pub async fn market_status() -> Result<MarketStatusResponse> {
268 let client = build_client()?;
269 let json = client.get_raw("/v1/marketstatus/now", &[]).await?;
270 serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
271 field: "market_status".to_string(),
272 context: format!("Failed to parse market status: {e}"),
273 })
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[tokio::test]
281 async fn test_ticker_details_mock() {
282 let mut server = mockito::Server::new_async().await;
283 let _mock = server
284 .mock("GET", "/v3/reference/tickers/AAPL")
285 .match_query(mockito::Matcher::AllOf(vec![
286 mockito::Matcher::UrlEncoded("apiKey".into(), "test-key".into()),
287 ]))
288 .with_status(200)
289 .with_body(serde_json::json!({
290 "request_id": "abc",
291 "status": "OK",
292 "results": {
293 "ticker": "AAPL",
294 "name": "Apple Inc.",
295 "market": "stocks",
296 "locale": "us",
297 "primary_exchange": "XNAS",
298 "type": "CS",
299 "active": true,
300 "currency_name": "usd",
301 "market_cap": 2850000000000.0,
302 "description": "Apple Inc. designs, manufactures, and markets smartphones..."
303 }
304 }).to_string())
305 .create_async().await;
306
307 let client = super::super::build_test_client(&server.url()).unwrap();
308 let json = client
309 .get_raw("/v3/reference/tickers/AAPL", &[])
310 .await
311 .unwrap();
312 let resp: TickerDetailsResponse = serde_json::from_value(json).unwrap();
313 let details = resp.results.unwrap();
314 assert_eq!(details.name.as_deref(), Some("Apple Inc."));
315 assert_eq!(details.ticker.as_deref(), Some("AAPL"));
316 assert!((details.market_cap.unwrap() - 2850000000000.0).abs() < 1.0);
317 }
318
319 #[tokio::test]
320 async fn test_market_status_mock() {
321 let mut server = mockito::Server::new_async().await;
322 let _mock = server
323 .mock("GET", "/v1/marketstatus/now")
324 .match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
325 "apiKey".into(),
326 "test-key".into(),
327 )]))
328 .with_status(200)
329 .with_body(
330 serde_json::json!({
331 "market": "open",
332 "earlyHours": false,
333 "afterHours": false,
334 "serverTime": "2024-01-15T12:00:00-05:00"
335 })
336 .to_string(),
337 )
338 .create_async()
339 .await;
340
341 let client = super::super::build_test_client(&server.url()).unwrap();
342 let json = client.get_raw("/v1/marketstatus/now", &[]).await.unwrap();
343 let resp: MarketStatusResponse = serde_json::from_value(json).unwrap();
344 assert_eq!(resp.market.as_deref(), Some("open"));
345 }
346}