finance_query/adapters/polygon/futures/
snapshots.rs1use serde::{Deserialize, Serialize};
4
5use crate::adapters::common::encode_path_segment;
6use crate::error::{FinanceError, Result};
7
8use super::super::build_client;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12#[non_exhaustive]
13pub struct FuturesSession {
14 pub change: Option<f64>,
16 pub change_percent: Option<f64>,
18 pub close: Option<f64>,
20 pub high: Option<f64>,
22 pub low: Option<f64>,
24 pub open: Option<f64>,
26 pub previous_close: Option<f64>,
28 pub settlement: Option<f64>,
30 pub volume: Option<f64>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36#[non_exhaustive]
37pub struct FuturesSnapshot {
38 pub ticker: Option<String>,
40 pub name: Option<String>,
42 pub market_status: Option<String>,
44 #[serde(rename = "type")]
46 pub snapshot_type: Option<String>,
47 pub session: Option<FuturesSession>,
49 pub last_updated: Option<i64>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55#[non_exhaustive]
56pub struct FuturesSnapshotResponse {
57 pub status: Option<String>,
59 pub request_id: Option<String>,
61 pub results: Option<Vec<FuturesSnapshot>>,
63}
64
65pub async fn futures_snapshot(ticker: &str) -> Result<FuturesSnapshotResponse> {
69 let client = build_client()?;
70 let path = format!("/v3/snapshot/futures/{}", encode_path_segment(ticker));
71 let json = client.get_raw(&path, &[]).await?;
72 serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
73 field: "futures_snapshot".to_string(),
74 context: format!("Failed to parse futures snapshot response: {e}"),
75 })
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[tokio::test]
83 async fn test_futures_snapshot_mock() {
84 let mut server = mockito::Server::new_async().await;
85 let _mock = server
86 .mock("GET", "/v3/snapshot/futures/ESZ4")
87 .match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
88 "apiKey".into(),
89 "test-key".into(),
90 )]))
91 .with_status(200)
92 .with_header("content-type", "application/json")
93 .with_body(
94 serde_json::json!({
95 "status": "OK",
96 "request_id": "abc123",
97 "results": [
98 {
99 "ticker": "ESZ4",
100 "name": "E-mini S&P 500 Dec 2024",
101 "market_status": "open",
102 "type": "futures",
103 "session": {
104 "change": 15.0,
105 "change_percent": 0.31,
106 "close": 4790.0,
107 "high": 4800.0,
108 "low": 4760.0,
109 "open": 4775.0,
110 "previous_close": 4775.0,
111 "settlement": 4785.0,
112 "volume": 1500000.0
113 },
114 "last_updated": 1705363200000000000_i64
115 }
116 ]
117 })
118 .to_string(),
119 )
120 .create_async()
121 .await;
122
123 let client = super::super::super::build_test_client(&server.url()).unwrap();
124 let json = client
125 .get_raw("/v3/snapshot/futures/ESZ4", &[])
126 .await
127 .unwrap();
128
129 let resp: FuturesSnapshotResponse = serde_json::from_value(json).unwrap();
130 assert_eq!(resp.status.as_deref(), Some("OK"));
131 let results = resp.results.unwrap();
132 assert_eq!(results.len(), 1);
133 assert_eq!(results[0].ticker.as_deref(), Some("ESZ4"));
134 let session = results[0].session.as_ref().unwrap();
135 assert!((session.change.unwrap() - 15.0).abs() < 0.01);
136 assert!((session.close.unwrap() - 4790.0).abs() < 0.01);
137 }
138}