finance_query/adapters/polygon/futures/
contracts.rs1use serde::{Deserialize, Serialize};
4
5use crate::error::{FinanceError, Result};
6
7use super::super::build_client;
8use super::super::models::PaginatedResponse;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12#[non_exhaustive]
13pub struct FuturesContract {
14 pub ticker: Option<String>,
16 pub name: Option<String>,
18 pub market: Option<String>,
20 pub asset_class: Option<String>,
22 pub expiration_date: Option<String>,
24 pub first_trade_date: Option<String>,
26 pub last_trade_date: Option<String>,
28 pub contract_size: Option<f64>,
30 pub contract_unit: Option<String>,
32 pub tick_size: Option<f64>,
34 pub underlying_ticker: Option<String>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40#[non_exhaustive]
41pub struct FuturesProduct {
42 pub ticker: Option<String>,
44 pub name: Option<String>,
46 pub asset_class: Option<String>,
48 pub market: Option<String>,
50 pub exchange: Option<String>,
52 pub contract_size: Option<f64>,
54 pub contract_unit: Option<String>,
56 pub tick_size: Option<f64>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62#[non_exhaustive]
63pub struct FuturesSchedule {
64 pub ticker: Option<String>,
66 pub session_type: Option<String>,
68 pub start_time: Option<String>,
70 pub end_time: Option<String>,
72 pub timezone: Option<String>,
74 pub day_of_week: Option<String>,
76}
77
78pub async fn futures_contracts(
82 params: &[(&str, &str)],
83) -> Result<PaginatedResponse<FuturesContract>> {
84 let client = build_client()?;
85 let path = "/v3/reference/futures/contracts";
86 let json = client.get_raw(path, params).await?;
87 serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
88 field: "futures_contracts".to_string(),
89 context: format!("Failed to parse futures contracts response: {e}"),
90 })
91}
92
93pub async fn futures_products(
97 params: &[(&str, &str)],
98) -> Result<PaginatedResponse<FuturesProduct>> {
99 let client = build_client()?;
100 let path = "/v3/reference/futures/products";
101 let json = client.get_raw(path, params).await?;
102 serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
103 field: "futures_products".to_string(),
104 context: format!("Failed to parse futures products response: {e}"),
105 })
106}
107
108pub async fn futures_schedules(
112 params: &[(&str, &str)],
113) -> Result<PaginatedResponse<FuturesSchedule>> {
114 let client = build_client()?;
115 let path = "/v3/reference/futures/schedules";
116 let json = client.get_raw(path, params).await?;
117 serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
118 field: "futures_schedules".to_string(),
119 context: format!("Failed to parse futures schedules response: {e}"),
120 })
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[tokio::test]
128 async fn test_futures_contracts_mock() {
129 let mut server = mockito::Server::new_async().await;
130 let _mock = server
131 .mock("GET", "/v3/reference/futures/contracts")
132 .match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
133 "apiKey".into(),
134 "test-key".into(),
135 )]))
136 .with_status(200)
137 .with_header("content-type", "application/json")
138 .with_body(
139 serde_json::json!({
140 "status": "OK",
141 "request_id": "abc123",
142 "results": [
143 {
144 "ticker": "ESZ4",
145 "name": "E-mini S&P 500 Dec 2024",
146 "market": "futures",
147 "asset_class": "equity_index",
148 "expiration_date": "2024-12-20",
149 "first_trade_date": "2024-06-21",
150 "contract_size": 50.0,
151 "contract_unit": "USD",
152 "tick_size": 0.25,
153 "underlying_ticker": "I:SPX"
154 }
155 ],
156 "resultsCount": 1
157 })
158 .to_string(),
159 )
160 .create_async()
161 .await;
162
163 let client = super::super::super::build_test_client(&server.url()).unwrap();
164 let json = client
165 .get_raw("/v3/reference/futures/contracts", &[])
166 .await
167 .unwrap();
168
169 let resp: PaginatedResponse<FuturesContract> = serde_json::from_value(json).unwrap();
170 let results = resp.results.unwrap();
171 assert_eq!(results.len(), 1);
172 assert_eq!(results[0].ticker.as_deref(), Some("ESZ4"));
173 assert_eq!(results[0].name.as_deref(), Some("E-mini S&P 500 Dec 2024"));
174 }
175}